PageSourceSearch

https://mkurnali.ge/static/videojs/video.js?rhmmg3fkr8il7ul

js mkurnali.ge collected 2026-09-25 08:14:10 UTC 1,980,782 bytes, 58,346 lines download raw bytes

1/**
2 * @license
3 * Video.js 7.8.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$3, document) { 'use strict';
18  
19    window$3 = window$3 && Object.prototype.hasOwnProperty.call(window$3, 'default') ? window$3['default'] : window$3;
20    document = document && Object.prototype.hasOwnProperty.call(document, 'default') ? document['default'] : document;
21  
22    var version = "7.8.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$3.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$3.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$3.console.info || window$3.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$3.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  
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$3.getComputedStyle === 'function') {
513        var computedStyleValue = window$3.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$3.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$3.parent !== window$3.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
vendor: 22,010 bytes, lines 773-1433
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;
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$3.pageXOffset || body.scrollLeft;
1091      var left = box.left + scrollLeft - clientLeft;
1092      var clientTop = docEl.clientTop || body.clientTop || 0;
1093      var scrollTop = window$3.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`
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      __proto__: null,
1365      isReal: isReal,
1366      isEl: isEl,
1367      isInFrame: isInFrame,
1368      createEl: createEl,
1369      textContent: textContent,
1370      prependTo: prependTo,
1371      hasClass: hasClass,
1372      addClass: addClass,
1373      removeClass: removeClass,
1374      toggleClass: toggleClass,
1375      setAttributes: setAttributes,
1376      getAttributes: getAttributes,
1377      getAttribute: getAttribute,
1378      setAttribute: setAttribute,
1379      removeAttribute: removeAttribute,
1380      blockTextSelection: blockTextSelection,
1381      unblockTextSelection: unblockTextSelection,
1382      getBoundingClientRect: getBoundingClientRect,
1383      findPosition: findPosition,
1384      getPointerPosition: getPointerPosition,
1385      isTextNode: isTextNode,
1386      emptyEl: emptyEl,
1387      normalizeContent: normalizeContent,
1388      appendContent: appendContent,
1389      insertContent: insertContent,
1390      isSingleLeftClick: isSingleLeftClick,
1391      $: $,
1392      $$: $$
1393    });
1394  
1395    /**
1396     * @file setup.js - Functions for setting up a player without
1397     * user interaction based on the data-setup `attribute` of the video tag.
1398     *
1399     * @module setup
1400     */
1401    var _windowLoaded = false;
1402    var videojs;
1403    /**
1404     * Set up any tags that have a data-setup `attribute` when the player is started.
1405     */
1406  
1407    var autoSetup = function autoSetup() {
1408      // Protect against breakage in non-browser environments and check global autoSetup option.
1409      if (!isReal() || videojs.options.autoSetup === false) {
1410        return;
1411      }
1412  
1413      var vids = Array.prototype.slice.call(document.getElementsByTagName('video'));
1414      var audios = Array.prototype.slice.call(document.getElementsByTagName('audio'));
1415      var divs = Array.prototype.slice.call(document.getElementsByTagName('video-js'));
1416      var mediaEls = vids.concat(audios, divs); // Check if any media elements exist
1417  
1418      if (mediaEls && mediaEls.length > 0) {
1419        for (var i = 0, e = mediaEls.length; i < e; i++) {
1420          var mediaEl = mediaEls[i]; // Check if element exists, has getAttribute func.
1421  
1422          if (mediaEl && mediaEl.getAttribute) {
1423            // Make sure this player hasn't already been set up.
1424            if (mediaEl.player === undefined) {
1425              var options = mediaEl.getAttribute('data-setup'); // Check if data-setup attr exists.
1426              // We only auto-setup if they've added the data-setup attr.
1427  
1428              if (options !== null) {
1429                // Create new video.js instance.
1430                videojs(mediaEl);
1431              }
1432            } // If getAttribute isn't defined, we need to wait for the DOM.
1433  
1434          } else {
1435            autoSetupTimeout(1);
1436            break;
1437          }
1438        } // No videos were found, so keep looping unless page is finished loading.
1439  
1440      } else if (!_windowLoaded) {
1441        autoSetupTimeout(1);
1442      }
1443    };
1444    /**
1445     * Wait until the page is loaded before running autoSetup. This will be called in
1446     * autoSetup if `hasLoaded` returns false.
1447     *
1448     * @param {number} wait
1449     *        How long to wait in ms
1450     *
1451     * @param {module:videojs} [vjs]
1452     *        The videojs library function
1453     */
1454  
1455  
1456    function autoSetupTimeout(wait, vjs) {
1457      if (vjs) {
1458        videojs = vjs;
1459      }
1460  
1461      window$3.setTimeout(autoSetup, wait);
1462    }
1463    /**
1464     * Used to set the internal tracking of window loaded state to true.
1465     *
1466     * @private
1467     */
1468  
1469  
1470    function setWindowLoaded() {
1471      _windowLoaded = true;
1472      window$3.removeEventListener('load', setWindowLoaded);
1473    }
1474  
1475    if (isReal()) {
1476      if (document.readyState === 'complete') {
1477        setWindowLoaded();
1478      } else {
1479        /**
1480         * Listen for the load event on window, and set _windowLoaded to true.
1481         *
1482         * We use a standard event listener here to avoid incrementing the GUID
1483         * before any players are created.
1484         *
1485         * @listens load
1486         */
1487        window$3.addEventListener('load', setWindowLoaded);
1488      }
1489    }
1490  
1491    /**
1492     * @file stylesheet.js
1493     * @module stylesheet
1494     */
1495    /**
1496     * Create a DOM syle element given a className for it.
1497     *
1498     * @param {string} className
1499     *        The className to add to the created style element.
1500     *
1501     * @return {Element}
1502     *         The element that was created.
1503     */
1504  
1505    var createStyleElement = function createStyleElement(className) {
1506      var style = document.createElement('style');
1507      style.className = className;
1508      return style;
1509    };
1510    /**
1511     * Add text to a DOM element.
1512     *
1513     * @param {Element} el
1514     *        The Element to add text content to.
1515     *
1516     * @param {string} content
1517     *        The text to add to the element.
1518     */
1519  
1520    var setTextContent = function setTextContent(el, content) {
1521      if (el.styleSheet) {
1522        el.styleSheet.cssText = content;
1523      } else {
1524        el.textContent = content;
1525      }
1526    };
1527  
1528    /**
1529     * @file guid.js
1530     * @module guid
1531     */
1532    // Default value for GUIDs. This allows us to reset the GUID counter in tests.
1533    //
1534    // The initial GUID is 3 because some users have come to rely on the first
1535    // default player ID ending up as `vjs_video_3`.
1536    //
1537    // See: https://github.com/videojs/video.js/pull/6216
1538    var _initialGuid = 3;
1539    /**
1540     * Unique ID for an element or function
1541     *
1542     * @type {Number}
1543     */
1544  
1545    var _guid = _initialGuid;
1546    /**
1547     * Get a unique auto-incrementing ID by number that has not been returned before.
1548     *
1549     * @return {number}
1550     *         A new unique ID.
1551     */
1552  
1553    function newGUID() {
1554      return _guid++;
1555    }
1556  
1557    /**
1558     * @file dom-data.js
1559     * @module dom-data
1560     */
1561    var FakeWeakMap;
1562  
1563    if (!window$3.WeakMap) {
1564      FakeWeakMap = /*#__PURE__*/function () {
1565        function FakeWeakMap() {
1566          this.vdata = 'vdata' + Math.floor(window$3.performance && window$3.performance.now() || Date.now());
1567          this.data = {};
1568        }
1569  
1570        var _proto = FakeWeakMap.prototype;
1571  
1572        _proto.set = function set(key, value) {
1573          var access = key[this.vdata] || newGUID();
1574  
1575          if (!key[this.vdata]) {
1576            key[this.vdata] = access;
1577          }
1578  
1579          this.data[access] = value;
1580          return this;
1581        };
1582  
1583        _proto.get = function get(key) {
1584          var access = key[this.vdata]; // we have data, return it
1585  
1586          if (access) {
1587            return this.data[access];
1588          } // we don't have data, return nothing.
1589          // return undefined explicitly as that's the contract for this method
1590  
1591  
1592          log('We have no data for this element', key);
1593          return undefined;
1594        };
1595  
1596        _proto.has = function has(key) {
1597          var access = key[this.vdata];
1598          return access in this.data;
1599        };
1600  
1601        _proto["delete"] = function _delete(key) {
1602          var access = key[this.vdata];
1603  
1604          if (access) {
1605            delete this.data[access];
1606            delete key[this.vdata];
1607          }
1608        };
1609  
1610        return FakeWeakMap;
1611      }();
1612    }
1613    /**
1614     * Element Data Store.
1615     *
1616     * Allows for binding data to an element without putting it directly on the
1617     * element. Ex. Event listeners are stored here.
1618     * (also from jsninja.com, slightly modified and updated for closure compiler)
1619     *
1620     * @type {Object}
1621     * @private
1622     */
1623  
1624  
1625    var DomData = window$3.WeakMap ? new WeakMap() : new FakeWeakMap();
1626  
1627    /**
1628     * @file events.js. An Event System (John Resig - Secrets of a JS Ninja http://jsninja.com/)
1629     * (Original book version wasn't completely usable, so fixed some things and made Closure Compiler compatible)
1630     * This should work very similarly to jQuery's events, however it's based off the book version which isn't as
1631     * robust as jquery's, so there's probably some differences.
1632     *
1633     * @file events.js
1634     * @module events
1635     */
1636    /**
1637     * Clean up the listener cache and dispatchers
1638     *
1639     * @param {Element|Object} elem
1640     *        Element to clean up
1641     *
1642     * @param {string} type
1643     *        Type of event to clean up
1644     */
1645  
1646    function _cleanUpEvents(elem, type) {
1647      if (!DomData.has(elem)) {
1648        return;
1649      }
1650  
1651      var data = DomData.get(elem); // Remove the events of a particular type if there are none left
1652  
1653      if (data.handlers[type].length === 0) {
1654        delete data.handlers[type]; // data.handlers[type] = null;
1655        // Setting to null was causing an error with data.handlers
1656        // Remove the meta-handler from the element
1657  
1658        if (elem.removeEventListener) {
1659          elem.removeEventListener(type, data.dispatcher, false);
1660        } else if (elem.detachEvent) {
1661          elem.detachEvent('on' + type, data.dispatcher);
1662        }
1663      } // Remove the events object if there are no types left
1664  
1665  
1666      if (Object.getOwnPropertyNames(data.handlers).length <= 0) {
1667        delete data.handlers;
1668        delete data.dispatcher;
1669        delete data.disabled;
1670      } // Finally remove the element data if there is no data left
1671  
1672  
1673      if (Object.getOwnPropertyNames(data).length === 0) {
1674        DomData["delete"](elem);
1675      }
1676    }
1677    /**
1678     * Loops through an array of event types and calls the requested method for each type.
1679     *
1680     * @param {Function} fn
1681     *        The event method we want to use.
1682     *
1683     * @param {Element|Object} elem
1684     *        Element or object to bind listeners to
1685     *
1686     * @param {string} type
1687     *        Type of event to bind to.
1688     *
1689     * @param {EventTarget~EventListener} callback
1690     *        Event listener.
1691     */
1692  
1693  
1694    function _handleMultipleEvents(fn, elem, types, callback) {
1695      types.forEach(function (type) {
1696        // Call the event method for each one of the types
1697        fn(elem, type, callback);
1698      });
1699    }
1700    /**
1701     * Fix a native event to have standard property values
1702     *
1703     * @param {Object} event
1704     *        Event object to fix.
1705     *
1706     * @return {Object}
1707     *         Fixed event object.
1708     */
1709  
1710  
1711    function fixEvent(event) {
1712      if (event.fixed_) {
1713        return event;
1714      }
1715  
1716      function returnTrue() {
1717        return true;
1718      }
1719  
1720      function returnFalse() {
1721        return false;
1722      } // Test if fixing up is needed
1723      // Used to check if !event.stopPropagation instead of isPropagationStopped
1724      // But native events return true for stopPropagation, but don't have
1725      // other expected methods like isPropagationStopped. Seems to be a problem
1726      // with the Javascript Ninja code. So we're just overriding all events now.
1727  
1728  
1729      if (!event || !event.isPropagationStopped) {
1730        var old = event || window$3.event;
1731        event = {}; // Clone the old object so that we can modify the values event = {};
1732        // IE8 Doesn't like when you mess with native event properties
1733        // Firefox returns false for event.hasOwnProperty('type') and other props
1734        //  which makes copying more difficult.
1735        // TODO: Probably best to create a whitelist of event props
1736  
1737        for (var key in old) {
1738          // Safari 6.0.3 warns you if you try to copy deprecated layerX/Y
1739          // Chrome warns you if you try to copy deprecated keyboardEvent.keyLocation
1740          // and webkitMovementX/Y
1741          if (key !== 'layerX' && key !== 'layerY' && key !== 'keyLocation' && key !== 'webkitMovementX' && key !== 'webkitMovementY') {
1742            // Chrome 32+ warns if you try to copy deprecated returnValue, but
1743            // we still want to if preventDefault isn't supported (IE8).
1744            if (!(key === 'returnValue' && old.preventDefault)) {
1745              event[key] = old[key];
1746            }
1747          }
1748        } // The event occurred on this element
1749  
1750  
1751        if (!event.target) {
1752          event.target = event.srcElement || document;
1753        } // Handle which other element the event is related to
1754  
1755  
1756        if (!event.relatedTarget) {
1757          event.relatedTarget = event.fromElement === event.target ? event.toElement : event.fromElement;
1758        } // Stop the default browser action
1759  
1760  
1761        event.preventDefault = function () {
1762          if (old.preventDefault) {
1763            old.preventDefault();
1764          }
1765  
1766          event.returnValue = false;
vendor: 4,748 bytes, lines 1767-1924
1767          old.returnValue = false;
1768          event.defaultPrevented = true;
1769        };
1770  
1771        event.defaultPrevented = false; // Stop the event from bubbling
1772  
1773        event.stopPropagation = function () {
1774          if (old.stopPropagation) {
1775            old.stopPropagation();
1776          }
1777  
1778          event.cancelBubble = true;
1779          old.cancelBubble = true;
1780          event.isPropagationStopped = returnTrue;
1781        };
1782  
1783        event.isPropagationStopped = returnFalse; // Stop the event from bubbling and executing other handlers
1784  
1785        event.stopImmediatePropagation = function () {
1786          if (old.stopImmediatePropagation) {
1787            old.stopImmediatePropagation();
1788          }
1789  
1790          event.isImmediatePropagationStopped = returnTrue;
1791          event.stopPropagation();
1792        };
1793  
1794        event.isImmediatePropagationStopped = returnFalse; // Handle mouse position
1795  
1796        if (event.clientX !== null && event.clientX !== undefined) {
1797          var doc = document.documentElement;
1798          var body = document.body;
1799          event.pageX = event.clientX + (doc && doc.scrollLeft || body && body.scrollLeft || 0) - (doc && doc.clientLeft || body && body.clientLeft || 0);
1800          event.pageY = event.clientY + (doc && doc.scrollTop || body && body.scrollTop || 0) - (doc && doc.clientTop || body && body.clientTop || 0);
1801        } // Handle key presses
1802  
1803  
1804        event.which = event.charCode || event.keyCode; // Fix button for mouse clicks:
1805        // 0 == left; 1 == middle; 2 == right
1806  
1807        if (event.button !== null && event.button !== undefined) {
1808          // The following is disabled because it does not pass videojs-standard
1809          // and... yikes.
1810  
1811          /* eslint-disable */
1812          event.button = event.button & 1 ? 0 : event.button & 4 ? 1 : event.button & 2 ? 2 : 0;
1813          /* eslint-enable */
1814        }
1815      }
1816  
1817      event.fixed_ = true; // Returns fixed-up instance
1818  
1819      return event;
1820    }
1821    /**
1822     * Whether passive event listeners are supported
1823     */
1824  
1825    var _supportsPassive;
1826  
1827    var supportsPassive = function supportsPassive() {
1828      if (typeof _supportsPassive !== 'boolean') {
1829        _supportsPassive = false;
1830  
1831        try {
1832          var opts = Object.defineProperty({}, 'passive', {
1833            get: function get() {
1834              _supportsPassive = true;
1835            }
1836          });
1837          window$3.addEventListener('test', null, opts);
1838          window$3.removeEventListener('test', null, opts);
1839        } catch (e) {// disregard
1840        }
1841      }
1842  
1843      return _supportsPassive;
1844    };
1845    /**
1846     * Touch events Chrome expects to be passive
1847     */
1848  
1849  
1850    var passiveEvents = ['touchstart', 'touchmove'];
1851    /**
1852     * Add an event listener to element
1853     * It stores the handler function in a separate cache object
1854     * and adds a generic handler to the element's event,
1855     * along with a unique id (guid) to the element.
1856     *
1857     * @param {Element|Object} elem
1858     *        Element or object to bind listeners to
1859     *
1860     * @param {string|string[]} type
1861     *        Type of event to bind to.
1862     *
1863     * @param {EventTarget~EventListener} fn
1864     *        Event listener.
1865     */
1866  
1867    function on(elem, type, fn) {
1868      if (Array.isArray(type)) {
1869        return _handleMultipleEvents(on, elem, type, fn);
1870      }
1871  
1872      if (!DomData.has(elem)) {
1873        DomData.set(elem, {});
1874      }
1875  
1876      var data = DomData.get(elem); // We need a place to store all our handler data
1877  
1878      if (!data.handlers) {
1879        data.handlers = {};
1880      }
1881  
1882      if (!data.handlers[type]) {
1883        data.handlers[type] = [];
1884      }
1885  
1886      if (!fn.guid) {
1887        fn.guid = newGUID();
1888      }
1889  
1890      data.handlers[type].push(fn);
1891  
1892      if (!data.dispatcher) {
1893        data.disabled = false;
1894  
1895        data.dispatcher = function (event, hash) {
1896          if (data.disabled) {
1897            return;
1898          }
1899  
1900          event = fixEvent(event);
1901          var handlers = data.handlers[event.type];
1902  
1903          if (handlers) {
1904            // Copy handlers so if handlers are added/removed during the process it doesn't throw everything off.
1905            var handlersCopy = handlers.slice(0);
1906  
1907            for (var m = 0, n = handlersCopy.length; m < n; m++) {
1908              if (event.isImmediatePropagationStopped()) {
1909                break;
1910              } else {
1911                try {
1912                  handlersCopy[m].call(elem, event, hash);
1913                } catch (e) {
1914                  log.error(e);
1915                }
1916              }
1917            }
1918          }
1919        };
1920      }
1921  
1922      if (data.handlers[type].length === 1) {
1923        if (elem.addEventListener) {
1924          var options = false;
vendor: 11,738 bytes, lines 1925-2313
1925  
1926          if (supportsPassive() && passiveEvents.indexOf(type) > -1) {
1927            options = {
1928              passive: true
1929            };
1930          }
1931  
1932          elem.addEventListener(type, data.dispatcher, options);
1933        } else if (elem.attachEvent) {
1934          elem.attachEvent('on' + type, data.dispatcher);
1935        }
1936      }
1937    }
1938    /**
1939     * Removes event listeners from an element
1940     *
1941     * @param {Element|Object} elem
1942     *        Object to remove listeners from.
1943     *
1944     * @param {string|string[]} [type]
1945     *        Type of listener to remove. Don't include to remove all events from element.
1946     *
1947     * @param {EventTarget~EventListener} [fn]
1948     *        Specific listener to remove. Don't include to remove listeners for an event
1949     *        type.
1950     */
1951  
1952    function off(elem, type, fn) {
1953      // Don't want to add a cache object through getElData if not needed
1954      if (!DomData.has(elem)) {
1955        return;
1956      }
1957  
1958      var data = DomData.get(elem); // If no events exist, nothing to unbind
1959  
1960      if (!data.handlers) {
1961        return;
1962      }
1963  
1964      if (Array.isArray(type)) {
1965        return _handleMultipleEvents(off, elem, type, fn);
1966      } // Utility function
1967  
1968  
1969      var removeType = function removeType(el, t) {
1970        data.handlers[t] = [];
1971  
1972        _cleanUpEvents(el, t);
1973      }; // Are we removing all bound events?
1974  
1975  
1976      if (type === undefined) {
1977        for (var t in data.handlers) {
1978          if (Object.prototype.hasOwnProperty.call(data.handlers || {}, t)) {
1979            removeType(elem, t);
1980          }
1981        }
1982  
1983        return;
1984      }
1985  
1986      var handlers = data.handlers[type]; // If no handlers exist, nothing to unbind
1987  
1988      if (!handlers) {
1989        return;
1990      } // If no listener was provided, remove all listeners for type
1991  
1992  
1993      if (!fn) {
1994        removeType(elem, type);
1995        return;
1996      } // We're only removing a single handler
1997  
1998  
1999      if (fn.guid) {
2000        for (var n = 0; n < handlers.length; n++) {
2001          if (handlers[n].guid === fn.guid) {
2002            handlers.splice(n--, 1);
2003          }
2004        }
2005      }
2006  
2007      _cleanUpEvents(elem, type);
2008    }
2009    /**
2010     * Trigger an event for an element
2011     *
2012     * @param {Element|Object} elem
2013     *        Element to trigger an event on
2014     *
2015     * @param {EventTarget~Event|string} event
2016     *        A string (the type) or an event object with a type attribute
2017     *
2018     * @param {Object} [hash]
2019     *        data hash to pass along with the event
2020     *
2021     * @return {boolean|undefined}
2022     *         Returns the opposite of `defaultPrevented` if default was
2023     *         prevented. Otherwise, returns `undefined`
2024     */
2025  
2026    function trigger(elem, event, hash) {
2027      // Fetches element data and a reference to the parent (for bubbling).
2028      // Don't want to add a data object to cache for every parent,
2029      // so checking hasElData first.
2030      var elemData = DomData.has(elem) ? DomData.get(elem) : {};
2031      var parent = elem.parentNode || elem.ownerDocument; // type = event.type || event,
2032      // handler;
2033      // If an event name was passed as a string, creates an event out of it
2034  
2035      if (typeof event === 'string') {
2036        event = {
2037          type: event,
2038          target: elem
2039        };
2040      } else if (!event.target) {
2041        event.target = elem;
2042      } // Normalizes the event properties.
2043  
2044  
2045      event = fixEvent(event); // If the passed element has a dispatcher, executes the established handlers.
2046  
2047      if (elemData.dispatcher) {
2048        elemData.dispatcher.call(elem, event, hash);
2049      } // Unless explicitly stopped or the event does not bubble (e.g. media events)
2050      // recursively calls this function to bubble the event up the DOM.
2051  
2052  
2053      if (parent && !event.isPropagationStopped() && event.bubbles === true) {
2054        trigger.call(null, parent, event, hash); // If at the top of the DOM, triggers the default action unless disabled.
2055      } else if (!parent && !event.defaultPrevented && event.target && event.target[event.type]) {
2056        if (!DomData.has(event.target)) {
2057          DomData.set(event.target, {});
2058        }
2059  
2060        var targetData = DomData.get(event.target); // Checks if the target has a default action for this event.
2061  
2062        if (event.target[event.type]) {
2063          // Temporarily disables event dispatching on the target as we have already executed the handler.
2064          targetData.disabled = true; // Executes the default action.
2065  
2066          if (typeof event.target[event.type] === 'function') {
2067            event.target[event.type]();
2068          } // Re-enables event dispatching.
2069  
2070  
2071          targetData.disabled = false;
2072        }
2073      } // Inform the triggerer if the default was prevented by returning false
2074  
2075  
2076      return !event.defaultPrevented;
2077    }
2078    /**
2079     * Trigger a listener only once for an event.
2080     *
2081     * @param {Element|Object} elem
2082     *        Element or object to bind to.
2083     *
2084     * @param {string|string[]} type
2085     *        Name/type of event
2086     *
2087     * @param {Event~EventListener} fn
2088     *        Event listener function
2089     */
2090  
2091    function one(elem, type, fn) {
2092      if (Array.isArray(type)) {
2093        return _handleMultipleEvents(one, elem, type, fn);
2094      }
2095  
2096      var func = function func() {
2097        off(elem, type, func);
2098        fn.apply(this, arguments);
2099      }; // copy the guid to the new function so it can removed using the original function's ID
2100  
2101  
2102      func.guid = fn.guid = fn.guid || newGUID();
2103      on(elem, type, func);
2104    }
2105    /**
2106     * Trigger a listener only once and then turn if off for all
2107     * configured events
2108     *
2109     * @param {Element|Object} elem
2110     *        Element or object to bind to.
2111     *
2112     * @param {string|string[]} type
2113     *        Name/type of event
2114     *
2115     * @param {Event~EventListener} fn
2116     *        Event listener function
2117     */
2118  
2119    function any(elem, type, fn) {
2120      var func = function func() {
2121        off(elem, type, func);
2122        fn.apply(this, arguments);
2123      }; // copy the guid to the new function so it can removed using the original function's ID
2124  
2125  
2126      func.guid = fn.guid = fn.guid || newGUID(); // multiple ons, but one off for everything
2127  
2128      on(elem, type, func);
2129    }
2130  
2131    var Events = /*#__PURE__*/Object.freeze({
2132      __proto__: null,
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$3.performance.now();
2200  
2201      var throttled = function throttled() {
2202        var now = window$3.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$3;
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  
vendor: 24,124 bytes, lines 2465-3178
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$3.clearTimeout(oldTimeout);
2484      var timeout = window$3.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  
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$3.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}
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 = /*#__PURE__*/function () {
3228      /**
3229       * A callback that is called when a component is ready. Does not have any
3230       * paramters and any callback value will be ignored.
3231       *
3232       * @callback Component~ReadyCallback
3233       * @this Component
3234       */
3235  
3236      /**
3237       * Creates an instance of this class.
3238       *
3239       * @param {Player} player
3240       *        The `Player` that this class should be attached to.
3241       *
3242       * @param {Object} [options]
3243       *        The key/value store of player options.
3244       *
3245       * @param {Object[]} [options.children]
3246       *        An array of children objects to intialize this component with. Children objects have
3247       *        a name property that will be used if more than one component of the same type needs to be
3248       *        added.
3249       *
3250       * @param {Component~ReadyCallback} [ready]
3251       *        Function that gets called when the `Component` is ready.
3252       */
3253      function Component(player, options, ready) {
3254        // The component might be the player itself and we can't pass `this` to super
3255        if (!player && this.play) {
3256          this.player_ = player = this; // eslint-disable-line
3257        } else {
3258          this.player_ = player;
3259        }
3260  
3261        this.isDisposed_ = false; // Hold the reference to the parent component via `addChild` method
3262  
3263        this.parentComponent_ = null; // Make a copy of prototype.options_ to protect against overriding defaults
3264  
3265        this.options_ = mergeOptions({}, this.options_); // Updated options with supplied options
3266  
3267        options = this.options_ = mergeOptions(this.options_, options); // Get ID from options or options element if one is supplied
3268  
3269        this.id_ = options.id || options.el && options.el.id; // If there was no ID from the options, generate one
3270  
3271        if (!this.id_) {
3272          // Don't require the player ID function in the case of mock players
3273          var id = player && player.id && player.id() || 'no_player';
3274          this.id_ = id + "_component_" + newGUID();
3275        }
3276  
3277        this.name_ = options.name || null; // Create element if one wasn't provided in options
3278  
3279        if (options.el) {
3280          this.el_ = options.el;
3281        } else if (options.createEl !== false) {
3282          this.el_ = this.createEl();
3283        } // if evented is anything except false, we want to mixin in evented
3284  
3285  
3286        if (options.evented !== false) {
3287          // Make this an evented object and use `el_`, if available, as its event bus
3288          evented(this, {
3289            eventBusKey: this.el_ ? 'el_' : null
3290          });
3291        }
3292  
3293        stateful(this, this.constructor.defaultState);
3294        this.children_ = [];
3295        this.childIndex_ = {};
3296        this.childNameIndex_ = {};
3297        var SetSham;
3298  
3299        if (!window$3.Set) {
3300          SetSham = /*#__PURE__*/function () {
3301            function SetSham() {
3302              this.set_ = {};
3303            }
3304  
3305            var _proto2 = SetSham.prototype;
3306  
3307            _proto2.has = function has(key) {
3308              return key in this.set_;
3309            };
3310  
3311            _proto2["delete"] = function _delete(key) {
3312              var has = this.has(key);
3313              delete this.set_[key];
3314              return has;
3315            };
3316  
3317            _proto2.add = function add(key) {
3318              this.set_[key] = 1;
3319              return this;
3320            };
3321  
3322            _proto2.forEach = function forEach(callback, thisArg) {
3323              for (var key in this.set_) {
3324                callback.call(thisArg, key, key, this);
3325              }
3326            };
3327  
3328            return SetSham;
3329          }();
3330        }
3331  
3332        this.setTimeoutIds_ = window$3.Set ? new Set() : new SetSham();
3333        this.setIntervalIds_ = window$3.Set ? new Set() : new SetSham();
3334        this.rafIds_ = window$3.Set ? new Set() : new SetSham();
3335        this.clearingTimersOnDispose_ = false; // Add any child components in options
3336  
3337        if (options.initChildren !== false) {
3338          this.initChildren();
3339        }
3340  
3341        this.ready(ready); // Don't want to trigger ready here or it will before init is actually
3342        // finished for all children that run this constructor
3343  
3344        if (options.reportTouchActivity !== false) {
3345          this.enableTouchActivity();
3346        }
3347      }
3348      /**
3349       * Dispose of the `Component` and all child components.
3350       *
3351       * @fires Component#dispose
3352       */
3353  
3354  
3355      var _proto = Component.prototype;
3356  
3357      _proto.dispose = function dispose() {
3358        // Bail out if the component has already been disposed.
3359        if (this.isDisposed_) {
3360          return;
3361        }
3362        /**
3363         * Triggered when a `Component` is disposed.
3364         *
3365         * @event Component#dispose
3366         * @type {EventTarget~Event}
3367         *
3368         * @property {boolean} [bubbles=false]
3369         *           set to false so that the dispose event does not
3370         *           bubble up
3371         */
3372  
3373  
3374        this.trigger({
3375          type: 'dispose',
3376          bubbles: false
3377        });
3378        this.isDisposed_ = true; // Dispose all children.
3379  
3380        if (this.children_) {
3381          for (var i = this.children_.length - 1; i >= 0; i--) {
3382            if (this.children_[i].dispose) {
3383              this.children_[i].dispose();
3384            }
3385          }
3386        } // Delete child references
3387  
3388  
3389        this.children_ = null;
3390        this.childIndex_ = null;
3391        this.childNameIndex_ = null;
3392        this.parentComponent_ = null;
3393  
3394        if (this.el_) {
3395          // Remove element from DOM
3396          if (this.el_.parentNode) {
3397            this.el_.parentNode.removeChild(this.el_);
3398          }
3399  
3400          if (DomData.has(this.el_)) {
3401            DomData["delete"](this.el_);
3402          }
3403  
3404          this.el_ = null;
3405        } // remove reference to the player after disposing of the element
3406  
3407  
3408        this.player_ = null;
3409      }
3410      /**
3411       * Determine whether or not this component has been disposed.
3412       *
3413       * @return {boolean}
3414       *         If the component has been disposed, will be `true`. Otherwise, `false`.
3415       */
3416      ;
3417  
3418      _proto.isDisposed = function isDisposed() {
3419        return Boolean(this.isDisposed_);
3420      }
3421      /**
3422       * Return the {@link Player} that the `Component` has attached to.
3423       *
3424       * @return {Player}
3425       *         The player that this `Component` has attached to.
3426       */
3427      ;
3428  
3429      _proto.player = function player() {
3430        return this.player_;
3431      }
3432      /**
3433       * Deep merge of options objects with new options.
3434       * > Note: When both `obj` and `options` contain properties whose values are objects.
3435       *         The two properties get merged using {@link module:mergeOptions}
3436       *
3437       * @param {Object} obj
3438       *        The object that contains new options.
3439       *
3440       * @return {Object}
3441       *         A new object of `this.options_` and `obj` merged together.
3442       */
3443      ;
3444  
3445      _proto.options = function options(obj) {
3446        if (!obj) {
3447          return this.options_;
3448        }
3449  
3450        this.options_ = mergeOptions(this.options_, obj);
3451        return this.options_;
3452      }
3453      /**
3454       * Get the `Component`s DOM element
3455       *
3456       * @return {Element}
3457       *         The DOM element for this `Component`.
3458       */
3459      ;
3460  
3461      _proto.el = function el() {
3462        return this.el_;
3463      }
3464      /**
3465       * Create the `Component`s DOM element.
3466       *
3467       * @param {string} [tagName]
3468       *        Element's DOM node type. e.g. 'div'
3469       *
3470       * @param {Object} [properties]
3471       *        An object of properties that should be set.
3472       *
3473       * @param {Object} [attributes]
3474       *        An object of attributes that should be set.
3475       *
3476       * @return {Element}
3477       *         The element that gets created.
3478       */
3479      ;
3480  
3481      _proto.createEl = function createEl$1(tagName, properties, attributes) {
3482        return createEl(tagName, properties, attributes);
3483      }
3484      /**
3485       * Localize a string given the string in english.
3486       *
3487       * If tokens are provided, it'll try and run a simple token replacement on the provided string.
3488       * The tokens it looks for look like `{1}` with the index being 1-indexed into the tokens array.
3489       *
3490       * If a `defaultValue` is provided, it'll use that over `string`,
3491       * if a value isn't found in provided language files.
3492       * This is useful if you want to have a descriptive key for token replacement
3493       * but have a succinct localized string and not require `en.json` to be included.
3494       *
3495       * Currently, it is used for the progress bar timing.
3496       * ```js
3497       * {
3498       *   "progress bar timing: currentTime={1} duration={2}": "{1} of {2}"
3499       * }
3500       * ```
3501       * It is then used like so:
3502       * ```js
3503       * this.localize('progress bar timing: currentTime={1} duration{2}',
3504       *               [this.player_.currentTime(), this.player_.duration()],
3505       *               '{1} of {2}');
3506       * ```
3507       *
3508       * Which outputs something like: `01:23 of 24:56`.
3509       *
3510       *
3511       * @param {string} string
3512       *        The string to localize and the key to lookup in the language files.
3513       * @param {string[]} [tokens]
3514       *        If the current item has token replacements, provide the tokens here.
3515       * @param {string} [defaultValue]
3516       *        Defaults to `string`. Can be a default value to use for token replacement
3517       *        if the lookup key is needed to be separate.
3518       *
3519       * @return {string}
3520       *         The localized string or if no localization exists the english string.
3521       */
3522      ;
3523  
3524      _proto.localize = function localize(string, tokens, defaultValue) {
3525        if (defaultValue === void 0) {
3526          defaultValue = string;
3527        }
3528  
3529        var code = this.player_.language && this.player_.language();
3530        var languages = this.player_.languages && this.player_.languages();
3531        var language = languages && languages[code];
3532        var primaryCode = code && code.split('-')[0];
3533        var primaryLang = languages && languages[primaryCode];
3534        var localizedString = defaultValue;
3535  
3536        if (language && language[string]) {
3537          localizedString = language[string];
3538        } else if (primaryLang && primaryLang[string]) {
3539          localizedString = primaryLang[string];
3540        }
3541  
3542        if (tokens) {
3543          localizedString = localizedString.replace(/\{(\d+)\}/g, function (match, index) {
3544            var value = tokens[index - 1];
3545            var ret = value;
3546  
3547            if (typeof value === 'undefined') {
3548              ret = match;
3549            }
3550  
3551            return ret;
3552          });
3553        }
3554  
3555        return localizedString;
3556      }
3557      /**
3558       * Return the `Component`s DOM element. This is where children get inserted.
3559       * This will usually be the the same as the element returned in {@link Component#el}.
3560       *
3561       * @return {Element}
3562       *         The content element for this `Component`.
3563       */
3564      ;
3565  
3566      _proto.contentEl = function contentEl() {
3567        return this.contentEl_ || this.el_;
3568      }
3569      /**
3570       * Get this `Component`s ID
3571       *
3572       * @return {string}
3573       *         The id of this `Component`
3574       */
3575      ;
3576  
3577      _proto.id = function id() {
3578        return this.id_;
3579      }
3580      /**
3581       * Get the `Component`s name. The name gets used to reference the `Component`
3582       * and is set during registration.
3583       *
3584       * @return {string}
3585       *         The name of this `Component`.
3586       */
3587      ;
3588  
3589      _proto.name = function name() {
3590        return this.name_;
3591      }
3592      /**
3593       * Get an array of all child components
3594       *
3595       * @return {Array}
3596       *         The children
3597       */
3598      ;
3599  
3600      _proto.children = function children() {
3601        return this.children_;
3602      }
3603      /**
3604       * Returns the child `Component` with the given `id`.
3605       *
3606       * @param {string} id
3607       *        The id of the child `Component` to get.
3608       *
3609       * @return {Component|undefined}
3610       *         The child `Component` with the given `id` or undefined.
3611       */
3612      ;
3613  
3614      _proto.getChildById = function getChildById(id) {
3615        return this.childIndex_[id];
3616      }
3617      /**
3618       * Returns the child `Component` with the given `name`.
3619       *
3620       * @param {string} name
3621       *        The name of the child `Component` to get.
3622       *
3623       * @return {Component|undefined}
3624       *         The child `Component` with the given `name` or undefined.
3625       */
3626      ;
3627  
3628      _proto.getChild = function getChild(name) {
3629        if (!name) {
3630          return;
3631        }
3632  
3633        return this.childNameIndex_[name];
3634      }
3635      /**
3636       * Returns the descendant `Component` following the givent
3637       * descendant `names`. For instance ['foo', 'bar', 'baz'] would
3638       * try to get 'foo' on the current component, 'bar' on the 'foo'
3639       * component and 'baz' on the 'bar' component and return undefined
3640       * if any of those don't exist.
3641       *
3642       * @param {...string[]|...string} names
3643       *        The name of the child `Component` to get.
3644       *
3645       * @return {Component|undefined}
3646       *         The descendant `Component` following the given descendant
3647       *         `names` or undefined.
3648       */
3649      ;
3650  
3651      _proto.getDescendant = function getDescendant() {
3652        for (var _len = arguments.length, names = new Array(_len), _key = 0; _key < _len; _key++) {
3653          names[_key] = arguments[_key];
3654        }
3655  
3656        // flatten array argument into the main array
3657        names = names.reduce(function (acc, n) {
3658          return acc.concat(n);
3659        }, []);
3660        var currentChild = this;
3661  
3662        for (var i = 0; i < names.length; i++) {
3663          currentChild = currentChild.getChild(names[i]);
3664  
3665          if (!currentChild || !currentChild.getChild) {
3666            return;
3667          }
3668        }
3669  
3670        return currentChild;
3671      }
3672      /**
3673       * Add a child `Component` inside the current `Component`.
3674       *
3675       *
3676       * @param {string|Component} child
3677       *        The name or instance of a child to add.
3678       *
3679       * @param {Object} [options={}]
3680       *        The key/value store of options that will get passed to children of
3681       *        the child.
3682       *
3683       * @param {number} [index=this.children_.length]
3684       *        The index to attempt to add a child into.
3685       *
3686       * @return {Component}
3687       *         The `Component` that gets added as a child. When using a string the
3688       *         `Component` will get created by this process.
3689       */
3690      ;
3691  
3692      _proto.addChild = function addChild(child, options, index) {
3693        if (options === void 0) {
3694          options = {};
3695        }
3696  
3697        if (index === void 0) {
3698          index = this.children_.length;
3699        }
3700  
3701        var component;
3702        var componentName; // If child is a string, create component with options
3703  
3704        if (typeof child === 'string') {
3705          componentName = toTitleCase(child);
3706          var componentClassName = options.componentClass || componentName; // Set name through options
3707  
3708          options.name = componentName; // Create a new object & element for this controls set
3709          // If there's no .player_, this is a player
3710  
3711          var ComponentClass = Component.getComponent(componentClassName);
3712  
3713          if (!ComponentClass) {
3714            throw new Error("Component " + componentClassName + " does not exist");
3715          } // data stored directly on the videojs object may be
3716          // misidentified as a component to retain
3717          // backwards-compatibility with 4.x. check to make sure the
3718          // component class can be instantiated.
3719  
3720  
3721          if (typeof ComponentClass !== 'function') {
3722            return null;
3723          }
3724  
3725          component = new ComponentClass(this.player_ || this, options); // child is a component instance
3726        } else {
3727          component = child;
3728        }
3729  
3730        if (component.parentComponent_) {
3731          component.parentComponent_.removeChild(component);
3732        }
3733  
3734        this.children_.splice(index, 0, component);
3735        component.parentComponent_ = this;
3736  
3737        if (typeof component.id === 'function') {
3738          this.childIndex_[component.id()] = component;
3739        } // If a name wasn't used to create the component, check if we can use the
3740        // name function of the component
3741  
3742  
3743        componentName = componentName || component.name && toTitleCase(component.name());
3744  
3745        if (componentName) {
3746          this.childNameIndex_[componentName] = component;
3747          this.childNameIndex_[toLowerCase(componentName)] = component;
3748        } // Add the UI object's element to the container div (box)
3749        // Having an element is not required
3750  
3751  
3752        if (typeof component.el === 'function' && component.el()) {
3753          // If inserting before a component, insert before that component's element
3754          var refNode = null;
3755  
3756          if (this.children_[index + 1]) {
3757            // Most children are components, but the video tech is an HTML element
3758            if (this.children_[index + 1].el_) {
3759              refNode = this.children_[index + 1].el_;
3760            } else if (isEl(this.children_[index + 1])) {
3761              refNode = this.children_[index + 1];
3762            }
3763          }
3764  
3765          this.contentEl().insertBefore(component.el(), refNode);
3766        } // Return so it can stored on parent object if desired.
3767  
3768  
3769        return component;
3770      }
3771      /**
3772       * Remove a child `Component` from this `Component`s list of children. Also removes
3773       * the child `Component`s element from this `Component`s element.
3774       *
3775       * @param {Component} component
3776       *        The child `Component` to remove.
3777       */
3778      ;
3779  
3780      _proto.removeChild = function removeChild(component) {
3781        if (typeof component === 'string') {
3782          component = this.getChild(component);
3783        }
3784  
3785        if (!component || !this.children_) {
3786          return;
3787        }
3788  
3789        var childFound = false;
3790  
3791        for (var i = this.children_.length - 1; i >= 0; i--) {
3792          if (this.children_[i] === component) {
3793            childFound = true;
3794            this.children_.splice(i, 1);
3795            break;
3796          }
3797        }
3798  
3799        if (!childFound) {
3800          return;
3801        }
3802  
3803        component.parentComponent_ = null;
3804        this.childIndex_[component.id()] = null;
3805        this.childNameIndex_[toTitleCase(component.name())] = null;
3806        this.childNameIndex_[toLowerCase(component.name())] = null;
3807        var compEl = component.el();
3808  
3809        if (compEl && compEl.parentNode === this.contentEl()) {
3810          this.contentEl().removeChild(component.el());
3811        }
3812      }
3813      /**
3814       * Add and initialize default child `Component`s based upon options.
3815       */
3816      ;
3817  
3818      _proto.initChildren = function initChildren() {
3819        var _this = this;
3820  
3821        var children = this.options_.children;
3822  
3823        if (children) {
3824          // `this` is `parent`
3825          var parentOptions = this.options_;
3826  
3827          var handleAdd = function handleAdd(child) {
3828            var name = child.name;
3829            var opts = child.opts; // Allow options for children to be set at the parent options
3830            // e.g. videojs(id, { controlBar: false });
3831            // instead of videojs(id, { children: { controlBar: false });
3832  
3833            if (parentOptions[name] !== undefined) {
3834              opts = parentOptions[name];
3835            } // Allow for disabling default components
3836            // e.g. options['children']['posterImage'] = false
3837  
3838  
3839            if (opts === false) {
3840              return;
3841            } // Allow options to be passed as a simple boolean if no configuration
3842            // is necessary.
3843  
3844  
3845            if (opts === true) {
3846              opts = {};
3847            } // We also want to pass the original player options
3848            // to each component as well so they don't need to
3849            // reach back into the player for options later.
3850  
3851  
3852            opts.playerOptions = _this.options_.playerOptions; // Create and add the child component.
3853            // Add a direct reference to the child by name on the parent instance.
3854            // If two of the same component are used, different names should be supplied
3855            // for each
3856  
3857            var newChild = _this.addChild(name, opts);
3858  
3859            if (newChild) {
3860              _this[name] = newChild;
3861            }
3862          }; // Allow for an array of children details to passed in the options
3863  
3864  
3865          var workingChildren;
3866          var Tech = Component.getComponent('Tech');
3867  
3868          if (Array.isArray(children)) {
3869            workingChildren = children;
3870          } else {
3871            workingChildren = Object.keys(children);
3872          }
3873  
3874          workingChildren // children that are in this.options_ but also in workingChildren  would
3875          // give us extra children we do not want. So, we want to filter them out.
3876          .concat(Object.keys(this.options_).filter(function (child) {
3877            return !workingChildren.some(function (wchild) {
3878              if (typeof wchild === 'string') {
3879                return child === wchild;
3880              }
3881  
3882              return child === wchild.name;
3883            });
3884          })).map(function (child) {
3885            var name;
3886            var opts;
3887  
3888            if (typeof child === 'string') {
3889              name = child;
3890              opts = children[name] || _this.options_[name] || {};
3891            } else {
3892              name = child.name;
3893              opts = child;
3894            }
3895  
3896            return {
3897              name: name,
3898              opts: opts
3899            };
3900          }).filter(function (child) {
3901            // we have to make sure that child.name isn't in the techOrder since
3902            // techs are registerd as Components but can't aren't compatible
3903            // See https://github.com/videojs/video.js/issues/2772
3904            var c = Component.getComponent(child.opts.componentClass || toTitleCase(child.name));
3905            return c && !Tech.isTech(c);
3906          }
vendor: 4,369 bytes, lines 3906-4040
3906).forEach(handleAdd);
3907        }
3908      }
3909      /**
3910       * Builds the default DOM class name. Should be overriden by sub-components.
3911       *
3912       * @return {string}
3913       *         The DOM class name for this object.
3914       *
3915       * @abstract
3916       */
3917      ;
3918  
3919      _proto.buildCSSClass = function buildCSSClass() {
3920        // Child classes can include a function that does:
3921        // return 'CLASS NAME' + this._super();
3922        return '';
3923      }
3924      /**
3925       * Bind a listener to the component's ready state.
3926       * Different from event listeners in that if the ready event has already happened
3927       * it will trigger the function immediately.
3928       *
3929       * @return {Component}
3930       *         Returns itself; method can be chained.
3931       */
3932      ;
3933  
3934      _proto.ready = function ready(fn, sync) {
3935        if (sync === void 0) {
3936          sync = false;
3937        }
3938  
3939        if (!fn) {
3940          return;
3941        }
3942  
3943        if (!this.isReady_) {
3944          this.readyQueue_ = this.readyQueue_ || [];
3945          this.readyQueue_.push(fn);
3946          return;
3947        }
3948  
3949        if (sync) {
3950          fn.call(this);
3951        } else {
3952          // Call the function asynchronously by default for consistency
3953          this.setTimeout(fn, 1);
3954        }
3955      }
3956      /**
3957       * Trigger all the ready listeners for this `Component`.
3958       *
3959       * @fires Component#ready
3960       */
3961      ;
3962  
3963      _proto.triggerReady = function triggerReady() {
3964        this.isReady_ = true; // Ensure ready is triggered asynchronously
3965  
3966        this.setTimeout(function () {
3967          var readyQueue = this.readyQueue_; // Reset Ready Queue
3968  
3969          this.readyQueue_ = [];
3970  
3971          if (readyQueue && readyQueue.length > 0) {
3972            readyQueue.forEach(function (fn) {
3973              fn.call(this);
3974            }, this);
3975          } // Allow for using event listeners also
3976  
3977          /**
3978           * Triggered when a `Component` is ready.
3979           *
3980           * @event Component#ready
3981           * @type {EventTarget~Event}
3982           */
3983  
3984  
3985          this.trigger('ready');
3986        }, 1);
3987      }
3988      /**
3989       * Find a single DOM element matching a `selector`. This can be within the `Component`s
3990       * `contentEl()` or another custom context.
3991       *
3992       * @param {string} selector
3993       *        A valid CSS selector, which will be passed to `querySelector`.
3994       *
3995       * @param {Element|string} [context=this.contentEl()]
3996       *        A DOM element within which to query. Can also be a selector string in
3997       *        which case the first matching element will get used as context. If
3998       *        missing `this.contentEl()` gets used. If  `this.contentEl()` returns
3999       *        nothing it falls back to `document`.
4000       *
4001       * @return {Element|null}
4002       *         the dom element that was found, or null
4003       *
4004       * @see [Information on CSS Selectors](https://developer.mozilla.org/en-US/docs/Web/Guide/CSS/Getting_Started/Selectors)
4005       */
4006      ;
4007  
4008      _proto.$ = function $$1(selector, context) {
4009        return $(selector, context || this.contentEl());
4010      }
4011      /**
4012       * Finds all DOM element matching a `selector`. This can be within the `Component`s
4013       * `contentEl()` or another custom context.
4014       *
4015       * @param {string} selector
4016       *        A valid CSS selector, which will be passed to `querySelectorAll`.
4017       *
4018       * @param {Element|string} [context=this.contentEl()]
4019       *        A DOM element within which to query. Can also be a selector string in
4020       *        which case the first matching element will get used as context. If
4021       *        missing `this.contentEl()` gets used. If  `this.contentEl()` returns
4022       *        nothing it falls back to `document`.
4023       *
4024       * @return {NodeList}
4025       *         a list of dom elements that were found
4026       *
4027       * @see [Information on CSS Selectors](https://developer.mozilla.org/en-US/docs/Web/Guide/CSS/Getting_Started/Selectors)
4028       */
4029      ;
4030  
4031      _proto.$$ = function $$$1(selector, context) {
4032        return $$(selector, context || this.contentEl());
4033      }
4034      /**
4035       * Check if a component's element has a CSS class name.
4036       *
4037       * @param {string} classToCheck
4038       *        CSS class name to check.
4039       *
4040       * @return {boolean}
4041       *         - True if the `Component` has the class.
4042       *         - False if the `Component` does not have the class`
4043       */
4044      ;
4045  
4046      _proto.hasClass = function hasClass$1(classToCheck) {
4047        return hasClass(this.el_, classToCheck);
4048      }
4049      /**
4050       * Add a CSS class name to the `Component`s element.
4051       *
4052       * @param {string} classToAdd
4053       *        CSS class name to add
4054       */
4055      ;
4056  
4057      _proto.addClass = function addClass$1(classToAdd) {
4058        addClass(this.el_, classToAdd);
4059      }
4060      /**
4061       * Remove a CSS class name from the `Component`s element.
4062       *
4063       * @param {string} classToRemove
4064       *        CSS class name to remove
4065       */
4066      ;
4067  
4068      _proto.removeClass = function removeClass$1(classToRemove) {
4069        removeClass(this.el_, classToRemove);
4070      }
4071      /**
4072       * Add or remove a CSS class name from the component's element.
4073       * - `classToToggle` gets added when {@link Component#hasClass} would return false.
4074       * - `classToToggle` gets removed when {@link Component#hasClass} would return true.
4075       *
4076       * @param  {string} classToToggle
4077       *         The class to add or remove based on (@link Component#hasClass}
4078       *
4079       * @param  {boolean|Dom~predicate} [predicate]
4080       *         An {@link Dom~predicate} function or a boolean
4081       */
4082      ;
4083  
4084      _proto.toggleClass = function toggleClass$1(classToToggle, predicate) {
4085        toggleClass(this.el_, classToToggle, predicate);
4086      }
4087      /**
4088       * Show the `Component`s element if it is hidden by removing the
4089       * 'vjs-hidden' class name from it.
4090       */
4091      ;
4092  
4093      _proto.show = function show() {
4094        this.removeClass('vjs-hidden');
4095      }
4096      /**
4097       * Hide the `Component`s element if it is currently showing by adding the
4098       * 'vjs-hidden` class name to it.
4099       */
4100      ;
4101  
4102      _proto.hide = function hide() {
4103        this.addClass('vjs-hidden');
4104      }
4105      /**
4106       * Lock a `Component`s element in its visible state by adding the 'vjs-lock-showing'
4107       * class name to it. Used during fadeIn/fadeOut.
4108       *
4109       * @private
4110       */
4111      ;
4112  
4113      _proto.lockShowing = function lockShowing() {
4114        this.addClass('vjs-lock-showing');
4115      }
4116      /**
4117       * Unlock a `Component`s element from its visible state by removing the 'vjs-lock-showing'
4118       * class name from it. Used during fadeIn/fadeOut.
4119       *
4120       * @private
4121       */
4122      ;
4123  
4124      _proto.unlockShowing = function unlockShowing() {
4125        this.removeClass('vjs-lock-showing');
4126      }
4127      /**
4128       * Get the value of an attribute on the `Component`s element.
4129       *
4130       * @param {string} attribute
4131       *        Name of the attribute to get the value from.
4132       *
4133       * @return {string|null}
4134       *         - The value of the attribute that was asked for.
4135       *         - Can be an empty string on some browsers if the attribute does not exist
4136       *           or has no value
4137       *         - Most browsers will return null if the attibute does not exist or has
4138       *           no value.
4139       *
4140       * @see [DOM API]{@link https://developer.mozilla.org/en-US/docs/Web/API/Element/getAttribute}
4141       */
4142      ;
4143  
4144      _proto.getAttribute = function getAttribute$1(attribute) {
4145        return getAttribute(this.el_, attribute);
4146      }
4147      /**
4148       * Set the value of an attribute on the `Component`'s element
4149       *
4150       * @param {string} attribute
4151       *        Name of the attribute to set.
4152       *
4153       * @param {string} value
4154       *        Value to set the attribute to.
4155       *
4156       * @see [DOM API]{@link https://developer.mozilla.org/en-US/docs/Web/API/Element/setAttribute}
4157       */
4158      ;
4159  
4160      _proto.setAttribute = function setAttribute$1(attribute, value) {
4161        setAttribute(this.el_, attribute, value);
4162      }
4163      /**
4164       * Remove an attribute from the `Component`s element.
4165       *
4166       * @param {string} attribute
4167       *        Name of the attribute to remove.
4168       *
4169       * @see [DOM API]{@link https://developer.mozilla.org/en-US/docs/Web/API/Element/removeAttribute}
4170       */
4171      ;
4172  
4173      _proto.removeAttribute = function removeAttribute$1(attribute) {
4174        removeAttribute(this.el_, attribute);
4175      }
4176      /**
4177       * Get or set the width of the component based upon the CSS styles.
4178       * See {@link Component#dimension} for more detailed information.
4179       *
4180       * @param {number|string} [num]
4181       *        The width that you want to set postfixed with '%', 'px' or nothing.
4182       *
4183       * @param {boolean} [skipListeners]
4184       *        Skip the componentresize event trigger
4185       *
4186       * @return {number|string}
4187       *         The width when getting, zero if there is no width. Can be a string
4188       *           postpixed with '%' or 'px'.
4189       */
4190      ;
4191  
4192      _proto.width = function width(num, skipListeners) {
4193        return this.dimension('width', num, skipListeners);
4194      }
4195      /**
4196       * Get or set the height of the component based upon the CSS styles.
4197       * See {@link Component#dimension} for more detailed information.
4198       *
4199       * @param {number|string} [num]
4200       *        The height that you want to set postfixed with '%', 'px' or nothing.
4201       *
4202       * @param {boolean} [skipListeners]
4203       *        Skip the componentresize event trigger
4204       *
4205       * @return {number|string}
4206       *         The width when getting, zero if there is no width. Can be a string
4207       *         postpixed with '%' or 'px'.
4208       */
4209      ;
4210  
4211      _proto.height = function height(num, skipListeners) {
4212        return this.dimension('height', num, skipListeners);
4213      }
4214      /**
4215       * Set both the width and height of the `Component` element at the same time.
4216       *
4217       * @param  {number|string} width
4218       *         Width to set the `Component`s element to.
4219       *
4220       * @param  {number|string} height
4221       *         Height to set the `Component`s element to.
4222       */
4223      ;
4224  
4225      _proto.dimensions = function dimensions(width, height) {
4226        // Skip componentresize listeners on width for optimization
4227        this.width(width, true);
4228        this.height(height);
4229      }
4230      /**
4231       * Get or set width or height of the `Component` element. This is the shared code
4232       * for the {@link Component#width} and {@link Component#height}.
4233       *
4234       * Things to know:
4235       * - If the width or height in an number this will return the number postfixed with 'px'.
4236       * - If the width/height is a percent this will return the percent postfixed with '%'
4237       * - Hidden elements have a width of 0 with `window.getComputedStyle`. This function
4238       *   defaults to the `Component`s `style.width` and falls back to `window.getComputedStyle`.
4239       *   See [this]{@link http://www.foliotek.com/devblog/getting-the-width-of-a-hidden-element-with-jquery-u
4239sing-width/}
4240       *   for more information
4241       * - If you want the computed style of the component, use {@link Component#currentWidth}
4242       *   and {@link {Component#currentHeight}
4243       *
4244       * @fires Component#componentresize
4245       *
4246       * @param {string} widthOrHeight
4247       8        'width' or 'height'
4248       *
4249       * @param  {number|string} [num]
4250       8         New dimension
4251       *
4252       * @param  {boolean} [skipListeners]
4253       *         Skip componentresize event trigger
4254       *
4255       * @return {number}
4256       *         The dimension when getting or 0 if unset
4257       */
4258      ;
4259  
4260      _proto.dimension = function dimension(widthOrHeight, num, skipListeners) {
4261        if (num !== undefined) {
4262          // Set to zero if null or literally NaN (NaN !== NaN)
4263          if (num === null || num !== num) {
4264            num = 0;
4265          } // Check if using css width/height (% or px) and adjust
4266  
4267  
4268          if (('' + num).indexOf('%') !== -1 || ('' + num).indexOf('px') !== -1) {
4269            this.el_.style[widthOrHeight] = num;
4270          } else if (num === 'auto') {
4271            this.el_.style[widthOrHeight] = '';
4272          } else {
4273            this.el_.style[widthOrHeight] = num + 'px';
4274          } // skipListeners allows us to avoid triggering the resize event when setting both width and height
4275  
4276  
4277          if (!skipListeners) {
4278            /**
4279             * Triggered when a component is resized.
4280             *
4281             * @event Component#componentresize
4282             * @type {EventTarget~Event}
4283             */
4284            this.trigger('componentresize');
4285          }
4286  
4287          return;
4288        } // Not setting a value, so getting it
4289        // Make sure element exists
4290  
4291  
4292        if (!this.el_) {
4293          return 0;
4294        } // Get dimension value from style
4295  
4296  
4297        var val = this.el_.style[widthOrHeight];
4298        var pxIndex = val.indexOf('px');
4299  
4300        if (pxIndex !== -1) {
4301          // Return the pixel value with no 'px'
4302          return parseInt(val.slice(0, pxIndex), 10);
4303        } // No px so using % or no style was set, so falling back to offsetWidth/height
4304        // If component has display:none, offset will return 0
4305        // TODO: handle display:none and no dimension style using px
4306  
4307  
4308        return parseInt(this.el_['offset' + toTitleCase(widthOrHeight)], 10);
4309      }
4310      /**
4311       * Get the computed width or the height of the component's element.
4312       *
4313       * Uses `window.getComputedStyle`.
4314       *
4315       * @param {string} widthOrHeight
4316       *        A string containing 'width' or 'height'. Whichever one you want to get.
4317       *
4318       * @return {number}
4319       *         The dimension that gets asked for or 0 if nothing was set
4320       *         for that dimension.
4321       */
4322      ;
4323  
4324      _proto.currentDimension = function currentDimension(widthOrHeight) {
4325        var computedWidthOrHeight = 0;
4326  
4327        if (widthOrHeight !== 'width' && widthOrHeight !== 'height') {
4328          throw new Error('currentDimension only accepts width or height value');
4329        }
4330  
4331        computedWidthOrHeight = computedStyle(this.el_, widthOrHeight); // remove 'px' from variable and parse as integer
4332  
4333        computedWidthOrHeight = parseFloat(computedWidthOrHeight); // if the computed value is still 0, it's possible that the browser is lying
4334        // and we want to check the offset values.
4335        // This code also runs wherever getComputedStyle doesn't exist.
4336  
4337        if (computedWidthOrHeight === 0 || isNaN(computedWidthOrHeight)) {
4338          var rule = "offset" + toTitleCase(widthOrHeight);
4339          computedWidthOrHeight = this.el_[rule];
4340        }
4341  
4342        return computedWidthOrHeight;
4343      }
4344      /**
4345       * An object that contains width and height values of the `Component`s
4346       * computed style. Uses `window.getComputedStyle`.
4347       *
4348       * @typedef {Object} Component~DimensionObject
4349       *
4350       * @property {number} width
4351       *           The width of the `Component`s computed style.
4352       *
4353       * @property {number} height
4354       *           The height of the `Component`s computed style.
4355       */
4356  
4357      /**
4358       * Get an object that contains computed width and height values of the
4359       * component's element.
4360       *
4361       * Uses `window.getComputedStyle`.
4362       *
4363       * @return {Component~DimensionObject}
4364       *         The computed dimensions of the component's element.
4365       */
4366      ;
4367  
4368      _proto.currentDimensions = function currentDimensions() {
4369        return {
4370          width: this.currentDimension('width'),
4371          height: this.currentDimension('height')
4372        };
4373      }
4374      /**
4375       * Get the computed width of the component's element.
4376       *
4377       * Uses `window.getComputedStyle`.
4378       *
4379       * @return {number}
4380       *         The computed width of the component's element.
4381       */
4382      ;
4383  
4384      _proto.currentWidth = function currentWidth() {
4385        return this.currentDimension('width');
4386      }
4387      /**
4388       * Get the computed height of the component's element.
4389       *
4390       * Uses `window.getComputedStyle`.
4391       *
4392       * @return {number}
4393       *         The computed height of the component's element.
4394       */
4395      ;
4396  
4397      _proto.currentHeight = function currentHeight() {
4398        return this.currentDimension('height');
4399      }
4400      /**
4401       * Set the focus to this component
4402       */
4403      ;
4404  
4405      _proto.focus = function focus() {
4406        this.el_.focus();
4407      }
4408      /**
4409       * Remove the focus from this component
4410       */
4411      ;
4412  
4413      _proto.blur = function blur() {
4414        this.el_.blur();
4415      }
4416      /**
4417       * When this Component receives a `keydown` event which it does not process,
4418       *  it passes the event to the Player for handling.
4419       *
4420       * @param {EventTarget~Event} event
vendor: 11,043 bytes, lines 4421-4682
4421       *        The `keydown` event that caused this function to be called.
4422       */
4423      ;
4424  
4425      _proto.handleKeyDown = function handleKeyDown(event) {
4426        if (this.player_) {
4427          // We only stop propagation here because we want unhandled events to fall
4428          // back to the browser.
4429          event.stopPropagation();
4430          this.player_.handleKeyDown(event);
4431        }
4432      }
4433      /**
4434       * Many components used to have a `handleKeyPress` method, which was poorly
4435       * named because it listened to a `keydown` event. This method name now
4436       * delegates to `handleKeyDown`. This means anyone calling `handleKeyPress`
4437       * will not see their method calls stop working.
4438       *
4439       * @param {EventTarget~Event} event
4440       *        The event that caused this function to be called.
4441       */
4442      ;
4443  
4444      _proto.handleKeyPress = function handleKeyPress(event) {
4445        this.handleKeyDown(event);
4446      }
4447      /**
4448       * Emit a 'tap' events when touch event support gets detected. This gets used to
4449       * support toggling the controls through a tap on the video. They get enabled
4450       * because every sub-component would have extra overhead otherwise.
4451       *
4452       * @private
4453       * @fires Component#tap
4454       * @listens Component#touchstart
4455       * @listens Component#touchmove
4456       * @listens Component#touchleave
4457       * @listens Component#touchcancel
4458       * @listens Component#touchend
4459        */
4460      ;
4461  
4462      _proto.emitTapEvents = function emitTapEvents() {
4463        // Track the start time so we can determine how long the touch lasted
4464        var touchStart = 0;
4465        var firstTouch = null; // Maximum movement allowed during a touch event to still be considered a tap
4466        // Other popular libs use anywhere from 2 (hammer.js) to 15,
4467        // so 10 seems like a nice, round number.
4468  
4469        var tapMovementThreshold = 10; // The maximum length a touch can be while still being considered a tap
4470  
4471        var touchTimeThreshold = 200;
4472        var couldBeTap;
4473        this.on('touchstart', function (event) {
4474          // If more than one finger, don't consider treating this as a click
4475          if (event.touches.length === 1) {
4476            // Copy pageX/pageY from the object
4477            firstTouch = {
4478              pageX: event.touches[0].pageX,
4479              pageY: event.touches[0].pageY
4480            }; // Record start time so we can detect a tap vs. "touch and hold"
4481  
4482            touchStart = window$3.performance.now(); // Reset couldBeTap tracking
4483  
4484            couldBeTap = true;
4485          }
4486        });
4487        this.on('touchmove', function (event) {
4488          // If more than one finger, don't consider treating this as a click
4489          if (event.touches.length > 1) {
4490            couldBeTap = false;
4491          } else if (firstTouch) {
4492            // Some devices will throw touchmoves for all but the slightest of taps.
4493            // So, if we moved only a small distance, this could still be a tap
4494            var xdiff = event.touches[0].pageX - firstTouch.pageX;
4495            var ydiff = event.touches[0].pageY - firstTouch.pageY;
4496            var touchDistance = Math.sqrt(xdiff * xdiff + ydiff * ydiff);
4497  
4498            if (touchDistance > tapMovementThreshold) {
4499              couldBeTap = false;
4500            }
4501          }
4502        });
4503  
4504        var noTap = function noTap() {
4505          couldBeTap = false;
4506        }; // TODO: Listen to the original target. http://youtu.be/DujfpXOKUp8?t=13m8s
4507  
4508  
4509        this.on('touchleave', noTap);
4510        this.on('touchcancel', noTap); // When the touch ends, measure how long it took and trigger the appropriate
4511        // event
4512  
4513        this.on('touchend', function (event) {
4514          firstTouch = null; // Proceed only if the touchmove/leave/cancel event didn't happen
4515  
4516          if (couldBeTap === true) {
4517            // Measure how long the touch lasted
4518            var touchTime = window$3.performance.now() - touchStart; // Make sure the touch was less than the threshold to be considered a tap
4519  
4520            if (touchTime < touchTimeThreshold) {
4521              // Don't let browser turn this into a click
4522              event.preventDefault();
4523              /**
4524               * Triggered when a `Component` is tapped.
4525               *
4526               * @event Component#tap
4527               * @type {EventTarget~Event}
4528               */
4529  
4530              this.trigger('tap'); // It may be good to copy the touchend event object and change the
4531              // type to tap, if the other event properties aren't exact after
4532              // Events.fixEvent runs (e.g. event.target)
4533            }
4534          }
4535        });
4536      }
4537      /**
4538       * This function reports user activity whenever touch events happen. This can get
4539       * turned off by any sub-components that wants touch events to act another way.
4540       *
4541       * Report user touch activity when touch events occur. User activity gets used to
4542       * determine when controls should show/hide. It is simple when it comes to mouse
4543       * events, because any mouse event should show the controls. So we capture mouse
4544       * events that bubble up to the player and report activity when that happens.
4545       * With touch events it isn't as easy as `touchstart` and `touchend` toggle player
4546       * controls. So touch events can't help us at the player level either.
4547       *
4548       * User activity gets checked asynchronously. So what could happen is a tap event
4549       * on the video turns the controls off. Then the `touchend` event bubbles up to
4550       * the player. Which, if it reported user activity, would turn the controls right
4551       * back on. We also don't want to completely block touch events from bubbling up.
4552       * Furthermore a `touchmove` event and anything other than a tap, should not turn
4553       * controls back on.
4554       *
4555       * @listens Component#touchstart
4556       * @listens Component#touchmove
4557       * @listens Component#touchend
4558       * @listens Component#touchcancel
4559       */
4560      ;
4561  
4562      _proto.enableTouchActivity = function enableTouchActivity() {
4563        // Don't continue if the root player doesn't support reporting user activity
4564        if (!this.player() || !this.player().reportUserActivity) {
4565          return;
4566        } // listener for reporting that the user is active
4567  
4568  
4569        var report = bind(this.player(), this.player().reportUserActivity);
4570        var touchHolding;
4571        this.on('touchstart', function () {
4572          report(); // For as long as the they are touching the device or have their mouse down,
4573          // we consider them active even if they're not moving their finger or mouse.
4574          // So we want to continue to update that they are active
4575  
4576          this.clearInterval(touchHolding); // report at the same interval as activityCheck
4577  
4578          touchHolding = this.setInterval(report, 250);
4579        });
4580  
4581        var touchEnd = function touchEnd(event) {
4582          report(); // stop the interval that maintains activity if the touch is holding
4583  
4584          this.clearInterval(touchHolding);
4585        };
4586  
4587        this.on('touchmove', report);
4588        this.on('touchend', touchEnd);
4589        this.on('touchcancel', touchEnd);
4590      }
4591      /**
4592       * A callback that has no parameters and is bound into `Component`s context.
4593       *
4594       * @callback Component~GenericCallback
4595       * @this Component
4596       */
4597  
4598      /**
4599       * Creates a function that runs after an `x` millisecond timeout. This function is a
4600       * wrapper around `window.setTimeout`. There are a few reasons to use this one
4601       * instead though:
4602       * 1. It gets cleared via  {@link Component#clearTimeout} when
4603       *    {@link Component#dispose} gets called.
4604       * 2. The function callback will gets turned into a {@link Component~GenericCallback}
4605       *
4606       * > Note: You can't use `window.clearTimeout` on the id returned by this function. This
4607       *         will cause its dispose listener not to get cleaned up! Please use
4608       *         {@link Component#clearTimeout} or {@link Component#dispose} instead.
4609       *
4610       * @param {Component~GenericCallback} fn
4611       *        The function that will be run after `timeout`.
4612       *
4613       * @param {number} timeout
4614       *        Timeout in milliseconds to delay before executing the specified function.
4615       *
4616       * @return {number}
4617       *         Returns a timeout ID that gets used to identify the timeout. It can also
4618       *         get used in {@link Component#clearTimeout} to clear the timeout that
4619       *         was set.
4620       *
4621       * @listens Component#dispose
4622       * @see [Similar to]{@link https://developer.mozilla.org/en-US/docs/Web/API/WindowTimers/setTimeout}
4623       */
4624      ;
4625  
4626      _proto.setTimeout = function setTimeout(fn, timeout) {
4627        var _this2 = this;
4628  
4629        // declare as variables so they are properly available in timeout function
4630        // eslint-disable-next-line
4631        var timeoutId;
4632        fn = bind(this, fn);
4633        this.clearTimersOnDispose_();
4634        timeoutId = window$3.setTimeout(function () {
4635          if (_this2.setTimeoutIds_.has(timeoutId)) {
4636            _this2.setTimeoutIds_["delete"](timeoutId);
4637          }
4638  
4639          fn();
4640        }, timeout);
4641        this.setTimeoutIds_.add(timeoutId);
4642        return timeoutId;
4643      }
4644      /**
4645       * Clears a timeout that gets created via `window.setTimeout` or
4646       * {@link Component#setTimeout}. If you set a timeout via {@link Component#setTimeout}
4647       * use this function instead of `window.clearTimout`. If you don't your dispose
4648       * listener will not get cleaned up until {@link Component#dispose}!
4649       *
4650       * @param {number} timeoutId
4651       *        The id of the timeout to clear. The return value of
4652       *        {@link Component#setTimeout} or `window.setTimeout`.
4653       *
4654       * @return {number}
4655       *         Returns the timeout id that was cleared.
4656       *
4657       * @see [Similar to]{@link https://developer.mozilla.org/en-US/docs/Web/API/WindowTimers/clearTimeout}
4658       */
4659      ;
4660  
4661      _proto.clearTimeout = function clearTimeout(timeoutId) {
4662        if (this.setTimeoutIds_.has(timeoutId)) {
4663          this.setTimeoutIds_["delete"](timeoutId);
4664          window$3.clearTimeout(timeoutId);
4665        }
4666  
4667        return timeoutId;
4668      }
4669      /**
4670       * Creates a function that gets run every `x` milliseconds. This function is a wrapper
4671       * around `window.setInterval`. There are a few reasons to use this one instead though.
4672       * 1. It gets cleared via  {@link Component#clearInterval} when
4673       *    {@link Component#dispose} gets called.
4674       * 2. The function callback will be a {@link Component~GenericCallback}
4675       *
4676       * @param {Component~GenericCallback} fn
4677       *        The function to run every `x` seconds.
4678       *
4679       * @param {number} interval
4680       *        Execute the specified function every `x` milliseconds.
4681       *
4682       * @return {number}
4683       *         Returns an id that can be used to identify the interval. It can also be be used in
4684       *         {@link Component#clearInterval} to clear the interval.
4685       *
4686       * @listens Component#dispose
4687       * @see [Similar to]{@link https://developer.mozilla.org/en-US/docs/Web/API/WindowTimers/setInterval}
4688       */
4689      ;
4690  
4691      _proto.setInterval = function setInterval(fn, interval) {
4692        fn = bind(this, fn);
4693        this.clearTimersOnDispose_();
4694        var intervalId = window$3.setInterval(fn, interval);
4695        this.setIntervalIds_.add(intervalId);
4696        return intervalId;
4697      }
4698      /**
4699       * Clears an interval that gets created via `window.setInterval` or
4700       * {@link Component#setInterval}. If you set an inteval via {@link Component#setInterval}
4701       * use this function instead of `window.clearInterval`. If you don't your dispose
4702       * listener will not get cleaned up until {@link Component#dispose}!
4703       *
4704       * @param {number} intervalId
4705       *        The id of the interval to clear. The return value of
4706       *        {@link Component#setInterval} or `window.setInterval`.
4707       *
4708       * @return {number}
4709       *         Returns the interval id that was cleared.
4710       *
4711       * @see [Similar to]{@link https://developer.mozilla.org/en-US/docs/Web/API/WindowTimers/clearInterval}
4712       */
4713      ;
4714  
4715      _proto.clearInterval = function clearInterval(intervalId) {
4716        if (this.setIntervalIds_.has(intervalId)) {
4717          this.setIntervalIds_["delete"](intervalId);
4718          window$3.clearInterval(intervalId);
4719        }
4720  
4721        return intervalId;
4722      }
4723      /**
4724       * Queues up a callback to be passed to requestAnimationFrame (rAF), but
4725       * with a few extra bonuses:
4726       *
4727       * - Supports browsers that do not support rAF by falling back to
4728       *   {@link Component#setTimeout}.
4729       *
4730       * - The callback is turned into a {@link Component~GenericCallback} (i.e.
4731       *   bound to the component).
4732       *
4733       * - Automatic cancellation of the rAF callback is handled if the component
4734       *   is disposed before it is called.
4735       *
4736       * @param  {Component~GenericCallback} fn
4737       *         A function that will be bound to this component and executed just
4738       *         before the browser's next repaint.
4739       *
4740       * @return {number}
4741       *         Returns an rAF ID that gets used to identify the timeout. It can
4742       *         also be used in {@link Component#cancelAnimationFrame} to cancel
4743       *         the animation frame callback.
4744       *
4745       * @listens Component#dispose
4746       * @see [Similar to]{@link https://developer.mozilla.org/en-US/docs/Web/API/window/requestAnimationFrame}
4747       */
4748      ;
4749  
4750      _proto.requestAnimationFrame = function requestAnimationFrame(fn) {
4751        var _this3 = this;
4752  
4753        // Fall back to using a timer.
4754        if (!this.supportsRaf_) {
4755          return this.setTimeout(fn, 1000 / 60);
4756        }
4757  
4758        this.clearTimersOnDispose_(); // declare as variables so they are properly available in rAF function
4759        // eslint-disable-next-line
4760  
4761        var id;
4762        fn = bind(this, fn);
4763        id = window$3.requestAnimationFrame(function () {
4764          if (_this3.rafIds_.has(id)) {
4765            _this3.rafIds_["delete"](id);
4766          }
4767  
4768          fn();
4769        });
4770        this.rafIds_.add(id);
4771        return id;
4772      }
4773      /**
4774       * Cancels a queued callback passed to {@link Component#requestAnimationFrame}
4775       * (rAF).
4776       *
4777       * If you queue an rAF callback via {@link Component#requestAnimationFrame},
4778       * use this function instead of `window.cancelAnimationFrame`. If you don't,
4779       * your dispose listener will not get cleaned up until {@link Component#dispose}!
4780       *
4781       * @param {number} id
4782       *        The rAF ID to clear. The return value of {@link Component#requestAnimationFrame}.
4783       *
4784       * @return {number}
4785       *         Returns the rAF ID that was cleared.
4786       *
4787       * @see [Similar to]{@link https://developer.mozilla.org/en-US/docs/Web/API/window/cancelAnimationFrame}
4788       */
4789      ;
4790  
4791      _proto.cancelAnimationFrame = function cancelAnimationFrame(id) {
4792        // Fall back to using a timer.
4793        if (!this.supportsRaf_) {
4794          return this.clearTimeout(id);
4795        }
4796  
4797        if (this.rafIds_.has(id)) {
4798          this.rafIds_["delete"](id);
4799          window$3.cancelAnimationFrame(id);
4800        }
4801  
4802        return id;
4803      }
4804      /**
4805       * A function to setup `requestAnimationFrame`, `setTimeout`,
4806       * and `setInterval`, clearing on dispose.
4807       *
4808       * > Previously each timer added and removed dispose listeners on it's own.
4809       * For better performance it was decided to batch them all, and use `Set`s
4810       * to track outstanding timer ids.
4811       *
4812       * @private
4813       */
4814      ;
4815  
4816      _proto.clearTimersOnDispose_ = function clearTimersOnDispose_() {
4817        var _this4 = this;
4818  
4819        if (this.clearingTimersOnDispose_) {
4820          return;
4821        }
4822  
4823        this.clearingTimersOnDispose_ = true;
4824        this.one('dispose', function () {
4825          [['rafIds_', 'cancelAnimationFrame'], ['setTimeoutIds_', 'clearTimeout'], ['setIntervalIds_', 'clearInterval']].forEac
4825h(function (_ref) {
4826            var idName = _ref[0],
4827                cancelName = _ref[1];
4828  
4829            _this4[idName].forEach(_this4[cancelName], _this4);
4830          });
4831          _this4.clearingTimersOnDispose_ = false;
4832        });
4833      }
4834      /**
4835       * Register a `Component` with `videojs` given the name and the component.
4836       *
4837       * > NOTE: {@link Tech}s should not be registered as a `Component`. {@link Tech}s
4838       *         should be registered using {@link Tech.registerTech} or
4839       *         {@link videojs:videojs.registerTech}.
4840       *
4841       * > NOTE: This function can also be seen on videojs as
4842       *         {@link videojs:videojs.registerComponent}.
4843       *
4844       * @param {string} name
4845       *        The name of the `Component` to register.
4846       *
4847       * @param {Component} ComponentToRegister
4848       *        The `Component` class to register.
4849       *
4850       * @return {Component}
4851       *         The `Component` that was registered.
4852       */
4853      ;
4854  
4855      Component.registerComponent = function registerComponent(name, ComponentToRegister) {
4856        if (typeof name !== 'string' || !name) {
4857          throw new Error("Illegal component name, \"" + name + "\"; must be a non-empty string.");
4858        }
4859  
4860        var Tech = Component.getComponent('Tech'); // We need to make sure this check is only done if Tech has been registered.
4861  
4862        var isTech = Tech && Tech.isTech(ComponentToRegister);
4863        var isComp = Component === ComponentToRegister || Component.prototype.isPrototypeOf(ComponentToRegister.prototype);
4864  
4865        if (isTech || !isComp) {
4866          var reason;
4867  
4868          if (isTech) {
4869            reason = 'techs must be registered using Tech.registerTech()';
4870          } else {
4871            reason = 'must be a Component subclass';
4872          }
4873  
4874          throw new Error("Illegal component, \"" + name + "\"; " + reason + ".");
4875        }
4876  
4877        name = toTitleCase(name);
4878  
4879        if (!Component.components_) {
4880          Component.components_ = {};
4881        }
4882  
4883        var Player = Component.getComponent('Player');
4884  
4885        if (name === 'Player' && Player && Player.players) {
4886          var players = Player.players;
4887          var playerNames = Object.keys(players); // If we have players that were disposed, then their name will still be
4888          // in Players.players. So, we must loop through and verify that the value
4889          // for each item is not null. This allows registration of the Player component
4890          // after all players have been disposed or before any were created.
4891  
4892          if (players && playerNames.length > 0 && playerNames.map(function (pname) {
4893            return players[pname];
4894          }).every(Boolean)) {
4895            throw new Error('Can not register Player component after player has been created.');
4896          }
4897        }
4898  
4899        Component.components_[name] = ComponentToRegister;
4900        Component.components_[toLowerCase(name)] = ComponentToRegister;
4901        return ComponentToRegister;
4902      }
4903      /**
4904       * Get a `Component` based on the name it was registered with.
4905       *
4906       * @param {string} name
4907       *        The Name of the component to get.
4908       *
4909       * @return {Component}
4910       *         The `Component` that got registered under the given name.
4911       *
4912       * @deprecated In `videojs` 6 this will not return `Component`s that were not
4913       *             registered using {@link Component.registerComponent}. Currently we
4914       *             check the global `videojs` object for a `Component` name and
4915       *             return that if it exists.
4916       */
4917      ;
4918  
4919      Component.getComponent = function getComponent(name) {
4920        if (!name || !Component.components_) {
4921          return;
4922        }
4923  
4924        return Component.components_[name];
4925      };
4926  
4927      return Component;
4928    }();
4929    /**
4930     * Whether or not this component supports `requestAnimationFrame`.
4931     *
4932     * This is exposed primarily for testing purposes.
4933     *
4934     * @private
4935     * @type {Boolean}
4936     */
4937  
4938  
4939    Component.prototype.supportsRaf_ = typeof window$3.requestAnimationFrame === 'function' && typeof window$3.cancelAnimationFrame === 'function';
4940    Component.registerComponent('Component', Component);
4941  
4942    function _assertThisInitialized(self) {
4943      if (self === void 0) {
4944        throw new ReferenceError("this hasn't been initialised - super() hasn't been called");
4945      }
4946  
4947      return self;
4948    }
4949  
4950    var assertThisInitialized = _assertThisInitialized;
4951  
4952    var _typeof_1 = createCommonjsModule(function (module) {
4953      function _typeof(obj) {
4954        "@babel/helpers - typeof";
4955  
4956        if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") {
4957          module.exports = _typeof = function _typeof(obj) {
4958            return typeof obj;
4959          };
4960        } else {
4961          module.exports = _typeof = function _typeof(obj) {
4962            return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj;
4963          };
4964        }
4965  
4966        return _typeof(obj);
4967      }
4968  
4969      module.exports = _typeof;
4970    });
4971  
4972    var getPrototypeOf = createCommonjsModule(function (module) {
vendor: 23,458 bytes, lines 4973-5732
4973      function _getPrototypeOf(o) {
4974        module.exports = _getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf : function _getPrototypeOf(o) {
4975          return o.__proto__ || Object.getPrototypeOf(o);
4976        };
4977        return _getPrototypeOf(o);
4978      }
4979  
4980      module.exports = _getPrototypeOf;
4981    });
4982  
4983    function _inheritsLoose(subClass, superClass) {
4984      subClass.prototype = Object.create(superClass.prototype);
4985      subClass.prototype.constructor = subClass;
4986      subClass.__proto__ = superClass;
4987    }
4988  
4989    var inheritsLoose = _inheritsLoose;
4990  
4991    /**
4992     * @file browser.js
4993     * @module browser
4994     */
4995    var USER_AGENT = window$3.navigator && window$3.navigator.userAgent || '';
4996    var webkitVersionMap = /AppleWebKit\/([\d.]+)/i.exec(USER_AGENT);
4997    var appleWebkitVersion = webkitVersionMap ? parseFloat(webkitVersionMap.pop()) : null;
4998    /**
4999     * Whether or not this device is an iPod.
5000     *
5001     * @static
5002     * @const
5003     * @type {Boolean}
5004     */
5005  
5006    var IS_IPOD = /iPod/i.test(USER_AGENT);
5007    /**
5008     * The detected iOS version - or `null`.
5009     *
5010     * @static
5011     * @const
5012     * @type {string|null}
5013     */
5014  
5015    var IOS_VERSION = function () {
5016      var match = USER_AGENT.match(/OS (\d+)_/i);
5017  
5018      if (match && match[1]) {
5019        return match[1];
5020      }
5021  
5022      return null;
5023    }();
5024    /**
5025     * Whether or not this is an Android device.
5026     *
5027     * @static
5028     * @const
5029     * @type {Boolean}
5030     */
5031  
5032    var IS_ANDROID = /Android/i.test(USER_AGENT);
5033    /**
5034     * The detected Android version - or `null`.
5035     *
5036     * @static
5037     * @const
5038     * @type {number|string|null}
5039     */
5040  
5041    var ANDROID_VERSION = function () {
5042      // This matches Android Major.Minor.Patch versions
5043      // ANDROID_VERSION is Major.Minor as a Number, if Minor isn't available, then only Major is returned
5044      var match = USER_AGENT.match(/Android (\d+)(?:\.(\d+))?(?:\.(\d+))*/i);
5045  
5046      if (!match) {
5047        return null;
5048      }
5049  
5050      var major = match[1] && parseFloat(match[1]);
5051      var minor = match[2] && parseFloat(match[2]);
5052  
5053      if (major && minor) {
5054        return parseFloat(match[1] + '.' + match[2]);
5055      } else if (major) {
5056        return major;
5057      }
5058  
5059      return null;
5060    }();
5061    /**
5062     * Whether or not this is a native Android browser.
5063     *
5064     * @static
5065     * @const
5066     * @type {Boolean}
5067     */
5068  
5069    var IS_NATIVE_ANDROID = IS_ANDROID && ANDROID_VERSION < 5 && appleWebkitVersion < 537;
5070    /**
5071     * Whether or not this is Mozilla Firefox.
5072     *
5073     * @static
5074     * @const
5075     * @type {Boolean}
5076     */
5077  
5078    var IS_FIREFOX = /Firefox/i.test(USER_AGENT);
5079    /**
5080     * Whether or not this is Microsoft Edge.
5081     *
5082     * @static
5083     * @const
5084     * @type {Boolean}
5085     */
5086  
5087    var IS_EDGE = /Edg/i.test(USER_AGENT);
5088    /**
5089     * Whether or not this is Google Chrome.
5090     *
5091     * This will also be `true` for Chrome on iOS, which will have different support
5092     * as it is actually Safari under the hood.
5093     *
5094     * @static
5095     * @const
5096     * @type {Boolean}
5097     */
5098  
5099    var IS_CHROME = !IS_EDGE && (/Chrome/i.test(USER_AGENT) || /CriOS/i.test(USER_AGENT));
5100    /**
5101     * The detected Google Chrome version - or `null`.
5102     *
5103     * @static
5104     * @const
5105     * @type {number|null}
5106     */
5107  
5108    var CHROME_VERSION = function () {
5109      var match = USER_AGENT.match(/(Chrome|CriOS)\/(\d+)/);
5110  
5111      if (match && match[2]) {
5112        return parseFloat(match[2]);
5113      }
5114  
5115      return null;
5116    }();
5117    /**
5118     * The detected Internet Explorer version - or `null`.
5119     *
5120     * @static
5121     * @const
5122     * @type {number|null}
5123     */
5124  
5125    var IE_VERSION = function () {
5126      var result = /MSIE\s(\d+)\.\d/.exec(USER_AGENT);
5127      var version = result && parseFloat(result[1]);
5128  
5129      if (!version && /Trident\/7.0/i.test(USER_AGENT) && /rv:11.0/.test(USER_AGENT)) {
5130        // IE 11 has a different user agent string than other IE versions
5131        version = 11.0;
5132      }
5133  
5134      return version;
5135    }();
5136    /**
5137     * Whether or not this is desktop Safari.
5138     *
5139     * @static
5140     * @const
5141     * @type {Boolean}
5142     */
5143  
5144    var IS_SAFARI = /Safari/i.test(USER_AGENT) && !IS_CHROME && !IS_ANDROID && !IS_EDGE;
5145    /**
5146     * Whether or not this is a Windows machine.
5147     *
5148     * @static
5149     * @const
5150     * @type {Boolean}
5151     */
5152  
5153    var IS_WINDOWS = /Windows/i.test(USER_AGENT);
5154    /**
5155     * Whether or not this device is touch-enabled.
5156     *
5157     * @static
5158     * @const
5159     * @type {Boolean}
5160     */
5161  
5162    var TOUCH_ENABLED = isReal() && ('ontouchstart' in window$3 || window$3.navigator.maxTouchPoints || window$3.DocumentTouch && window$3.document instanceof window$3.DocumentTouch);
5163    /**
5164     * Whether or not this device is an iPad.
5165     *
5166     * @static
5167     * @const
5168     * @type {Boolean}
5169     */
5170  
5171    var IS_IPAD = /iPad/i.test(USER_AGENT) || IS_SAFARI && TOUCH_ENABLED && !/iPhone/i.test(USER_AGENT);
5172    /**
5173     * Whether or not this device is an iPhone.
5174     *
5175     * @static
5176     * @const
5177     * @type {Boolean}
5178     */
5179    // The Facebook app's UIWebView identifies as both an iPhone and iPad, so
5180    // to identify iPhones, we need to exclude iPads.
5181    // http://artsy.github.io/blog/2012/10/18/the-perils-of-ios-user-agent-sniffing/
5182  
5183    var IS_IPHONE = /iPhone/i.test(USER_AGENT) && !IS_IPAD;
5184    /**
5185     * Whether or not this is an iOS device.
5186     *
5187     * @static
5188     * @const
5189     * @type {Boolean}
5190     */
5191  
5192    var IS_IOS = IS_IPHONE || IS_IPAD || IS_IPOD;
5193    /**
5194     * Whether or not this is any flavor of Safari - including iOS.
5195     *
5196     * @static
5197     * @const
5198     * @type {Boolean}
5199     */
5200  
5201    var IS_ANY_SAFARI = (IS_SAFARI || IS_IOS) && !IS_CHROME;
5202  
5203    var browser = /*#__PURE__*/Object.freeze({
5204      __proto__: null,
5205      IS_IPOD: IS_IPOD,
5206      IOS_VERSION: IOS_VERSION,
5207      IS_ANDROID: IS_ANDROID,
5208      ANDROID_VERSION: ANDROID_VERSION,
5209      IS_NATIVE_ANDROID: IS_NATIVE_ANDROID,
5210      IS_FIREFOX: IS_FIREFOX,
5211      IS_EDGE: IS_EDGE,
5212      IS_CHROME: IS_CHROME,
5213      CHROME_VERSION: CHROME_VERSION,
5214      IE_VERSION: IE_VERSION,
5215      IS_SAFARI: IS_SAFARI,
5216      IS_WINDOWS: IS_WINDOWS,
5217      TOUCH_ENABLED: TOUCH_ENABLED,
5218      IS_IPAD: IS_IPAD,
5219      IS_IPHONE: IS_IPHONE,
5220      IS_IOS: IS_IOS,
5221      IS_ANY_SAFARI: IS_ANY_SAFARI
5222    });
5223  
5224    /**
5225     * @file time-ranges.js
5226     * @module time-ranges
5227     */
5228  
5229    /**
5230     * Returns the time for the specified index at the start or end
5231     * of a TimeRange object.
5232     *
5233     * @typedef    {Function} TimeRangeIndex
5234     *
5235     * @param      {number} [index=0]
5236     *             The range number to return the time for.
5237     *
5238     * @return     {number}
5239     *             The time offset at the specified index.
5240     *
5241     * @deprecated The index argument must be provided.
5242     *             In the future, leaving it out will throw an error.
5243     */
5244  
5245    /**
5246     * An object that contains ranges of time.
5247     *
5248     * @typedef  {Object} TimeRange
5249     *
5250     * @property {number} length
5251     *           The number of time ranges represented by this object.
5252     *
5253     * @property {module:time-ranges~TimeRangeIndex} start
5254     *           Returns the time offset at which a specified time range begins.
5255     *
5256     * @property {module:time-ranges~TimeRangeIndex} end
5257     *           Returns the time offset at which a specified time range ends.
5258     *
5259     * @see https://developer.mozilla.org/en-US/docs/Web/API/TimeRanges
5260     */
5261  
5262    /**
5263     * Check if any of the time ranges are over the maximum index.
5264     *
5265     * @private
5266     * @param   {string} fnName
5267     *          The function name to use for logging
5268     *
5269     * @param   {number} index
5270     *          The index to check
5271     *
5272     * @param   {number} maxIndex
5273     *          The maximum possible index
5274     *
5275     * @throws  {Error} if the timeRanges provided are over the maxIndex
5276     */
5277    function rangeCheck(fnName, index, maxIndex) {
5278      if (typeof index !== 'number' || index < 0 || index > maxIndex) {
5279        throw new Error("Failed to execute '" + fnName + "' on 'TimeRanges': The index provided (" + index + ") is non-numeric or out of bounds (0-" + maxIndex + ").");
5280      }
5281    }
5282    /**
5283     * Get the time for the specified index at the start or end
5284     * of a TimeRange object.
5285     *
5286     * @private
5287     * @param      {string} fnName
5288     *             The function name to use for logging
5289     *
5290     * @param      {string} valueIndex
5291     *             The property that should be used to get the time. should be
5292     *             'start' or 'end'
5293     *
5294     * @param      {Array} ranges
5295     *             An array of time ranges
5296     *
5297     * @param      {Array} [rangeIndex=0]
5298     *             The index to start the search at
5299     *
5300     * @return     {number}
5301     *             The time that offset at the specified index.
5302     *
5303     * @deprecated rangeIndex must be set to a value, in the future this will throw an error.
5304     * @throws     {Error} if rangeIndex is more than the length of ranges
5305     */
5306  
5307  
5308    function getRange(fnName, valueIndex, ranges, rangeIndex) {
5309      rangeCheck(fnName, rangeIndex, ranges.length - 1);
5310      return ranges[rangeIndex][valueIndex];
5311    }
5312    /**
5313     * Create a time range object given ranges of time.
5314     *
5315     * @private
5316     * @param   {Array} [ranges]
5317     *          An array of time ranges.
5318     */
5319  
5320  
5321    function createTimeRangesObj(ranges) {
5322      if (ranges === undefined || ranges.length === 0) {
5323        return {
5324          length: 0,
5325          start: function start() {
5326            throw new Error('This TimeRanges object is empty');
5327          },
5328          end: function end() {
5329            throw new Error('This TimeRanges object is empty');
5330          }
5331        };
5332      }
5333  
5334      return {
5335        length: ranges.length,
5336        start: getRange.bind(null, 'start', 0, ranges),
5337        end: getRange.bind(null, 'end', 1, ranges)
5338      };
5339    }
5340    /**
5341     * Create a `TimeRange` object which mimics an
5342     * {@link https://developer.mozilla.org/en-US/docs/Web/API/TimeRanges|HTML5 TimeRanges instance}.
5343     *
5344     * @param {number|Array[]} start
5345     *        The start of a single range (a number) or an array of ranges (an
5346     *        array of arrays of two numbers each).
5347     *
5348     * @param {number} end
5349     *        The end of a single range. Cannot be used with the array form of
5350     *        the `start` argument.
5351     */
5352  
5353  
5354    function createTimeRanges(start, end) {
5355      if (Array.isArray(start)) {
5356        return createTimeRangesObj(start);
5357      } else if (start === undefined || end === undefined) {
5358        return createTimeRangesObj();
5359      }
5360  
5361      return createTimeRangesObj([[start, end]]);
5362    }
5363  
5364    /**
5365     * @file buffer.js
5366     * @module buffer
5367     */
5368    /**
5369     * Compute the percentage of the media that has been buffered.
5370     *
5371     * @param {TimeRange} buffered
5372     *        The current `TimeRange` object representing buffered time ranges
5373     *
5374     * @param {number} duration
5375     *        Total duration of the media
5376     *
5377     * @return {number}
5378     *         Percent buffered of the total duration in decimal form.
5379     */
5380  
5381    function bufferedPercent(buffered, duration) {
5382      var bufferedDuration = 0;
5383      var start;
5384      var end;
5385  
5386      if (!duration) {
5387        return 0;
5388      }
5389  
5390      if (!buffered || !buffered.length) {
5391        buffered = createTimeRanges(0, 0);
5392      }
5393  
5394      for (var i = 0; i < buffered.length; i++) {
5395        start = buffered.start(i);
5396        end = buffered.end(i); // buffered end can be bigger than duration by a very small fraction
5397  
5398        if (end > duration) {
5399          end = duration;
5400        }
5401  
5402        bufferedDuration += end - start;
5403      }
5404  
5405      return bufferedDuration / duration;
5406    }
5407  
5408    /**
5409     * @file fullscreen-api.js
5410     * @module fullscreen-api
5411     * @private
5412     */
5413    /**
5414     * Store the browser-specific methods for the fullscreen API.
5415     *
5416     * @type {Object}
5417     * @see [Specification]{@link https://fullscreen.spec.whatwg.org}
5418     * @see [Map Approach From Screenfull.js]{@link https://github.com/sindresorhus/screenfull.js}
5419     */
5420  
5421    var FullscreenApi = {
5422      prefixed: true
5423    }; // browser API methods
5424  
5425    var apiMap = [['requestFullscreen', 'exitFullscreen', 'fullscreenElement', 'fullscreenEnabled', 'fullscreenchange', 'fullscreenerror', 'fullscreen'], // WebKit
5426    ['webkitRequestFullscreen', 'webkitExitFullscreen', 'webkitFullscreenElement', 'webkitFullscreenEnabled', 'webkitfullscreenchange', 'webkitfullscreenerror', '-webkit-full-screen'], // Mozilla
5427    ['mozRequestFullScreen', 'mozCancelFullScreen', 'mozFullScreenElement', 'mozFullScreenEnabled', 'mozfullscreenchange', 'mozfullscreenerror', '-moz-full-screen'], // Microsoft
5428    ['msRequestFullscreen', 'msExitFullscreen', 'msFullscreenElement', 'msFullscreenEnabled', 'MSFullscreenChange', 'MSFullscreenError', '-ms-fullscreen']];
5429    var specApi = apiMap[0];
5430    var browserApi; // determine the supported set of functions
5431  
5432    for (var i = 0; i < apiMap.length; i++) {
5433      // check for exitFullscreen function
5434      if (apiMap[i][1] in document) {
5435        browserApi = apiMap[i];
5436        break;
5437      }
5438    } // map the browser API names to the spec API names
5439  
5440  
5441    if (browserApi) {
5442      for (var _i = 0; _i < browserApi.length; _i++) {
5443        FullscreenApi[specApi[_i]] = browserApi[_i];
5444      }
5445  
5446      FullscreenApi.prefixed = browserApi[0] !== specApi[0];
5447    }
5448  
5449    /**
5450     * @file media-error.js
5451     */
5452    /**
5453     * A Custom `MediaError` class which mimics the standard HTML5 `MediaError` class.
5454     *
5455     * @param {number|string|Object|MediaError} value
5456     *        This can be of multiple types:
5457     *        - number: should be a standard error code
5458     *        - string: an error message (the code will be 0)
5459     *        - Object: arbitrary properties
5460     *        - `MediaError` (native): used to populate a video.js `MediaError` object
5461     *        - `MediaError` (video.js): will return itself if it's already a
5462     *          video.js `MediaError` object.
5463     *
5464     * @see [MediaError Spec]{@link https://dev.w3.org/html5/spec-author-view/video.html#mediaerror}
5465     * @see [Encrypted MediaError Spec]{@link https://www.w3.org/TR/2013/WD-encrypted-media-20130510/#error-codes}
5466     *
5467     * @class MediaError
5468     */
5469  
5470    function MediaError(value) {
5471      // Allow redundant calls to this constructor to avoid having `instanceof`
5472      // checks peppered around the code.
5473      if (value instanceof MediaError) {
5474        return value;
5475      }
5476  
5477      if (typeof value === 'number') {
5478        this.code = value;
5479      } else if (typeof value === 'string') {
5480        // default code is zero, so this is a custom error
5481        this.message = value;
5482      } else if (isObject(value)) {
5483        // We assign the `code` property manually because native `MediaError` objects
5484        // do not expose it as an own/enumerable property of the object.
5485        if (typeof value.code === 'number') {
5486          this.code = value.code;
5487        }
5488  
5489        assign(this, value);
5490      }
5491  
5492      if (!this.message) {
5493        this.message = MediaError.defaultMessages[this.code] || '';
5494      }
5495    }
5496    /**
5497     * The error code that refers two one of the defined `MediaError` types
5498     *
5499     * @type {Number}
5500     */
5501  
5502  
5503    MediaError.prototype.code = 0;
5504    /**
5505     * An optional message that to show with the error. Message is not part of the HTML5
5506     * video spec but allows for more informative custom errors.
5507     *
5508     * @type {String}
5509     */
5510  
5511    MediaError.prototype.message = '';
5512    /**
5513     * An optional status code that can be set by plugins to allow even more detail about
5514     * the error. For example a plugin might provide a specific HTTP status code and an
5515     * error message for that code. Then when the plugin gets that error this class will
5516     * know how to display an error message for it. This allows a custom message to show
5517     * up on the `Player` error overlay.
5518     *
5519     * @type {Array}
5520     */
5521  
5522    MediaError.prototype.status = null;
5523    /**
5524     * Errors indexed by the W3C standard. The order **CANNOT CHANGE**! See the
5525     * specification listed under {@link MediaError} for more information.
5526     *
5527     * @enum {array}
5528     * @readonly
5529     * @property {string} 0 - MEDIA_ERR_CUSTOM
5530     * @property {string} 1 - MEDIA_ERR_ABORTED
5531     * @property {string} 2 - MEDIA_ERR_NETWORK
5532     * @property {string} 3 - MEDIA_ERR_DECODE
5533     * @property {string} 4 - MEDIA_ERR_SRC_NOT_SUPPORTED
5534     * @property {string} 5 - MEDIA_ERR_ENCRYPTED
5535     */
5536  
5537    MediaError.errorTypes = ['MEDIA_ERR_CUSTOM', 'MEDIA_ERR_ABORTED', 'MEDIA_ERR_NETWORK', 'MEDIA_ERR_DECODE', 'MEDIA_ERR_SRC_NOT_SUPPORTED', 'MEDIA_ERR_ENCRYPTED'];
5538    /**
5539     * The default `MediaError` messages based on the {@link MediaError.errorTypes}.
5540     *
5541     * @type {Array}
5542     * @constant
5543     */
5544  
5545    MediaError.defaultMessages = {
5546      1: 'You aborted the media playback',
5547      2: 'A network error caused the media download to fail part-way.',
5548      3: 'The media playback was aborted due to a corruption problem or because the media used features your browser did not support.',
5549      4: 'The media could not be loaded, either because the server or network failed or because the format is not supported.',
5550      5: 'The media is encrypted and we do not have the keys to decrypt it.'
5551    }; // Add types as properties on MediaError
5552    // e.g. MediaError.MEDIA_ERR_SRC_NOT_SUPPORTED = 4;
5553  
5554    for (var errNum = 0; errNum < MediaError.errorTypes.length; errNum++) {
5555      MediaError[MediaError.errorTypes[errNum]] = errNum; // values should be accessible on both the class and instance
5556  
5557      MediaError.prototype[MediaError.errorTypes[errNum]] = errNum;
5558    } // jsdocs for instance/static members added above
5559  
5560    var tuple = SafeParseTuple;
5561  
5562    function SafeParseTuple(obj, reviver) {
5563      var json;
5564      var error = null;
5565  
5566      try {
5567        json = JSON.parse(obj, reviver);
5568      } catch (err) {
5569        error = err;
5570      }
5571  
5572      return [error, json];
5573    }
5574  
5575    /**
5576     * Returns whether an object is `Promise`-like (i.e. has a `then` method).
5577     *
5578     * @param  {Object}  value
5579     *         An object that may or may not be `Promise`-like.
5580     *
5581     * @return {boolean}
5582     *         Whether or not the object is `Promise`-like.
5583     */
5584    function isPromise(value) {
5585      return value !== undefined && value !== null && typeof value.then === 'function';
5586    }
5587    /**
5588     * Silence a Promise-like object.
5589     *
5590     * This is useful for avoiding non-harmful, but potentially confusing "uncaught
5591     * play promise" rejection error messages.
5592     *
5593     * @param  {Object} value
5594     *         An object that may or may not be `Promise`-like.
5595     */
5596  
5597    function silencePromise(value) {
5598      if (isPromise(value)) {
5599        value.then(null, function (e) {});
5600      }
5601    }
5602  
5603    /**
5604     * @file text-track-list-converter.js Utilities for capturing text track state and
5605     * re-creating tracks based on a capture.
5606     *
5607     * @module text-track-list-converter
5608     */
5609  
5610    /**
5611     * Examine a single {@link TextTrack} and return a JSON-compatible javascript object that
5612     * represents the {@link TextTrack}'s state.
5613     *
5614     * @param {TextTrack} track
5615     *        The text track to query.
5616     *
5617     * @return {Object}
5618     *         A serializable javascript representation of the TextTrack.
5619     * @private
5620     */
5621    var trackToJson_ = function trackToJson_(track) {
5622      var ret = ['kind', 'label', 'language', 'id', 'inBandMetadataTrackDispatchType', 'mode', 'src'].reduce(function (acc, prop, i) {
5623        if (track[prop]) {
5624          acc[prop] = track[prop];
5625        }
5626  
5627        return acc;
5628      }, {
5629        cues: track.cues && Array.prototype.map.call(track.cues, function (cue) {
5630          return {
5631            startTime: cue.startTime,
5632            endTime: cue.endTime,
5633            text: cue.text,
5634            id: cue.id
5635          };
5636        })
5637      });
5638      return ret;
5639    };
5640    /**
5641     * Examine a {@link Tech} and return a JSON-compatible javascript array that represents the
5642     * state of all {@link TextTrack}s currently configured. The return array is compatible with
5643     * {@link text-track-list-converter:jsonToTextTracks}.
5644     *
5645     * @param {Tech} tech
5646     *        The tech object to query
5647     *
5648     * @return {Array}
5649     *         A serializable javascript representation of the {@link Tech}s
5650     *         {@link TextTrackList}.
5651     */
5652  
5653  
5654    var textTracksToJson = function textTracksToJson(tech) {
5655      var trackEls = tech.$$('track');
5656      var trackObjs = Array.prototype.map.call(trackEls, function (t) {
5657        return t.track;
5658      });
5659      var tracks = Array.prototype.map.call(trackEls, function (trackEl) {
5660        var json = trackToJson_(trackEl.track);
5661  
5662        if (trackEl.src) {
5663          json.src = trackEl.src;
5664        }
5665  
5666        return json;
5667      });
5668      return tracks.concat(Array.prototype.filter.call(tech.textTracks(), function (track) {
5669        return trackObjs.indexOf(track) === -1;
5670      }).map(trackToJson_));
5671    };
5672    /**
5673     * Create a set of remote {@link TextTrack}s on a {@link Tech} based on an array of javascript
5674     * object {@link TextTrack} representations.
5675     *
5676     * @param {Array} json
5677     *        An array of `TextTrack` representation objects, like those that would be
5678     *        produced by `textTracksToJson`.
5679     *
5680     * @param {Tech} tech
5681     *        The `Tech` to create the `TextTrack`s on.
5682     */
5683  
5684  
5685    var jsonToTextTracks = function jsonToTextTracks(json, tech) {
5686      json.forEach(function (track) {
5687        var addedTrack = tech.addRemoteTextTrack(track).track;
5688  
5689        if (!track.src && track.cues) {
5690          track.cues.forEach(function (cue) {
5691            return addedTrack.addCue(cue);
5692          });
5693        }
5694      });
5695      return tech.textTracks();
5696    };
5697  
5698    var textTrackConverter = {
5699      textTracksToJson: textTracksToJson,
5700      jsonToTextTracks: jsonToTextTracks,
5701      trackToJson_: trackToJson_
5702    };
5703  
5704    var keycode = createCommonjsModule(function (module, exports) {
5705      // Source: http://jsfiddle.net/vWx8V/
5706      // http://stackoverflow.com/questions/5603195/full-list-of-javascript-keycodes
5707  
5708      /**
5709       * Conenience method returns corresponding value for given keyName or keyCode.
5710       *
5711       * @param {Mixed} keyCode {Number} or keyName {String}
5712       * @return {Mixed}
5713       * @api public
5714       */
5715      function keyCode(searchInput) {
5716        // Keyboard Events
5717        if (searchInput && 'object' === typeof searchInput) {
5718          var hasKeyCode = searchInput.which || searchInput.keyCode || searchInput.charCode;
5719          if (hasKeyCode) searchInput = hasKeyCode;
5720        } // Numbers
5721  
5722  
5723        if ('number' === typeof searchInput) return names[searchInput]; // Everything else (cast to string)
5724  
5725        var search = String(searchInput); // check codes
5726  
5727        var foundNamedKey = codes[search.toLowerCase()];
5728        if (foundNamedKey) return foundNamedKey; // check aliases
5729  
5730        var foundNamedKey = aliases[search.toLowerCase()];
5731        if (foundNamedKey) return foundNamedKey; // weird character?
5732  
vendor: 2,236 bytes, lines 5733-5818
5733        if (search.length === 1) return search.charCodeAt(0);
5734        return undefined;
5735      }
5736      /**
5737       * Compares a keyboard event with a given keyCode or keyName.
5738       *
5739       * @param {Event} event Keyboard event that should be tested
5740       * @param {Mixed} keyCode {Number} or keyName {String}
5741       * @return {Boolean}
5742       * @api public
5743       */
5744  
5745  
5746      keyCode.isEventKey = function isEventKey(event, nameOrCode) {
5747        if (event && 'object' === typeof event) {
5748          var keyCode = event.which || event.keyCode || event.charCode;
5749  
5750          if (keyCode === null || keyCode === undefined) {
5751            return false;
5752          }
5753  
5754          if (typeof nameOrCode === 'string') {
5755            // check codes
5756            var foundNamedKey = codes[nameOrCode.toLowerCase()];
5757  
5758            if (foundNamedKey) {
5759              return foundNamedKey === keyCode;
5760            } // check aliases
5761  
5762  
5763            var foundNamedKey = aliases[nameOrCode.toLowerCase()];
5764  
5765            if (foundNamedKey) {
5766              return foundNamedKey === keyCode;
5767            }
5768          } else if (typeof nameOrCode === 'number') {
5769            return nameOrCode === keyCode;
5770          }
5771  
5772          return false;
5773        }
5774      };
5775  
5776      exports = module.exports = keyCode;
5777      /**
5778       * Get by name
5779       *
5780       *   exports.code['enter'] // => 13
5781       */
5782  
5783      var codes = exports.code = exports.codes = {
5784        'backspace': 8,
5785        'tab': 9,
5786        'enter': 13,
5787        'shift': 16,
5788        'ctrl': 17,
5789        'alt': 18,
5790        'pause/break': 19,
5791        'caps lock': 20,
5792        'esc': 27,
5793        'space': 32,
5794        'page up': 33,
5795        'page down': 34,
5796        'end': 35,
5797        'home': 36,
5798        'left': 37,
5799        'up': 38,
5800        'right': 39,
5801        'down': 40,
5802        'insert': 45,
5803        'delete': 46,
5804        'command': 91,
5805        'left command': 91,
5806        'right command': 93,
5807        'numpad *': 106,
5808        'numpad +': 107,
5809        'numpad -': 109,
5810        'numpad .': 110,
5811        'numpad /': 111,
5812        'num lock': 144,
5813        'scroll lock': 145,
5814        'my computer': 182,
5815        'my calculator': 183,
5816        ';': 186,
5817        '=': 187,
5818        ',': 188,
5819        '-': 189,
5820        '.': 190,
5821        '/': 191,
5822        '`': 192,
5823        '[': 219,
5824        '\\': 220,
5825        ']': 221,
5826        "'": 222
5827      }; // Helper aliases
5828  
5829      var aliases = exports.aliases = {
5830        'windows': 91,
5831        '⇧': 16,
5832        '⌥': 18,
5833        '⌃': 17,
5834        '⌘': 91,
5835        'ctl': 17,
5836        'control': 17,
5837        'option': 18,
5838        'pause': 19,
5839        'break': 19,
5840        'caps': 20,
5841        'return': 13,
5842        'escape': 27,
5843        'spc': 32,
5844        'spacebar': 32,
5845        'pgup': 33,
5846        'pgdn': 34,
5847        'ins': 45,
5848        'del': 46,
5849        'cmd': 91
5850      };
5851      /*!
5852       * Programatically add the following
5853       */
5854      // lower case chars
5855  
5856      for (i = 97; i < 123; i++) {
5857        codes[String.fromCharCode(i)] = i - 32;
5858      } // numbers
5859  
5860  
5861      for (var i = 48; i < 58; i++) {
5862        codes[i - 48] = i;
5863      } // function keys
5864  
5865  
5866      for (i = 1; i < 13; i++) {
5867        codes['f' + i] = i + 111;
5868      } // numpad keys
5869  
5870  
5871      for (i = 0; i < 10; i++) {
5872        codes['numpad ' + i] = i + 96;
5873      }
5874      /**
5875       * Get by code
5876       *
5877       *   exports.name[13] // => 'Enter'
5878       */
5879  
5880  
5881      var names = exports.names = exports.title = {}; // title for backward compat
5882      // Create reverse mapping
5883  
5884      for (i in codes) {
5885        names[codes[i]] = i;
5886      } // Add aliases
5887  
5888  
5889      for (var alias in aliases) {
5890        codes[alias] = aliases[alias];
5891      }
5892    });
5893    var keycode_1 = keycode.code;
5894    var keycode_2 = keycode.codes;
5895    var keycode_3 = keycode.aliases;
5896    var keycode_4 = keycode.names;
5897    var keycode_5 = keycode.title;
5898  
5899    var MODAL_CLASS_NAME = 'vjs-modal-dialog';
5900    /**
5901     * The `ModalDialog` displays over the video and its controls, which blocks
5902     * interaction with the player until it is closed.
5903     *
5904     * Modal dialogs include a "Close" button and will close when that button
5905     * is activated - or when ESC is pressed anywhere.
5906     *
5907     * @extends Component
5908     */
5909  
5910    var ModalDialog = /*#__PURE__*/function (_Component) {
5911      inheritsLoose(ModalDialog, _Component);
5912  
5913      /**
5914       * Create an instance of this class.
5915       *
5916       * @param {Player} player
5917       *        The `Player` that this class should be attached to.
5918       *
5919       * @param {Object} [options]
5920       *        The key/value store of player options.
5921       *
5922       * @param {Mixed} [options.content=undefined]
5923       *        Provide customized content for this modal.
5924       *
5925       * @param {string} [options.description]
5926       *        A text description for the modal, primarily for accessibility.
5927       *
5928       * @param {boolean} [options.fillAlways=false]
5929       *        Normally, modals are automatically filled only the first time
5930       *        they open. This tells the modal to refresh its content
5931       *        every time it opens.
5932       *
5933       * @param {string} [options.label]
5934       *        A text label for the modal, primarily for accessibility.
5935       *
5936       * @param {boolean} [options.pauseOnOpen=true]
5937       *        If `true`, playback will will be paused if playing when
5938       *        the modal opens, and resumed when it closes.
5939       *
5940       * @param {boolean} [options.temporary=true]
5941       *        If `true`, the modal can only be opened once; it will be
5942       *        disposed as soon as it's closed.
5943       *
5944       * @param {boolean} [options.uncloseable=false]
5945       *        If `true`, the user will not be able to close the modal
5946       *        through the UI in the normal ways. Programmatic closing is
5947       *        still possible.
5948       */
5949      function ModalDialog(player, options) {
5950        var _this;
5951  
5952        _this = _Component.call(this, player, options) || this;
5953        _this.opened_ = _this.hasBeenOpened_ = _this.hasBeenFilled_ = false;
5954  
5955        _this.closeable(!_this.options_.uncloseable);
5956  
5957        _this.content(_this.options_.content); // Make sure the contentEl is defined AFTER any children are initialized
5958        // because we only want the contents of the modal in the contentEl
5959        // (not the UI elements like the close button).
5960  
5961  
5962        _this.contentEl_ = createEl('div', {
5963          className: MODAL_CLASS_NAME + "-content"
5964        }, {
5965          role: 'document'
5966        });
5967        _this.descEl_ = createEl('p', {
5968          className: MODAL_CLASS_NAME + "-description vjs-control-text",
5969          id: _this.el().getAttribute('aria-describedby')
5970        });
5971        textContent(_this.descEl_, _this.description());
5972  
5973        _this.el_.appendChild(_this.descEl_);
5974  
5975        _this.el_.appendChild(_this.contentEl_);
5976  
5977        return _this;
5978      }
5979      /**
5980       * Create the `ModalDialog`'s DOM element
5981       *
5982       * @return {Element}
5983       *         The DOM element that gets created.
5984       */
5985  
5986  
5987      var _proto = ModalDialog.prototype;
5988  
5989      _proto.createEl = function createEl() {
5990        return _Component.prototype.createEl.call(this, 'div', {
5991          className: this.buildCSSClass(),
5992          tabIndex: -1
5993        }, {
5994          'aria-describedby': this.id() + "_description",
5995          'aria-hidden': 'true',
5996          'aria-label': this.label(),
5997          'role': 'dialog'
5998        });
5999      };
6000  
6001      _proto.dispose = function dispose() {
6002        this.contentEl_ = null;
6003        this.descEl_ = null;
6004        this.previouslyActiveEl_ = null;
6005  
6006        _Component.prototype.dispose.call(this);
6007      }
6008      /**
6009       * Builds the default DOM `className`.
6010       *
6011       * @return {string}
6012       *         The DOM `className` for this object.
6013       */
6014      ;
6015  
6016      _proto.buildCSSClass = function buildCSSClass() {
6017        return MODAL_CLASS_NAME + " vjs-hidden " + _Component.prototype.buildCSSClass.call(this);
6018      }
6019      /**
vendor: 10,167 bytes, lines 6020-6355
6020       * Returns the label string for this modal. Primarily used for accessibility.
6021       *
6022       * @return {string}
6023       *         the localized or raw label of this modal.
6024       */
6025      ;
6026  
6027      _proto.label = function label() {
6028        return this.localize(this.options_.label || 'Modal Window');
6029      }
6030      /**
6031       * Returns the description string for this modal. Primarily used for
6032       * accessibility.
6033       *
6034       * @return {string}
6035       *         The localized or raw description of this modal.
6036       */
6037      ;
6038  
6039      _proto.description = function description() {
6040        var desc = this.options_.description || this.localize('This is a modal window.'); // Append a universal closeability message if the modal is closeable.
6041  
6042        if (this.closeable()) {
6043          desc += ' ' + this.localize('This modal can be closed by pressing the Escape key or activating the close button.');
6044        }
6045  
6046        return desc;
6047      }
6048      /**
6049       * Opens the modal.
6050       *
6051       * @fires ModalDialog#beforemodalopen
6052       * @fires ModalDialog#modalopen
6053       */
6054      ;
6055  
6056      _proto.open = function open() {
6057        if (!this.opened_) {
6058          var player = this.player();
6059          /**
6060            * Fired just before a `ModalDialog` is opened.
6061            *
6062            * @event ModalDialog#beforemodalopen
6063            * @type {EventTarget~Event}
6064            */
6065  
6066          this.trigger('beforemodalopen');
6067          this.opened_ = true; // Fill content if the modal has never opened before and
6068          // never been filled.
6069  
6070          if (this.options_.fillAlways || !this.hasBeenOpened_ && !this.hasBeenFilled_) {
6071            this.fill();
6072          } // If the player was playing, pause it and take note of its previously
6073          // playing state.
6074  
6075  
6076          this.wasPlaying_ = !player.paused();
6077  
6078          if (this.options_.pauseOnOpen && this.wasPlaying_) {
6079            player.pause();
6080          }
6081  
6082          this.on('keydown', this.handleKeyDown); // Hide controls and note if they were enabled.
6083  
6084          this.hadControls_ = player.controls();
6085          player.controls(false);
6086          this.show();
6087          this.conditionalFocus_();
6088          this.el().setAttribute('aria-hidden', 'false');
6089          /**
6090            * Fired just after a `ModalDialog` is opened.
6091            *
6092            * @event ModalDialog#modalopen
6093            * @type {EventTarget~Event}
6094            */
6095  
6096          this.trigger('modalopen');
6097          this.hasBeenOpened_ = true;
6098        }
6099      }
6100      /**
6101       * If the `ModalDialog` is currently open or closed.
6102       *
6103       * @param  {boolean} [value]
6104       *         If given, it will open (`true`) or close (`false`) the modal.
6105       *
6106       * @return {boolean}
6107       *         the current open state of the modaldialog
6108       */
6109      ;
6110  
6111      _proto.opened = function opened(value) {
6112        if (typeof value === 'boolean') {
6113          this[value ? 'open' : 'close']();
6114        }
6115  
6116        return this.opened_;
6117      }
6118      /**
6119       * Closes the modal, does nothing if the `ModalDialog` is
6120       * not open.
6121       *
6122       * @fires ModalDialog#beforemodalclose
6123       * @fires ModalDialog#modalclose
6124       */
6125      ;
6126  
6127      _proto.close = function close() {
6128        if (!this.opened_) {
6129          return;
6130        }
6131  
6132        var player = this.player();
6133        /**
6134          * Fired just before a `ModalDialog` is closed.
6135          *
6136          * @event ModalDialog#beforemodalclose
6137          * @type {EventTarget~Event}
6138          */
6139  
6140        this.trigger('beforemodalclose');
6141        this.opened_ = false;
6142  
6143        if (this.wasPlaying_ && this.options_.pauseOnOpen) {
6144          player.play();
6145        }
6146  
6147        this.off('keydown', this.handleKeyDown);
6148  
6149        if (this.hadControls_) {
6150          player.controls(true);
6151        }
6152  
6153        this.hide();
6154        this.el().setAttribute('aria-hidden', 'true');
6155        /**
6156          * Fired just after a `ModalDialog` is closed.
6157          *
6158          * @event ModalDialog#modalclose
6159          * @type {EventTarget~Event}
6160          */
6161  
6162        this.trigger('modalclose');
6163        this.conditionalBlur_();
6164  
6165        if (this.options_.temporary) {
6166          this.dispose();
6167        }
6168      }
6169      /**
6170       * Check to see if the `ModalDialog` is closeable via the UI.
6171       *
6172       * @param  {boolean} [value]
6173       *         If given as a boolean, it will set the `closeable` option.
6174       *
6175       * @return {boolean}
6176       *         Returns the final value of the closable option.
6177       */
6178      ;
6179  
6180      _proto.closeable = function closeable(value) {
6181        if (typeof value === 'boolean') {
6182          var closeable = this.closeable_ = !!value;
6183          var close = this.getChild('closeButton'); // If this is being made closeable and has no close button, add one.
6184  
6185          if (closeable && !close) {
6186            // The close button should be a child of the modal - not its
6187            // content element, so temporarily change the content element.
6188            var temp = this.contentEl_;
6189            this.contentEl_ = this.el_;
6190            close = this.addChild('closeButton', {
6191              controlText: 'Close Modal Dialog'
6192            });
6193            this.contentEl_ = temp;
6194            this.on(close, 'close', this.close);
6195          } // If this is being made uncloseable and has a close button, remove it.
6196  
6197  
6198          if (!closeable && close) {
6199            this.off(close, 'close', this.close);
6200            this.removeChild(close);
6201            close.dispose();
6202          }
6203        }
6204  
6205        return this.closeable_;
6206      }
6207      /**
6208       * Fill the modal's content element with the modal's "content" option.
6209       * The content element will be emptied before this change takes place.
6210       */
6211      ;
6212  
6213      _proto.fill = function fill() {
6214        this.fillWith(this.content());
6215      }
6216      /**
6217       * Fill the modal's content element with arbitrary content.
6218       * The content element will be emptied before this change takes place.
6219       *
6220       * @fires ModalDialog#beforemodalfill
6221       * @fires ModalDialog#modalfill
6222       *
6223       * @param {Mixed} [content]
6224       *        The same rules apply to this as apply to the `content` option.
6225       */
6226      ;
6227  
6228      _proto.fillWith = function fillWith(content) {
6229        var contentEl = this.contentEl();
6230        var parentEl = contentEl.parentNode;
6231        var nextSiblingEl = contentEl.nextSibling;
6232        /**
6233          * Fired just before a `ModalDialog` is filled with content.
6234          *
6235          * @event ModalDialog#beforemodalfill
6236          * @type {EventTarget~Event}
6237          */
6238  
6239        this.trigger('beforemodalfill');
6240        this.hasBeenFilled_ = true; // Detach the content element from the DOM before performing
6241        // manipulation to avoid modifying the live DOM multiple times.
6242  
6243        parentEl.removeChild(contentEl);
6244        this.empty();
6245        insertContent(contentEl, content);
6246        /**
6247         * Fired just after a `ModalDialog` is filled with content.
6248         *
6249         * @event ModalDialog#modalfill
6250         * @type {EventTarget~Event}
6251         */
6252  
6253        this.trigger('modalfill'); // Re-inject the re-filled content element.
6254  
6255        if (nextSiblingEl) {
6256          parentEl.insertBefore(contentEl, nextSiblingEl);
6257        } else {
6258          parentEl.appendChild(contentEl);
6259        } // make sure that the close button is last in the dialog DOM
6260  
6261  
6262        var closeButton = this.getChild('closeButton');
6263  
6264        if (closeButton) {
6265          parentEl.appendChild(closeButton.el_);
6266        }
6267      }
6268      /**
6269       * Empties the content element. This happens anytime the modal is filled.
6270       *
6271       * @fires ModalDialog#beforemodalempty
6272       * @fires ModalDialog#modalempty
6273       */
6274      ;
6275  
6276      _proto.empty = function empty() {
6277        /**
6278        * Fired just before a `ModalDialog` is emptied.
6279        *
6280        * @event ModalDialog#beforemodalempty
6281        * @type {EventTarget~Event}
6282        */
6283        this.trigger('beforemodalempty');
6284        emptyEl(this.contentEl());
6285        /**
6286        * Fired just after a `ModalDialog` is emptied.
6287        *
6288        * @event ModalDialog#modalempty
6289        * @type {EventTarget~Event}
6290        */
6291  
6292        this.trigger('modalempty');
6293      }
6294      /**
6295       * Gets or sets the modal content, which gets normalized before being
6296       * rendered into the DOM.
6297       *
6298       * This does not update the DOM or fill the modal, but it is called during
6299       * that process.
6300       *
6301       * @param  {Mixed} [value]
6302       *         If defined, sets the internal content value to be used on the
6303       *         next call(s) to `fill`. This value is normalized before being
6304       *         inserted. To "clear" the internal content value, pass `null`.
6305       *
6306       * @return {Mixed}
6307       *         The current content of the modal dialog
6308       */
6309      ;
6310  
6311      _proto.content = function content(value) {
6312        if (typeof value !== 'undefined') {
6313          this.content_ = value;
6314        }
6315  
6316        return this.content_;
6317      }
6318      /**
6319       * conditionally focus the modal dialog if focus was previously on the player.
6320       *
6321       * @private
6322       */
6323      ;
6324  
6325      _proto.conditionalFocus_ = function conditionalFocus_() {
6326        var activeEl = document.activeElement;
6327        var playerEl = this.player_.el_;
6328        this.previouslyActiveEl_ = null;
6329  
6330        if (playerEl.contains(activeEl) || playerEl === activeEl) {
6331          this.previouslyActiveEl_ = activeEl;
6332          this.focus();
6333        }
6334      }
6335      /**
6336       * conditionally blur the element and refocus the last focused element
6337       *
6338       * @private
6339       */
6340      ;
6341  
6342      _proto.conditionalBlur_ = function conditionalBlur_() {
6343        if (this.previouslyActiveEl_) {
6344          this.previouslyActiveEl_.focus();
6345          this.previouslyActiveEl_ = null;
6346        }
6347      }
6348      /**
6349       * Keydown handler. Attached when modal is focused.
6350       *
6351       * @listens keydown
6352       */
6353      ;
6354  
6355      _proto.handleKeyDown = function handleKeyDown(event) {
6356        // Do not allow keydowns to reach out of the modal dialog.
6357        event.stopPropagation();
6358  
6359        if (keycode.isEventKey(event, 'Escape') && this.closeable()) {
6360          event.preventDefault();
6361          this.close();
6362          return;
6363        } // exit early if it isn't a tab key
6364  
6365  
6366        if (!keycode.isEventKey(event, 'Tab')) {
6367          return;
6368        }
6369  
6370        var focusableEls = this.focusableEls_();
6371        var activeEl = this.el_.querySelector(':focus');
6372        var focusIndex;
6373  
6374        for (var i = 0; i < focusableEls.length; i++) {
6375          if (activeEl === focusableEls[i]) {
6376            focusIndex = i;
6377            break;
6378          }
6379        }
6380  
6381        if (document.activeElement === this.el_) {
6382          focusIndex = 0;
6383        }
6384  
6385        if (event.shiftKey && focusIndex === 0) {
6386          focusableEls[focusableEls.length - 1].focus();
6387          event.preventDefault();
6388        } else if (!event.shiftKey && focusIndex === focusableEls.length - 1) {
6389          focusableEls[0].focus();
6390          event.preventDefault();
6391        }
6392      }
6393      /**
6394       * get all focusable elements
6395       *
6396       * @private
6397       */
6398      ;
6399  
6400      _proto.focusableEls_ = function focusableEls_() {
6401        var allChildren = this.el_.querySelectorAll('*');
6402        return Array.prototype.filter.call(allChildren, function (child) {
6403          return (child instanceof window$3.HTMLAnchorElement || child instanceof window$3.HTMLAreaElement) && child.hasAttribute('href') || (child instanceof window$3.HTMLInputElement || child instanceof window$3.HTMLSelectElement || child instanceof window$3.HTMLTextAreaElement || child instanceof window$3.HTMLButtonElement) && !child.hasAttribute('disabled') || child instanceof window$3.HTMLIFrameElement || child instanceof window$3.HTMLObjectElement || child instanceof window$3.HTMLEmbedElement || child.hasAttribute('tabindex') && child.getAttribute('tabindex') !== -1 || child.hasAttribute('contenteditable');
6404        });
6405      };
6406  
6407      return ModalDialog;
6408    }(Component);
6409    /**
6410     * Default options for `ModalDialog` default options.
6411     *
6412     * @type {Object}
6413     * @private
6414     */
6415  
6416  
6417    ModalDialog.prototype.options_ = {
6418      pauseOnOpen: true,
6419      temporary: true
6420    };
6421    Component.registerComponent('ModalDialog', ModalDialog);
6422  
6423    /**
6424     * Common functionaliy between {@link TextTrackList}, {@link AudioTrackList}, and
6425     * {@link VideoTrackList}
6426     *
6427     * @extends EventTarget
6428     */
6429  
6430    var TrackList = /*#__PURE__*/function (_EventTarget) {
6431      inheritsLoose(TrackList, _EventTarget);
6432  
6433      /**
6434       * Create an instance of this class
6435       *
6436       * @param {Track[]} tracks
6437       *        A list of tracks to initialize the list with.
6438       *
6439       * @abstract
6440       */
6441      function TrackList(tracks) {
6442        var _this;
6443  
6444        if (tracks === void 0) {
6445          tracks = [];
6446        }
6447  
6448        _this = _EventTarget.call(this) || this;
6449        _this.tracks_ = [];
6450        /**
6451         * @memberof TrackList
6452         * @member {number} length
6453         *         The current number of `Track`s in the this Trackist.
6454         * @instance
6455         */
6456  
6457        Object.defineProperty(assertThisInitialized(_this), 'length', {
6458          get: function get() {
6459            return this.tracks_.length;
6460          }
6461        });
6462  
6463        for (var i = 0; i < tracks.length; i++) {
6464          _this.addTrack(tracks[i]);
6465        }
6466  
6467        return _this;
6468      }
6469      /**
6470       * Add a {@link Track} to the `TrackList`
6471       *
6472       * @param {Track} track
6473       *        The audio, video, or text track to add to the list.
6474       *
6475       * @fires TrackList#addtrack
6476       */
6477  
6478  
6479      var _proto = TrackList.prototype;
6480  
6481      _proto.addTrack = function addTrack(track) {
6482        var index = this.tracks_.length;
6483  
6484        if (!('' + index in this)) {
6485          Object.defineProperty(this, index, {
6486            get: function get() {
6487              return this.tracks_[index];
6488            }
6489          });
6490        } // Do not add duplicate tracks
6491  
6492  
6493        if (this.tracks_.indexOf(track) === -1) {
6494          this.tracks_.push(track);
6495          /**
6496           * Triggered when a track is added to a track list.
6497           *
6498           * @event TrackList#addtrack
6499           * @type {EventTarget~Event}
6500           * @property {Track} track
6501           *           A reference to track that was added.
6502           */
6503  
6504          this.trigger({
6505            track: track,
6506            type: 'addtrack',
6507            target: this
6508          });
6509        }
6510      }
6511      /**
6512       * Remove a {@link Track} from the `TrackList`
6513       *
6514       * @param {Track} rtrack
6515       *        The audio, video, or text track to remove from the list.
6516       *
6517       * @fires TrackList#removetrack
6518       */
6519      ;
6520  
6521      _proto.removeTrack = function removeTrack(rtrack) {
6522        var track;
6523  
6524        for (var i = 0, l = this.length; i < l; i++) {
6525          if (this[i] === rtrack) {
6526            track = this[i];
6527  
6528            if (track.off) {
6529              track.off();
6530            }
6531  
6532            this.tracks_.splice(i, 1);
6533            break;
6534          }
6535        }
6536  
6537        if (!track) {
6538          return;
6539        }
6540        /**
6541         * Triggered when a track is removed from track list.
6542         *
6543         * @event TrackList#removetrack
6544         * @type {EventTarget~Event}
6545         * @property {Track} track
6546         *           A reference to track that was removed.
6547         */
6548  
6549  
6550        this.trigger({
6551          track: track,
6552          type: 'removetrack',
6553          target: this
6554        });
6555      }
6556      /**
6557       * Get a Track from the TrackList by a tracks id
6558       *
6559       * @param {string} id - the id of the track to get
6560       * @method getTrackById
6561       * @return {Track}
6562       * @private
6563       */
6564      ;
6565  
6566      _proto.getTrackById = function getTrackById(id) {
6567        var result = null;
6568  
6569        for (var i = 0, l = this.length; i < l; i++) {
6570          var track = this[i];
6571  
6572          if (track.id === id) {
6573            result = track;
6574            break;
6575          }
6576        }
6577  
6578        return result;
6579      };
6580  
6581      return TrackList;
6582    }(EventTarget);
6583    /**
6584     * Triggered when a different track is selected/enabled.
6585     *
6586     * @event TrackList#change
6587     * @type {EventTarget~Event}
6588     */
6589  
6590    /**
6591     * Events that can be called with on + eventName. See {@link EventHandler}.
6592     *
6593     * @property {Object} TrackList#allowedEvents_
6594     * @private
6595     */
6596  
6597  
6598    TrackList.prototype.allowedEvents_ = {
6599      change: 'change',
6600      addtrack: 'addtrack',
6601      removetrack: 'removetrack'
6602    }; // emulate attribute EventHandler support to allow for feature detection
6603  
6604    for (var event in TrackList.prototype.allowedEvents_) {
6605      TrackList.prototype['on' + event] = null;
6606    }
6607  
6608    /**
6609     * Anywhere we call this function we diverge from the spec
6610     * as we only support one enabled audiotrack at a time
6611     *
6612     * @param {AudioTrackList} list
6613     *        list to work on
6614     *
6615     * @param {AudioTrack} track
6616     *        The track to skip
6617     *
6618     * @private
6619     */
6620  
6621    var disableOthers = function disableOthers(list, track) {
6622      for (var i = 0; i < list.length; i++) {
6623        if (!Object.keys(list[i]).length || track.id === list[i].id) {
6624          continue;
6625        } // another audio track is enabled, disable it
6626  
6627  
6628        list[i].enabled = false;
6629      }
6630    };
6631    /**
6632     * The current list of {@link AudioTrack} for a media file.
6633     *
6634     * @see [Spec]{@link https://html.spec.whatwg.org/multipage/embedded-content.html#audiotracklist}
6635     * @extends TrackList
6636     */
6637  
6638  
6639    var AudioTrackList = /*#__PURE__*/function (_TrackList) {
6640      inheritsLoose(AudioTrackList, _TrackList);
6641  
6642      /**
6643       * Create an instance of this class.
6644       *
6645       * @param {AudioTrack[]} [tracks=[]]
6646       *        A list of `AudioTrack` to instantiate the list with.
6647       */
6648      function AudioTrackList(tracks) {
6649        var _this;
6650  
6651        if (tracks === void 0) {
6652          tracks = [];
6653        }
6654  
6655        // make sure only 1 track is enabled
6656        // sorted from last index to first index
6657        for (var i = tracks.length - 1; i >= 0; i--) {
6658          if (tracks[i].enabled) {
6659            disableOthers(tracks, tracks[i]);
6660            break;
6661          }
6662        }
6663  
6664        _this = _TrackList.call(this, tracks) || this;
6665        _this.changing_ = false;
6666        return _this;
6667      }
6668      /**
6669       * Add an {@link AudioTrack} to the `AudioTrackList`.
6670       *
6671       * @param {AudioTrack} track
6672       *        The AudioTrack to add to the list
6673       *
6674       * @fires TrackList#addtrack
6675       */
6676  
6677  
6678      var _proto = AudioTrackList.prototype;
6679  
6680      _proto.addTrack = function addTrack(track) {
6681        var _this2 = this;
6682  
6683        if (track.enabled) {
6684          disableOthers(this, track);
6685        }
6686  
6687        _TrackList.prototype.addTrack.call(this, track); // native tracks don't have this
6688  
6689  
6690        if (!track.addEventListener) {
6691          return;
6692        }
6693  
6694        track.enabledChange_ = function () {
6695          // when we are disabling other tracks (since we don't support
6696          // more than one track at a time) we will set changing_
6697          // to true so that we don't trigger additional change events
6698          if (_this2.changing_) {
6699            return;
6700          }
6701  
6702          _this2.changing_ = true;
6703          disableOthers(_this2, track);
6704          _this2.changing_ = false;
6705  
6706          _this2.trigger('change');
6707        };
6708        /**
6709         * @listens AudioTrack#enabledchange
6710         * @fires TrackList#change
6711         */
6712  
6713  
6714        track.addEventListener('enabledchange', track.enabledChange_);
6715      };
6716  
6717      _proto.removeTrack = function removeTrack(rtrack) {
6718        _TrackList.prototype.removeTrack.call(this, rtrack);
6719  
6720        if (rtrack.removeEventListener && rtrack.enabledChange_) {
6721          rtrack.removeEventListener('enabledchange', rtrack.enabledChange_);
6722          rtrack.enabledChange_ = null;
6723        }
6724      };
6725  
6726      return AudioTrackList;
6727    }(TrackList);
6728  
6729    /**
6730     * Un-select all other {@link VideoTrack}s that are selected.
6731     *
6732     * @param {VideoTrackList} list
6733     *        list to work on
6734     *
6735     * @param {VideoTrack} track
6736     *        The track to skip
6737     *
6738     * @private
6739     */
6740  
6741    var disableOthers$1 = function disableOthers(list, track) {
6742      for (var i = 0; i < list.length; i++) {
6743        if (!Object.keys(list[i]).length || track.id === list[i].id) {
6744          continue;
6745        } // another video track is enabled, disable it
6746  
6747  
6748        list[i].selected = false;
6749      }
6750    };
6751    /**
6752     * The current list of {@link VideoTrack} for a video.
6753     *
6754     * @see [Spec]{@link https://html.spec.whatwg.org/multipage/embedded-content.html#videotracklist}
6755     * @extends TrackList
6756     */
6757  
6758  
6759    var VideoTrackList = /*#__PURE__*/function (_TrackList) {
6760      inheritsLoose(VideoTrackList, _TrackList);
6761  
6762      /**
6763       * Create an instance of this class.
6764       *
6765       * @param {VideoTrack[]} [tracks=[]]
6766       *        A list of `VideoTrack` to instantiate the list with.
6767       */
6768      function VideoTrackList(tracks) {
6769        var _this;
6770  
6771        if (tracks === void 0) {
6772          tracks = [];
6773        }
6774  
6775        // make sure only 1 track is enabled
6776        // sorted from last index to first index
6777        for (var i = tracks.length - 1; i >= 0; i--) {
6778          if (tracks[i].selected) {
6779            disableOthers$1(tracks, tracks[i]);
6780            break;
6781          }
6782        }
6783  
6784        _this = _TrackList.call(this, tracks) || this;
6785        _this.changing_ = false;
6786        /**
6787         * @member {number} VideoTrackList#selectedIndex
6788         *         The current index of the selected {@link VideoTrack`}.
6789         */
6790  
6791        Object.defineProperty(assertThisInitialized(_this), 'selectedIndex', {
6792          get: function get() {
6793            for (var _i = 0; _i < this.length; _i++) {
6794              if (this[_i].selected) {
6795                return _i;
6796              }
6797            }
6798  
6799            return -1;
6800          },
6801          set: function set() {}
6802        });
6803        return _this;
6804      }
6805      /**
6806       * Add a {@link VideoTrack} to the `VideoTrackList`.
6807       *
6808       * @param {VideoTrack} track
6809       *        The VideoTrack to add to the list
6810       *
6811       * @fires TrackList#addtrack
6812       */
6813  
6814  
6815      var _proto = VideoTrackList.prototype;
6816  
6817      _proto.addTrack = function addTrack(track) {
6818        var _this2 = this;
6819  
6820        if (track.selected) {
6821          disableOthers$1(this, track);
6822        }
6823  
6824        _TrackList.prototype.addTrack.call(this, track); // native tracks don't have this
6825  
6826  
6827        if (!track.addEventListener) {
6828          return;
6829        }
6830  
6831        track.selectedChange_ = function () {
6832          if (_this2.changing_) {
6833            return;
6834          }
6835  
6836          _this2.changing_ = true;
6837          disableOthers$1(_this2, track);
6838          _this2.changing_ = false;
6839  
6840          _this2.trigger('change');
6841        };
6842        /**
6843         * @listens VideoTrack#selectedchange
6844         * @fires TrackList#change
6845         */
6846  
6847  
6848        track.addEventListener('selectedchange', track.selectedChange_);
6849      };
6850  
6851      _proto.removeTrack = function removeTrack(rtrack) {
6852        _TrackList.prototype.removeTrack.call(this, rtrack);
6853  
6854        if (rtrack.removeEventListener && rtrack.selectedChange_) {
6855          rtrack.removeEventListener('selectedchange', rtrack.selectedChange_);
6856          rtrack.selectedChange_ = null;
6857        }
6858      };
6859  
6860      return VideoTrackList;
6861    }(TrackList);
6862  
6863    /**
6864     * The current list of {@link TextTrack} for a media file.
6865     *
6866     * @see [Spec]{@link https://html.spec.whatwg.org/multipage/embedded-content.html#texttracklist}
6867     * @extends TrackList
6868     */
6869  
6870    var TextTrackList = /*#__PURE__*/function (_TrackList) {
6871      inheritsLoose(TextTrackList, _TrackList);
6872  
6873      function TextTrackList() {
6874        return _TrackList.apply(this, arguments) || this;
6875      }
6876  
6877      var _proto = TextTrackList.prototype;
6878  
6879      /**
6880       * Add a {@link TextTrack} to the `TextTrackList`
6881       *
6882       * @param {TextTrack} track
6883       *        The text track to add to the list.
6884       *
6885       * @fires TrackList#addtrack
6886       */
6887      _proto.addTrack = function addTrack(track) {
6888        var _this = this;
6889  
6890        _TrackList.prototype.addTrack.call(this, track);
6891  
6892        if (!this.queueChange_) {
6893          this.queueChange_ = function () {
6894            return _this.queueTrigger('change');
6895          };
6896        }
6897  
6898        if (!this.triggerSelectedlanguagechange) {
6899          this.triggerSelectedlanguagechange_ = function () {
6900            return _this.trigger('selectedlanguagechange');
6901          };
6902        }
6903        /**
6904         * @listens TextTrack#modechange
6905         * @fires TrackList#change
6906         */
6907  
6908  
6909        track.addEventListener('modechange', this.queueChange_);
6910        var nonLanguageTextTrackKind = ['metadata', 'chapters'];
6911  
6912        if (nonLanguageTextTrackKind.indexOf(track.kind) === -1) {
6913          track.addEventListener('modechange', this.triggerSelectedlanguagechange_);
6914        }
6915      };
6916  
6917      _proto.removeTrack = function removeTrack(rtrack) {
6918        _TrackList.prototype.removeTrack.call(this, rtrack); // manually remove the event handlers we added
6919  
6920  
6921        if (rtrack.removeEventListener) {
6922          if (this.queueChange_) {
6923            rtrack.removeEventListener('modechange', this.queueChange_);
6924          }
6925  
6926          if (this.selectedlanguagechange_) {
6927            rtrack.removeEventListener('modechange', this.triggerSelectedlanguagechange_);
6928          }
6929        }
6930      };
6931  
6932      return TextTrackList;
6933    }(TrackList);
6934  
6935    /**
6936     * @file html-track-element-list.js
6937     */
6938  
6939    /**
6940     * The current list of {@link HtmlTrackElement}s.
6941     */
6942    var HtmlTrackElementList = /*#__PURE__*/function () {
6943      /**
6944       * Create an instance of this class.
6945       *
6946       * @param {HtmlTrackElement[]} [tracks=[]]
6947       *        A list of `HtmlTrackElement` to instantiate the list with.
6948       */
6949      function HtmlTrackElementList(trackElements) {
6950        if (trackElements === void 0) {
6951          trackElements = [];
6952        }
6953  
6954        this.trackElements_ = [];
6955        /**
6956         * @memberof HtmlTrackElementList
6957         * @member {number} length
6958         *         The current number of `Track`s in the this Trackist.
6959         * @instance
6960         */
6961  
6962        Object.defineProperty(this, 'length', {
6963          get: function get() {
6964            return this.trackElements_.length;
6965          }
6966        });
6967  
6968        for (var i = 0, length = trackElements.length; i < length; i++) {
6969          this.addTrackElement_(trackElements[i]);
6970        }
6971      }
6972      /**
6973       * Add an {@link HtmlTrackElement} to the `HtmlTrackElementList`
6974       *
6975       * @param {HtmlTrackElement} trackElement
6976       *        The track element to add to the list.
6977       *
6978       * @private
6979       */
6980  
6981  
6982      var _proto = HtmlTrackElementList.prototype;
6983  
6984      _proto.addTrackElement_ = function addTrackElement_(trackElement) {
6985        var index = this.trackElements_.length;
6986  
6987        if (!('' + index in this)) {
6988          Object.defineProperty(this, index, {
6989            get: function get() {
6990              return this.trackElements_[index];
6991            }
6992          });
6993        } // Do not add duplicate elements
6994  
6995  
6996        if (this.trackElements_.indexOf(trackElement) === -1) {
6997          this.trackElements_.push(trackElement);
6998        }
6999      }
7000      /**
7001       * Get an {@link HtmlTrackElement} from the `HtmlTrackElementList` given an
7002       * {@link TextTrack}.
7003       *
7004       * @param {TextTrack} track
7005       *        The track associated with a track element.
7006       *
7007       * @return {HtmlTrackElement|undefined}
7008       *         The track element that was found or undefined.
7009       *
7010       * @private
7011       */
7012      ;
7013  
7014      _proto.getTrackElementByTrack_ = function getTrackElementByTrack_(track) {
7015        var trackElement_;
7016  
7017        for (var i = 0, length = this.trackElements_.length; i < length; i++) {
7018          if (track === this.trackElements_[i].track) {
7019            trackElement_ = this.trackElements_[i];
7020            break;
7021          }
7022        }
7023  
7024        return trackElement_;
7025      }
7026      /**
7027       * Remove a {@link HtmlTrackElement} from the `HtmlTrackElementList`
7028       *
7029       * @param {HtmlTrackElement} trackElement
7030       *        The track element to remove from the list.
7031       *
7032       * @private
7033       */
7034      ;
7035  
7036      _proto.removeTrackElement_ = function removeTrackElement_(trackElement) {
7037        for (var i = 0, length = this.trackElements_.length; i < length; i++) {
7038          if (trackElement === this.trackElements_[i]) {
7039            if (this.trackElements_[i].track && typeof this.trackElements_[i].track.off === 'function') {
7040              this.trackElements_[i].track.off();
7041            }
7042  
7043            if (typeof this.trackElements_[i].off === 'function') {
7044              this.trackElements_[i].off();
7045            }
7046  
7047            this.trackElements_.splice(i, 1);
7048            break;
7049          }
7050        }
7051      };
7052  
7053      return HtmlTrackElementList;
7054    }();
7055  
7056    /**
7057     * @file text-track-cue-list.js
7058     */
7059  
7060    /**
7061     * @typedef {Object} TextTrackCueList~TextTrackCue
7062     *
7063     * @property {string} id
7064     *           The unique id for this text track cue
7065     *
7066     * @property {number} startTime
7067     *           The start time for this text track cue
7068     *
7069     * @property {number} endTime
7070     *           The end time for this text track cue
7071     *
7072     * @property {boolean} pauseOnExit
7073     *           Pause when the end time is reached if true.
7074     *
7075     * @see [Spec]{@link https://html.spec.whatwg.org/multipage/embedded-content.html#texttrackcue}
7076     */
7077  
7078    /**
7079     * A List of TextTrackCues.
7080     *
7081     * @see [Spec]{@link https://html.spec.whatwg.org/multipage/embedded-content.html#texttrackcuelist}
7082     */
7083    var TextTrackCueList = /*#__PURE__*/function () {
7084      /**
7085       * Create an instance of this class..
7086       *
7087       * @param {Array} cues
7088       *        A list of cues to be initialized with
7089       */
7090      function TextTrackCueList(cues) {
7091        TextTrackCueList.prototype.setCues_.call(this, cues);
7092        /**
7093         * @memberof TextTrackCueList
7094         * @member {number} length
7095         *         The current number of `TextTrackCue`s in the TextTrackCueList.
7096         * @instance
7097         */
7098  
7099        Object.defineProperty(this, 'length', {
7100          get: function get() {
7101            return this.length_;
7102          }
7103        });
7104      }
7105      /**
7106       * A setter for cues in this list. Creates getters
7107       * an an index for the cues.
7108       *
7109       * @param {Array} cues
7110       *        An array of cues to set
7111       *
7112       * @private
7113       */
7114  
7115  
7116      var _proto = TextTrackCueList.prototype;
7117  
7118      _proto.setCues_ = function setCues_(cues) {
7119        var oldLength = this.length || 0;
7120        var i = 0;
7121        var l = cues.length;
7122        this.cues_ = cues;
7123        this.length_ = cues.length;
7124  
7125        var defineProp = function defineProp(index) {
7126          if (!('' + index in this)) {
7127            Object.defineProperty(this, '' + index, {
7128              get: function get() {
7129                return this.cues_[index];
7130              }
7131            });
7132          }
7133        };
7134  
7135        if (oldLength < l) {
7136          i = oldLength;
7137  
7138          for (; i < l; i++) {
7139            defineProp.call(this, i);
7140          }
7141        }
7142      }
7143      /**
7144       * Get a `TextTrackCue` that is currently in the `TextTrackCueList` by id.
7145       *
7146       * @param {string} id
7147       *        The id of the cue that should be searched for.
7148       *
7149       * @return {TextTrackCueList~TextTrackCue|null}
7150       *         A single cue or null if none was found.
7151       */
7152      ;
7153  
7154      _proto.getCueById = function getCueById(id) {
7155        var result = null;
7156  
7157        for (var i = 0, l = this.length; i < l; i++) {
7158          var cue = this[i];
7159  
7160          if (cue.id === id) {
7161            result = cue;
7162            break;
7163          }
7164        }
7165  
7166        return result;
7167      };
7168  
7169      return TextTrackCueList;
7170    }();
7171  
7172    /**
7173     * @file track-kinds.js
7174     */
7175  
7176    /**
7177     * All possible `VideoTrackKind`s
7178     *
7179     * @see https://html.spec.whatwg.org/multipage/embedded-content.html#dom-videotrack-kind
7180     * @typedef VideoTrack~Kind
7181     * @enum
7182     */
7183    var VideoTrackKind = {
7184      alternative: 'alternative',
7185      captions: 'captions',
7186      main: 'main',
7187      sign: 'sign',
7188      subtitles: 'subtitles',
7189      commentary: 'commentary'
7190    };
7191    /**
7192     * All possible `AudioTrackKind`s
7193     *
7194     * @see https://html.spec.whatwg.org/multipage/embedded-content.html#dom-audiotrack-kind
7195     * @typedef AudioTrack~Kind
7196     * @enum
7197     */
7198  
7199    var AudioTrackKind = {
7200      'alternative': 'alternative',
7201      'descriptions': 'descriptions',
7202      'main': 'main',
7203      'main-desc': 'main-desc',
7204      'translation': 'translation',
7205      'commentary': 'commentary'
7206    };
7207    /**
7208     * All possible `TextTrackKind`s
7209     *
7210     * @see https://html.spec.whatwg.org/multipage/embedded-content.html#dom-texttrack-kind
7211     * @typedef TextTrack~Kind
7212     * @enum
7213     */
7214  
7215    var TextTrackKind = {
7216      subtitles: 'subtitles',
7217      captions: 'captions',
7218      descriptions: 'descriptions',
7219      chapters: 'chapters',
7220      metadata: 'metadata'
7221    };
7222    /**
7223     * All possible `TextTrackMode`s
7224     *
7225     * @see https://html.spec.whatwg.org/multipage/embedded-content.html#texttrackmode
7226     * @typedef TextTrack~Mode
7227     * @enum
7228     */
7229  
7230    var TextTrackMode = {
7231      disabled: 'disabled',
7232      hidden: 'hidden',
7233      showing: 'showing'
7234    };
7235  
7236    /**
7237     * A Track class that contains all of the common functionality for {@link AudioTrack},
7238     * {@link VideoTrack}, and {@link TextTrack}.
7239     *
7240     * > Note: This class should not be used directly
7241     *
7242     * @see {@link https://html.spec.whatwg.org/multipage/embedded-content.html}
7243     * @extends EventTarget
7244     * @abstract
7245     */
7246  
7247    var Track = /*#__PURE__*/function (_EventTarget) {
7248      inheritsLoose(Track, _EventTarget);
7249  
7250      /**
7251       * Create an instance of this class.
7252       *
7253       * @param {Object} [options={}]
7254       *        Object of option names and values
7255       *
7256       * @param {string} [options.kind='']
7257       *        A valid kind for the track type you are creating.
7258       *
7259       * @param {string} [options.id='vjs_track_' + Guid.newGUID()]
7260       *        A unique id for this AudioTrack.
7261       *
7262       * @param {string} [options.label='']
7263       *        The menu label for this track.
7264       *
7265       * @param {string} [options.language='']
7266       *        A valid two character language code.
7267       *
7268       * @abstract
7269       */
7270      function Track(options) {
7271        var _this;
7272  
7273        if (options === void 0) {
7274          options = {};
7275        }
7276  
7277        _this = _EventTarget.call(this) || this;
7278        var trackProps = {
7279          id: options.id || 'vjs_track_' + newGUID(),
7280          kind: options.kind || '',
7281          label: options.label || '',
7282          language: options.language || ''
7283        };
7284        /**
7285         * @memberof Track
7286         * @member {string} id
7287         *         The id of this track. Cannot be changed after creation.
7288         * @instance
7289         *
7290         * @readonly
7291         */
7292  
7293        /**
7294         * @memberof Track
7295         * @member {string} kind
7296         *         The kind of track that this is. Cannot be changed after creation.
7297         * @instance
7298         *
7299         * @readonly
7300         */
7301  
7302        /**
7303         * @memberof Track
7304         * @member {string} label
7305         *         The label of this track. Cannot be changed after creation.
7306         * @instance
7307         *
7308         * @readonly
7309         */
7310  
7311        /**
7312         * @memberof Track
7313         * @member {string} language
7314         *         The two letter language code for this track. Cannot be changed after
7315         *         creation.
7316         * @instance
7317         *
7318         * @readonly
7319         */
7320  
7321        var _loop = function _loop(key) {
7322          Object.defineProperty(assertThisInitialized(_this), key, {
7323            get: function get() {
7324              return trackProps[key];
7325            },
7326            set: function set() {}
7327          });
7328        };
7329  
7330        for (var key in trackProps) {
7331          _loop(key);
7332        }
7333  
7334        return _this;
7335      }
7336  
7337      return Track;
7338    }(EventTarget);
7339  
7340    /**
7341     * @file url.js
7342     * @module url
7343     */
7344    /**
7345     * @typedef {Object} url:URLObject
7346     *
7347     * @property {string} protocol
7348     *           The protocol of the url that was parsed.
7349     *
7350     * @property {string} hostname
7351     *           The hostname of the url that was parsed.
7352     *
7353     * @property {string} port
7354     *           The port of the url that was parsed.
7355     *
7356     * @property {string} pathname
7357     *           The pathname of the url that was parsed.
7358     *
7359     * @property {string} search
7360     *           The search query of the url that was parsed.
7361     *
7362     * @property {string} hash
7363     *           The hash of the url that was parsed.
7364     *
7365     * @property {string} host
7366     *           The host of the url that was parsed.
7367     */
7368  
7369    /**
7370     * Resolve and parse the elements of a URL.
7371     *
7372     * @function
7373     * @param    {String} url
7374     *           The url to parse
7375     *
7376     * @return   {url:URLObject}
7377     *           An object of url details
7378     */
7379  
7380    var parseUrl = function parseUrl(url) {
7381      var props = ['protocol', 'hostname', 'port', 'pathname', 'search', 'hash', 'host']; // add the url to an anchor and let the browser parse the URL
7382  
7383      var a = document.createElement('a');
7384      a.href = url; // IE8 (and 9?) Fix
7385      // ie8 doesn't parse the URL correctly until the anchor is actually
7386      // added to the body, and an innerHTML is needed to trigger the parsing
7387  
7388      var addToBody = a.host === '' && a.protocol !== 'file:';
7389      var div;
7390  
7391      if (addToBody) {
7392        div = document.createElement('div');
7393        div.innerHTML = "<a href=\"" + url + "\"></a>";
7394        a = div.firstChild; // prevent the div from affecting layout
7395  
7396        div.setAttribute('style', 'display:none; position:absolute;');
7397        document.body.appendChild(div);
7398      } // Copy the specific URL properties to a new object
7399      // This is also needed for IE8 because the anchor loses its
7400      // properties when it's removed from the dom
7401  
7402  
7403      var details = {};
7404  
7405      for (var i = 0; i < props.length; i++) {
7406        details[props[i]] = a[props[i]];
7407      } // IE9 adds the port to the host property unlike everyone else. If
7408      // a port identifier is added for standard ports, strip it.
7409  
7410  
7411      if (details.protocol === 'http:') {
7412        details.host = details.host.replace(/:80$/, '');
7413      }
7414  
7415      if (details.protocol === 'https:') {
7416        details.host = details.host.replace(/:443$/, '');
7417      }
7418  
7419      if (!details.protocol) {
7420        details.protocol = window$3.location.protocol;
7421      }
7422  
7423      if (addToBody) {
7424        document.body.removeChild(div);
7425      }
7426  
7427      return details;
7428    };
7429    /**
7430     * Get absolute version of relative URL. Used to tell Flash the correct URL.
7431     *
7432     * @function
7433     * @param    {string} url
7434     *           URL to make absolute
7435     *
7436     * @return   {string}
7437     *           Absolute URL
7438     *
7439     * @see      http://stackoverflow.com/questions/470832/getting-an-absolute-url-from-a-relative-one-ie6-issue
7440     */
7441  
7442    var getAbsoluteURL = function getAbsoluteURL(url) {
7443      // Check if absolute URL
7444      if (!url.match(/^https?:\/\//)) {
7445        // Convert to absolute URL. Flash hosted off-site needs an absolute URL.
7446        var div = document.createElement('div');
7447        div.innerHTML = "<a href=\"" + url + "\">x</a>";
7448        url = div.firstChild.href;
7449      }
7450  
7451      return url;
7452    };
7453    /**
7454     * Returns the extension of the passed file name. It will return an empty string
7455     * if passed an invalid path.
7456     *
7457     * @function
7458     * @param    {string} path
7459     *           The fileName path like '/path/to/file.mp4'
7460     *
7461     * @return  {string}
7462     *           The extension in lower case or an empty string if no
7463     *           extension could be found.
7464     */
7465  
7466    var getFileExtension = function getFileExtension(path) {
7467      if (typeof path === 'string') {
7468        var splitPathRe = /^(\/?)([\s\S]*?)((?:\.{1,2}|[^\/]+?)(\.([^\.\/\?]+)))(?:[\/]*|[\?].*)$/;
7469        var pathParts = splitPathRe.exec(path);
7470  
7471        if (pathParts) {
7472          return pathParts.pop().toLowerCase();
7473        }
7474      }
7475  
7476      return '';
7477    };
7478    /**
7479     * Returns whether the url passed is a cross domain request or not.
7480     *
7481     * @function
7482     * @param    {string} url
7483     *           The url to check.
7484     *
7485     * @param    {Object} [winLoc]
7486     *           the domain to check the url against, defaults to window.location
7487     *
7488     * @param    {string} [winLoc.protocol]
7489     *           The window location protocol defaults to window.location.protocol
7490     *
7491     * @param    {string} [winLoc.host]
7492     *           The window location host defaults to window.location.host
7493     *
7494     * @return   {boolean}
7495     *           Whether it is a cross domain request or not.
7496     */
7497  
7498    var isCrossOrigin = function isCrossOrigin(url, winLoc) {
7499      if (winLoc === void 0) {
7500        winLoc = window$3.location;
7501      }
7502  
7503      var urlInfo = parseUrl(url); // IE8 protocol relative urls will return ':' for protocol
7504  
7505      var srcProtocol = urlInfo.protocol === ':' ? winLoc.protocol : urlInfo.protocol; // Check if url is for another domain/origin
7506      // IE8 doesn't know location.origin, so we won't rely on it here
7507  
7508      var crossOrigin = srcProtocol + urlInfo.host !== winLoc.protocol + winLoc.host;
7509      return crossOrigin;
7510    };
7511  
7512    var Url = /*#__PURE__*/Object.freeze({
7513      __proto__: null,
7514      parseUrl: parseUrl,
7515      getAbsoluteURL: getAbsoluteURL,
7516      getFileExtension: getFileExtension,
7517      isCrossOrigin: isCrossOrigin
7518    });
7519  
7520    var isFunction_1 = isFunction;
7521    var toString$1 = Object.prototype.toString;
7522  
7523    function isFunction(fn) {
7524      var string = toString$1.call(fn);
7525      return string === '[object Function]' || typeof fn === 'function' && string !== '[object RegExp]' || typeof window !== 'undefined' && ( // IE8 and below
7526      fn === window.setTimeout || fn === window.alert || fn === window.confirm || fn === window.prompt);
7527    }
7528  
7529    /**
7530     * @license
7531     * slighly modified parse-headers 2.0.2 <https://github.com/kesla/parse-headers/>
7532     * Copyright (c) 2014 David Björklund
7533     * Available under the MIT license
7534     * <https://github.com/kesla/parse-headers/blob/master/LICENCE>
7535     */
7536  
7537  
7538    var parseHeaders = function parseHeaders(headers) {
7539      var result = {};
7540  
7541      if (!headers) {
7542        return result;
7543      }
7544  
7545      headers.trim().split('\n').forEach(function (row) {
7546        var index = row.indexOf(':');
7547        var key = row.slice(0, index).trim().toLowerCase();
7548        var value = row.slice(index + 1).trim();
7549  
7550        if (typeof result[key] === 'undefined') {
7551          result[key] = value;
7552        } else if (Array.isArray(result[key])) {
7553          result[key].push(value);
7554        } else {
7555          result[key] = [result[key], value];
7556        }
7557      });
7558      return result;
7559    };
7560  
7561    var xhr = createXHR; // Allow use of default import syntax in TypeScript
7562  
7563    var default_1 = createXHR;
7564    createXHR.XMLHttpRequest = window$3.XMLHttpRequest || noop;
7565    createXHR.XDomainRequest = "withCredentials" in new createXHR.XMLHttpRequest() ? createXHR.XMLHttpRequest : window$3.XDomainRequest;
vendor: 4,182 bytes, lines 7566-7724
7566    forEachArray(["get", "put", "post", "patch", "head", "delete"], function (method) {
7567      createXHR[method === "delete" ? "del" : method] = function (uri, options, callback) {
7568        options = initParams(uri, options, callback);
7569        options.method = method.toUpperCase();
7570        return _createXHR(options);
7571      };
7572    });
7573  
7574    function forEachArray(array, iterator) {
7575      for (var i = 0; i < array.length; i++) {
7576        iterator(array[i]);
7577      }
7578    }
7579  
7580    function isEmpty(obj) {
7581      for (var i in obj) {
7582        if (obj.hasOwnProperty(i)) return false;
7583      }
7584  
7585      return true;
7586    }
7587  
7588    function initParams(uri, options, callback) {
7589      var params = uri;
7590  
7591      if (isFunction_1(options)) {
7592        callback = options;
7593  
7594        if (typeof uri === "string") {
7595          params = {
7596            uri: uri
7597          };
7598        }
7599      } else {
7600        params = _extends_1({}, options, {
7601          uri: uri
7602        });
7603      }
7604  
7605      params.callback = callback;
7606      return params;
7607    }
7608  
7609    function createXHR(uri, options, callback) {
7610      options = initParams(uri, options, callback);
7611      return _createXHR(options);
7612    }
7613  
7614    function _createXHR(options) {
7615      if (typeof options.callback === "undefined") {
7616        throw new Error("callback argument missing");
7617      }
7618  
7619      var called = false;
7620  
7621      var callback = function cbOnce(err, response, body) {
7622        if (!called) {
7623          called = true;
7624          options.callback(err, response, body);
7625        }
7626      };
7627  
7628      function readystatechange() {
7629        if (xhr.readyState === 4) {
7630          setTimeout(loadFunc, 0);
7631        }
7632      }
7633  
7634      function getBody() {
7635        // Chrome with requestType=blob throws errors arround when even testing access to responseText
7636        var body = undefined;
7637  
7638        if (xhr.response) {
7639          body = xhr.response;
7640        } else {
7641          body = xhr.responseText || getXml(xhr);
7642        }
7643  
7644        if (isJson) {
7645          try {
7646            body = JSON.parse(body);
7647          } catch (e) {}
7648        }
7649  
7650        return body;
7651      }
7652  
7653      function errorFunc(evt) {
7654        clearTimeout(timeoutTimer);
7655  
7656        if (!(evt instanceof Error)) {
7657          evt = new Error("" + (evt || "Unknown XMLHttpRequest Error"));
7658        }
7659  
7660        evt.statusCode = 0;
7661        return callback(evt, failureResponse);
7662      } // will load the data & process the response in a special response object
7663  
7664  
7665      function loadFunc() {
7666        if (aborted) return;
7667        var status;
7668        clearTimeout(timeoutTimer);
7669  
7670        if (options.useXDR && xhr.status === undefined) {
7671          //IE8 CORS GET successful response doesn't have a status field, but body is fine
7672          status = 200;
7673        } else {
7674          status = xhr.status === 1223 ? 204 : xhr.status;
7675        }
7676  
7677        var response = failureResponse;
7678        var err = null;
7679  
7680        if (status !== 0) {
7681          response = {
7682            body: getBody(),
7683            statusCode: status,
7684            method: method,
7685            headers: {},
7686            url: uri,
7687            rawRequest: xhr
7688          };
7689  
7690          if (xhr.getAllResponseHeaders) {
7691            //remember xhr can in fact be XDR for CORS in IE
7692            response.headers = parseHeaders(xhr.getAllResponseHeaders());
7693          }
7694        } else {
7695          err = new Error("Internal XMLHttpRequest Error");
7696        }
7697  
7698        return callback(err, response, response.body);
7699      }
7700  
7701      var xhr = options.xhr || null;
7702  
7703      if (!xhr) {
7704        if (options.cors || options.useXDR) {
7705          xhr = new createXHR.XDomainRequest();
7706        } else {
7707          xhr = new createXHR.XMLHttpRequest();
7708        }
7709      }
7710  
7711      var key;
7712      var aborted;
7713      var uri = xhr.url = options.uri || options.url;
7714      var method = xhr.method = options.method || "GET";
7715      var body = options.body || options.data;
7716      var headers = xhr.headers = options.headers || {};
7717      var sync = !!options.sync;
7718      var isJson = false;
7719      var timeoutTimer;
7720      var failureResponse = {
7721        body: undefined,
7722        headers: {},
7723        statusCode: 0,
7724        method: method,
vendor: 4,249 bytes, lines 7725-7856
7725        url: uri,
7726        rawRequest: xhr
7727      };
7728  
7729      if ("json" in options && options.json !== false) {
7730        isJson = true;
7731        headers["accept"] || headers["Accept"] || (headers["Accept"] = "application/json"); //Don't override existing accept header declared by user
7732  
7733        if (method !== "GET" && method !== "HEAD") {
7734          headers["content-type"] || headers["Content-Type"] || (headers["Content-Type"] = "application/json"); //Don't override existing accept header declared by user
7735  
7736          body = JSON.stringify(options.json === true ? body : options.json);
7737        }
7738      }
7739  
7740      xhr.onreadystatechange = readystatechange;
7741      xhr.onload = loadFunc;
7742      xhr.onerror = errorFunc; // IE9 must have onprogress be set to a unique function.
7743  
7744      xhr.onprogress = function () {// IE must die
7745      };
7746  
7747      xhr.onabort = function () {
7748        aborted = true;
7749      };
7750  
7751      xhr.ontimeout = errorFunc;
7752      xhr.open(method, uri, !sync, options.username, options.password); //has to be after open
7753  
7754      if (!sync) {
7755        xhr.withCredentials = !!options.withCredentials;
7756      } // Cannot set timeout with sync request
7757      // not setting timeout on the xhr object, because of old webkits etc. not handling that correctly
7758      // both npm's request and jquery 1.x use this kind of timeout, so this is being consistent
7759  
7760  
7761      if (!sync && options.timeout > 0) {
7762        timeoutTimer = setTimeout(function () {
7763          if (aborted) return;
7764          aborted = true; //IE9 may still call readystatechange
7765  
7766          xhr.abort("timeout");
7767          var e = new Error("XMLHttpRequest timeout");
7768          e.code = "ETIMEDOUT";
7769          errorFunc(e);
7770        }, options.timeout);
7771      }
7772  
7773      if (xhr.setRequestHeader) {
7774        for (key in headers) {
7775          if (headers.hasOwnProperty(key)) {
7776            xhr.setRequestHeader(key, headers[key]);
7777          }
7778        }
7779      } else if (options.headers && !isEmpty(options.headers)) {
7780        throw new Error("Headers cannot be set on an XDomainRequest object");
7781      }
7782  
7783      if ("responseType" in options) {
7784        xhr.responseType = options.responseType;
7785      }
7786  
7787      if ("beforeSend" in options && typeof options.beforeSend === "function") {
7788        options.beforeSend(xhr);
7789      } // Microsoft Edge browser sends "undefined" when send is called with undefined value.
7790      // XMLHttpRequest spec says to pass null as body to indicate no body
7791      // See https://github.com/naugtur/xhr/issues/100.
7792  
7793  
7794      xhr.send(body || null);
7795      return xhr;
7796    }
7797  
7798    function getXml(xhr) {
7799      // xhr.responseXML will throw Exception "InvalidStateError" or "DOMException"
7800      // See https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest/responseXML.
7801      try {
7802        if (xhr.responseType === "document") {
7803          return xhr.responseXML;
7804        }
7805  
7806        var firefoxBugTakenEffect = xhr.responseXML && xhr.responseXML.documentElement.nodeName === "parsererror";
7807  
7808        if (xhr.responseType === "" && !firefoxBugTakenEffect) {
7809          return xhr.responseXML;
7810        }
7811      } catch (e) {}
7812  
7813      return null;
7814    }
7815  
7816    function noop() {}
7817    xhr["default"] = default_1;
7818  
7819    /**
7820     * Takes a webvtt file contents and parses it into cues
7821     *
7822     * @param {string} srcContent
7823     *        webVTT file contents
7824     *
7825     * @param {TextTrack} track
7826     *        TextTrack to add cues to. Cues come from the srcContent.
7827     *
7828     * @private
7829     */
7830  
7831    var parseCues = function parseCues(srcContent, track) {
7832      var parser = new window$3.WebVTT.Parser(window$3, window$3.vttjs, window$3.WebVTT.StringDecoder());
7833      var errors = [];
7834  
7835      parser.oncue = function (cue) {
7836        track.addCue(cue);
7837      };
7838  
7839      parser.onparsingerror = function (error) {
7840        errors.push(error);
7841      };
7842  
7843      parser.onflush = function () {
7844        track.trigger({
7845          type: 'loadeddata',
7846          target: track
7847        });
7848      };
7849  
7850      parser.parse(srcContent);
7851  
7852      if (errors.length > 0) {
7853        if (window$3.console && window$3.console.groupCollapsed) {
7854          window$3.console.groupCollapsed("Text Track parsing errors for " + track.src);
7855        }
7856  
7857        errors.forEach(function (error) {
7858          return log.error(error);
7859        });
7860  
7861        if (window$3.console && window$3.console.groupEnd) {
7862          window$3.console.groupEnd();
7863        }
7864      }
7865  
7866      parser.flush();
7867    };
7868    /**
7869     * Load a `TextTrack` from a specified url.
7870     *
7871     * @param {string} src
7872     *        Url to load track from.
7873     *
7874     * @param {TextTrack} track
7875     *        Track to add cues to. Comes from the content at the end of `url`.
7876     *
7877     * @private
7878     */
7879  
7880  
7881    var loadTrack = function loadTrack(src, track) {
7882      var opts = {
7883        uri: src
7884      };
7885      var crossOrigin = isCrossOrigin(src);
7886  
7887      if (crossOrigin) {
7888        opts.cors = crossOrigin;
7889      }
7890  
7891      xhr(opts, bind(this, function (err, response, responseBody) {
7892        if (err) {
7893          return log.error(err, response);
7894        }
7895  
7896        track.loaded_ = true; // Make sure that vttjs has loaded, otherwise, wait till it finished loading
7897        // NOTE: this is only used for the alt/video.novtt.js build
7898  
7899        if (typeof window$3.WebVTT !== 'function') {
7900          if (track.tech_) {
7901            // to prevent use before define eslint error, we define loadHandler
7902            // as a let here
7903            track.tech_.any(['vttjsloaded', 'vttjserror'], function (event) {
7904              if (event.type === 'vttjserror') {
7905                log.error("vttjs failed to load, stopping trying to process " + track.src);
7906                return;
7907              }
7908  
7909              return parseCues(responseBody, track);
7910            });
7911          }
7912        } else {
7913          parseCues(responseBody, track);
7914        }
7915      }));
7916    };
7917    /**
7918     * A representation of a single `TextTrack`.
7919     *
7920     * @see [Spec]{@link https://html.spec.whatwg.org/multipage/embedded-content.html#texttrack}
7921     * @extends Track
7922     */
7923  
7924  
7925    var TextTrack = /*#__PURE__*/function (_Track) {
7926      inheritsLoose(TextTrack, _Track);
7927  
7928      /**
7929       * Create an instance of this class.
7930       *
7931       * @param {Object} options={}
7932       *        Object of option names and values
7933       *
7934       * @param {Tech} options.tech
7935       *        A reference to the tech that owns this TextTrack.
7936       *
7937       * @param {TextTrack~Kind} [options.kind='subtitles']
7938       *        A valid text track kind.
7939       *
7940       * @param {TextTrack~Mode} [options.mode='disabled']
7941       *        A valid text track mode.
7942       *
7943       * @param {string} [options.id='vjs_track_' + Guid.newGUID()]
7944       *        A unique id for this TextTrack.
7945       *
7946       * @param {string} [options.label='']
7947       *        The menu label for this track.
7948       *
7949       * @param {string} [options.language='']
7950       *        A valid two character language code.
7951       *
7952       * @param {string} [options.srclang='']
7953       *        A valid two character language code. An alternative, but deprioritized
7954       *        version of `options.language`
7955       *
7956       * @param {string} [options.src]
7957       *        A url to TextTrack cues.
7958       *
7959       * @param {boolean} [options.default]
7960       *        If this track should default to on or off.
7961       */
7962      function TextTrack(options) {
7963        var _this;
7964  
7965        if (options === void 0) {
7966          options = {};
7967        }
7968  
7969        if (!options.tech) {
7970          throw new Error('A tech was not provided.');
7971        }
7972  
7973        var settings = mergeOptions(options, {
7974          kind: TextTrackKind[options.kind] || 'subtitles',
7975          language: options.language || options.srclang || ''
7976        });
7977        var mode = TextTrackMode[settings.mode] || 'disabled';
7978        var default_ = settings["default"];
7979  
7980        if (settings.kind === 'metadata' || settings.kind === 'chapters') {
7981          mode = 'hidden';
7982        }
7983  
7984        _this = _Track.call(this, settings) || this;
7985        _this.tech_ = settings.tech;
7986        _this.cues_ = [];
7987        _this.activeCues_ = [];
7988        _this.preload_ = _this.tech_.preloadTextTracks !== false;
7989        var cues = new TextTrackCueList(_this.cues_);
7990        var activeCues = new TextTrackCueList(_this.activeCues_);
7991        var changed = false;
7992        var timeupdateHandler = bind(assertThisInitialized(_this), function () {
7993          // Accessing this.activeCues for the side-effects of updating itself
7994          // due to its nature as a getter function. Do not remove or cues will
7995          // stop updating!
7996          // Use the setter to prevent deletion from uglify (pure_getters rule)
7997          this.activeCues = this.activeCues;
7998  
7999          if (changed) {
8000            this.trigger('cuechange');
8001            changed = false;
8002          }
8003        });
8004  
8005        if (mode !== 'disabled') {
8006          _this.tech_.ready(function () {
8007            _this.tech_.on('timeupdate', timeupdateHandler);
8008          }, true);
8009        }
8010  
8011        Object.defineProperties(assertThisInitialized(_this), {
8012          /**
8013           * @memberof TextTrack
8014           * @member {boolean} default
8015           *         If this track was set to be on or off by default. Cannot be changed after
8016           *         creation.
8017           * @instance
8018           *
8019           * @readonly
8020           */
8021          "default": {
8022            get: function get() {
8023              return default_;
8024            },
8025            set: function set() {}
8026          },
8027  
8028          /**
8029           * @memberof TextTrack
8030           * @member {string} mode
8031           *         Set the mode of this TextTrack to a valid {@link TextTrack~Mode}. Will
8032           *         not be set if setting to an invalid mode.
8033           * @instance
8034           *
8035           * @fires TextTrack#modechange
8036           */
8037          mode: {
8038            get: function get() {
8039              return mode;
8040            },
8041            set: function set(newMode) {
8042              var _this2 = this;
8043  
8044              if (!TextTrackMode[newMode]) {
8045                return;
8046              }
8047  
8048              mode = newMode;
8049  
8050              if (!this.preload_ && mode !== 'disabled' && this.cues.length === 0) {
8051                // On-demand load.
8052                loadTrack(this.src, this);
8053              }
8054  
8055              if (mode !== 'disabled') {
8056                this.tech_.ready(function () {
8057                  _this2.tech_.on('timeupdate', timeupdateHandler);
8058                }, true);
8059              } else {
8060                this.tech_.off('timeupdate', timeupdateHandler);
8061              }
8062              /**
8063               * An event that fires when mode changes on this track. This allows
8064               * the TextTrackList that holds this track to act accordingly.
8065               *
8066               * > Note: This is not part of the spec!
8067               *
8068               * @event TextTrack#modechange
8069               * @type {EventTarget~Event}
8070               */
8071  
8072  
8073              this.trigger('modechange');
8074            }
8075          },
8076  
8077          /**
8078           * @memberof TextTrack
8079           * @member {TextTrackCueList} cues
8080           *         The text track cue list for this TextTrack.
8081           * @instance
8082           */
8083          cues: {
8084            get: function get() {
8085              if (!this.loaded_) {
8086                return null;
8087              }
8088  
8089              return cues;
8090            },
8091            set: function set() {}
8092          },
8093  
8094          /**
8095           * @memberof TextTrack
8096           * @member {TextTrackCueList} activeCues
8097           *         The list text track cues that are currently active for this TextTrack.
8098           * @instance
8099           */
8100          activeCues: {
8101            get: function get() {
8102              if (!this.loaded_) {
8103                return null;
8104              } // nothing to do
8105  
8106  
8107              if (this.cues.length === 0) {
8108                return activeCues;
8109              }
8110  
8111              var ct = this.tech_.currentTime();
8112              var active = [];
8113  
8114              for (var i = 0, l = this.cues.length; i < l; i++) {
8115                var cue = this.cues[i];
8116  
8117                if (cue.startTime <= ct && cue.endTime >= ct) {
8118                  active.push(cue);
8119                } else if (cue.startTime === cue.endTime && cue.startTime <= ct && cue.startTime + 0.5 >= ct) {
8120                  active.push(cue);
8121                }
8122              }
8123  
8124              changed = false;
8125  
8126              if (active.length !== this.activeCues_.length) {
8127                changed = true;
8128              } else {
8129                for (var _i = 0; _i < active.length; _i++) {
8130                  if (this.activeCues_.indexOf(active[_i]) === -1) {
8131                    changed = true;
8132                  }
8133                }
8134              }
8135  
8136              this.activeCues_ = active;
8137              activeCues.setCues_(this.activeCues_);
8138              return activeCues;
8139            },
8140            // /!\ Keep this setter empty (see the timeupdate handler above)
8141            set: function set() {}
8142          }
8143        });
8144  
8145        if (settings.src) {
8146          _this.src = settings.src;
8147  
8148          if (!_this.preload_) {
8149            // Tracks will load on-demand.
8150            // Act like we're loaded for other purposes.
8151            _this.loaded_ = true;
8152          }
8153  
8154          if (_this.preload_ || default_ || settings.kind !== 'subtitles' && settings.kind !== 'captions') {
8155            loadTrack(_this.src, assertThisInitialized(_this));
8156          }
8157        } else {
8158          _this.loaded_ = true;
8159        }
8160  
8161        return _this;
8162      }
8163      /**
8164       * Add a cue to the internal list of cues.
8165       *
8166       * @param {TextTrack~Cue} cue
8167       *        The cue to add to our internal list
8168       */
8169  
8170  
8171      var _proto = TextTrack.prototype;
8172  
8173      _proto.addCue = function addCue(originalCue) {
8174        var cue = originalCue;
8175  
8176        if (window$3.vttjs && !(originalCue instanceof window$3.vttjs.VTTCue)) {
8177          cue = new window$3.vttjs.VTTCue(originalCue.startTime, originalCue.endTime, originalCue.text);
8178  
8179          for (var prop in originalCue) {
8180            if (!(prop in cue)) {
8181              cue[prop] = originalCue[prop];
8182            }
8183          } // make sure that `id` is copied over
8184  
8185  
8186          cue.id = originalCue.id;
8187          cue.originalCue_ = originalCue;
8188        }
8189  
8190        var tracks = this.tech_.textTracks();
8191  
8192        for (var i = 0; i < tracks.length; i++) {
8193          if (tracks[i] !== this) {
8194            tracks[i].removeCue(cue);
8195          }
8196        }
8197  
8198        this.cues_.push(cue);
8199        this.cues.setCues_(this.cues_);
8200      }
8201      /**
8202       * Remove a cue from our internal list
8203       *
8204       * @param {TextTrack~Cue} removeCue
8205       *        The cue to remove from our internal list
8206       */
8207      ;
8208  
8209      _proto.removeCue = function removeCue(_removeCue) {
8210        var i = this.cues_.length;
8211  
8212        while (i--) {
8213          var cue = this.cues_[i];
8214  
8215          if (cue === _removeCue || cue.originalCue_ && cue.originalCue_ === _removeCue) {
8216            this.cues_.splice(i, 1);
8217            this.cues.setCues_(this.cues_);
8218            break;
8219          }
8220        }
8221      };
8222  
8223      return TextTrack;
8224    }(Track);
8225    /**
8226     * cuechange - One or more cues in the track have become active or stopped being active.
8227     */
8228  
8229  
8230    TextTrack.prototype.allowedEvents_ = {
8231      cuechange: 'cuechange'
8232    };
8233  
8234    /**
8235     * A representation of a single `AudioTrack`. If it is part of an {@link AudioTrackList}
8236     * only one `AudioTrack` in the list will be enabled at a time.
8237     *
8238     * @see [Spec]{@link https://html.spec.whatwg.org/multipage/embedded-content.html#audiotrack}
8239     * @extends Track
8240     */
8241  
8242    var AudioTrack = /*#__PURE__*/function (_Track) {
8243      inheritsLoose(AudioTrack, _Track);
8244  
8245      /**
8246       * Create an instance of this class.
8247       *
8248       * @param {Object} [options={}]
8249       *        Object of option names and values
8250       *
8251       * @param {AudioTrack~Kind} [options.kind='']
8252       *        A valid audio track kind
8253       *
8254       * @param {string} [options.id='vjs_track_' + Guid.newGUID()]
8255       *        A unique id for this AudioTrack.
8256       *
8257       * @param {string} [options.label='']
8258       *        The menu label for this track.
8259       *
8260       * @param {string} [options.language='']
8261       *        A valid two character language code.
8262       *
8263       * @param {boolean} [options.enabled]
8264       *        If this track is the one that is currently playing. If this track is part of
8265       *        an {@link AudioTrackList}, only one {@link AudioTrack} will be enabled.
8266       */
8267      function AudioTrack(options) {
8268        var _this;
8269  
8270        if (options === void 0) {
8271          options = {};
8272        }
8273  
8274        var settings = mergeOptions(options, {
8275          kind: AudioTrackKind[options.kind] || ''
8276        });
8277        _this = _Track.call(this, settings) || this;
8278        var enabled = false;
8279        /**
8280         * @memberof AudioTrack
8281         * @member {boolean} enabled
8282         *         If this `AudioTrack` is enabled or not. When setting this will
8283         *         fire {@link AudioTrack#enabledchange} if the state of enabled is changed.
8284         * @instance
8285         *
8286         * @fires VideoTrack#selectedchange
8287         */
8288  
8289        Object.defineProperty(assertThisInitialized(_this), 'enabled', {
8290          get: function get() {
8291            return enabled;
8292          },
8293          set: function set(newEnabled) {
8294            // an invalid or unchanged value
8295            if (typeof newEnabled !== 'boolean' || newEnabled === enabled) {
8296              return;
8297            }
8298  
8299            enabled = newEnabled;
8300            /**
8301             * An event that fires when enabled changes on this track. This allows
8302             * the AudioTrackList that holds this track to act accordingly.
8303             *
8304             * > Note: This is not part of the spec! Native tracks will do
8305             *         this internally without an event.
8306             *
8307             * @event AudioTrack#enabledchange
8308             * @type {EventTarget~Event}
8309             */
8310  
8311            this.trigger('enabledchange');
8312          }
8313        }); // if the user sets this track to selected then
8314        // set selected to that true value otherwise
8315        // we keep it false
8316  
8317        if (settings.enabled) {
8318          _this.enabled = settings.enabled;
8319        }
8320  
8321        _this.loaded_ = true;
8322        return _this;
8323      }
8324  
8325      return AudioTrack;
8326    }(Track);
8327  
8328    /**
8329     * A representation of a single `VideoTrack`.
8330     *
8331     * @see [Spec]{@link https://html.spec.whatwg.org/multipage/embedded-content.html#videotrack}
8332     * @extends Track
8333     */
8334  
8335    var VideoTrack = /*#__PURE__*/function (_Track) {
8336      inheritsLoose(VideoTrack, _Track);
8337  
8338      /**
8339       * Create an instance of this class.
8340       *
8341       * @param {Object} [options={}]
8342       *        Object of option names and values
8343       *
8344       * @param {string} [options.kind='']
8345       *        A valid {@link VideoTrack~Kind}
8346       *
8347       * @param {string} [options.id='vjs_track_' + Guid.newGUID()]
8348       *        A unique id for this AudioTrack.
8349       *
8350       * @param {string} [options.label='']
8351       *        The menu label for this track.
8352       *
8353       * @param {string} [options.language='']
8354       *        A valid two character language code.
8355       *
8356       * @param {boolean} [options.selected]
8357       *        If this track is the one that is currently playing.
8358       */
8359      function VideoTrack(options) {
8360        var _this;
8361  
8362        if (options === void 0) {
8363          options = {};
8364        }
8365  
8366        var settings = mergeOptions(options, {
8367          kind: VideoTrackKind[options.kind] || ''
8368        });
8369        _this = _Track.call(this, settings) || this;
8370        var selected = false;
8371        /**
8372         * @memberof VideoTrack
8373         * @member {boolean} selected
8374         *         If this `VideoTrack` is selected or not. When setting this will
8375         *         fire {@link VideoTrack#selectedchange} if the state of selected changed.
8376         * @instance
8377         *
8378         * @fires VideoTrack#selectedchange
8379         */
8380  
8381        Object.defineProperty(assertThisInitialized(_this), 'selected', {
8382          get: function get() {
8383            return selected;
8384          },
8385          set: function set(newSelected) {
8386            // an invalid or unchanged value
8387            if (typeof newSelected !== 'boolean' || newSelected === selected) {
8388              return;
8389            }
8390  
8391            selected = newSelected;
8392            /**
8393             * An event that fires when selected changes on this track. This allows
8394             * the VideoTrackList that holds this track to act accordingly.
8395             *
8396             * > Note: This is not part of the spec! Native tracks will do
8397             *         this internally without an event.
8398             *
8399             * @event VideoTrack#selectedchange
8400             * @type {EventTarget~Event}
8401             */
8402  
8403            this.trigger('selectedchange');
8404          }
8405        }); // if the user sets this track to selected then
8406        // set selected to that true value otherwise
8407        // we keep it false
8408  
8409        if (settings.selected) {
8410          _this.selected = settings.selected;
8411        }
8412  
8413        return _this;
8414      }
8415  
8416      return VideoTrack;
8417    }(Track);
8418  
8419    /**
8420     * @memberof HTMLTrackElement
8421     * @typedef {HTMLTrackElement~ReadyState}
8422     * @enum {number}
8423     */
8424  
8425    var NONE = 0;
8426    var LOADING = 1;
8427    var LOADED = 2;
8428    var ERROR = 3;
8429    /**
8430     * A single track represented in the DOM.
8431     *
8432     * @see [Spec]{@link https://html.spec.whatwg.org/multipage/embedded-content.html#htmltrackelement}
8433     * @extends EventTarget
8434     */
8435  
8436    var HTMLTrackElement = /*#__PURE__*/function (_EventTarget) {
8437      inheritsLoose(HTMLTrackElement, _EventTarget);
8438  
8439      /**
8440       * Create an instance of this class.
8441       *
8442       * @param {Object} options={}
8443       *        Object of option names and values
8444       *
8445       * @param {Tech} options.tech
8446       *        A reference to the tech that owns this HTMLTrackElement.
8447       *
8448       * @param {TextTrack~Kind} [options.kind='subtitles']
8449       *        A valid text track kind.
8450       *
8451       * @param {TextTrack~Mode} [options.mode='disabled']
8452       *        A valid text track mode.
8453       *
8454       * @param {string} [options.id='vjs_track_' + Guid.newGUID()]
8455       *        A unique id for this TextTrack.
8456       *
8457       * @param {string} [options.label='']
8458       *        The menu label for this track.
8459       *
8460       * @param {string} [options.language='']
8461       *        A valid two character language code.
8462       *
8463       * @param {string} [options.srclang='']
8464       *        A valid two character language code. An alternative, but deprioritized
8465       *        vesion of `options.language`
8466       *
8467       * @param {string} [options.src]
8468       *        A url to TextTrack cues.
8469       *
8470       * @param {boolean} [options.default]
8471       *        If this track should default to on or off.
8472       */
8473      function HTMLTrackElement(options) {
8474        var _this;
8475  
8476        if (options === void 0) {
8477          options = {};
8478        }
8479  
8480        _this = _EventTarget.call(this) || this;
8481        var readyState;
8482        var track = new TextTrack(options);
8483        _this.kind = track.kind;
8484        _this.src = track.src;
8485        _this.srclang = track.language;
8486        _this.label = track.label;
8487        _this["default"] = track["default"];
8488        Object.defineProperties(assertThisInitialized(_this), {
8489          /**
8490           * @memberof HTMLTrackElement
8491           * @member {HTMLTrackElement~ReadyState} readyState
8492           *         The current ready state of the track element.
8493           * @instance
8494           */
8495          readyState: {
8496            get: function get() {
8497              return readyState;
8498            }
8499          },
8500  
8501          /**
8502           * @memberof HTMLTrackElement
8503           * @member {TextTrack} track
8504           *         The underlying TextTrack object.
8505           * @instance
8506           *
8507           */
8508          track: {
8509            get: function get() {
8510              return track;
8511            }
8512          }
8513        });
8514        readyState = NONE;
8515        /**
8516         * @listens TextTrack#loadeddata
8517         * @fires HTMLTrackElement#load
8518         */
8519  
8520        track.addEventListener('loadeddata', function () {
8521          readyState = LOADED;
8522  
8523          _this.trigger({
8524            type: 'load',
8525            target: assertThisInitialized(_this)
8526          });
8527        });
8528        return _this;
8529      }
8530  
8531      return HTMLTrackElement;
8532    }(EventTarget);
8533  
8534    HTMLTrackElement.prototype.allowedEvents_ = {
8535      load: 'load'
8536    };
8537    HTMLTrackElement.NONE = NONE;
8538    HTMLTrackElement.LOADING = LOADING;
8539    HTMLTrackElement.LOADED = LOADED;
8540    HTMLTrackElement.ERROR = ERROR;
8541  
8542    /*
8543     * This file contains all track properties that are used in
8544     * player.js, tech.js, html5.js and possibly other techs in the future.
8545     */
8546  
8547    var NORMAL = {
8548      audio: {
8549        ListClass: AudioTrackList,
8550        TrackClass: AudioTrack,
8551        capitalName: 'Audio'
8552      },
8553      video: {
8554        ListClass: VideoTrackList,
8555        TrackClass: VideoTrack,
8556        capitalName: 'Video'
8557      },
8558      text: {
8559        ListClass: TextTrackList,
8560        TrackClass: TextTrack,
8561        capitalName: 'Text'
8562      }
8563    };
8564    Object.keys(NORMAL).forEach(function (type) {
8565      NORMAL[type].getterName = type + "Tracks";
8566      NORMAL[type].privateName = type + "Tracks_";
8567    });
8568    var REMOTE = {
8569      remoteText: {
8570        ListClass: TextTrackList,
8571        TrackClass: TextTrack,
8572        capitalName: 'RemoteText',
8573        getterName: 'remoteTextTracks',
8574        privateName: 'remoteTextTracks_'
8575      },
8576      remoteTextEl: {
8577        ListClass: HtmlTrackElementList,
8578        TrackClass: HTMLTrackElement,
8579        capitalName: 'RemoteTextTrackEls',
8580        getterName: 'remoteTextTrackEls',
8581        privateName: 'remoteTextTrackEls_'
8582      }
8583    };
8584  
8585    var ALL = _extends_1({}, NORMAL, REMOTE);
8586  
8587    REMOTE.names = Object.keys(REMOTE);
8588    NORMAL.names = Object.keys(NORMAL);
8589    ALL.names = [].concat(REMOTE.names).concat(NORMAL.names);
8590  
8591    /**
8592     * Copyright 2013 vtt.js Contributors
8593     *
8594     * Licensed under the Apache License, Version 2.0 (the "License");
8595     * you may not use this file except in compliance with the License.
8596     * You may obtain a copy of the License at
8597     *
8598     *   http://www.apache.org/licenses/LICENSE-2.0
8599     *
8600     * Unless required by applicable law or agreed to in writing, software
8601     * distributed under the License is distributed on an "AS IS" BASIS,
8602     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
8603     * See the License for the specific language governing permissions and
8604     * limitations under the License.
8605     */
8606  
8607    /* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
8608  
8609    /* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */
8610  
8611    var _objCreate = Object.create || function () {
8612      function F() {}
8613  
8614      return function (o) {
8615        if (arguments.length !== 1) {
8616          throw new Error('Object.create shim only accepts one parameter.');
8617        }
8618  
8619        F.prototype = o;
8620        return new F();
8621      };
8622    }(); // Creates a new ParserError object from an errorData object. The errorData
8623    // object should have default code and message properties. The default message
8624    // property can be overriden by passing in a message parameter.
8625    // See ParsingError.Errors below for acceptable errors.
8626  
8627  
8628    function ParsingError(errorData, message) {
8629      this.name = "ParsingError";
8630      this.code = errorData.code;
8631      this.message = message || errorData.message;
8632    }
8633  
8634    ParsingError.prototype = _objCreate(Error.prototype);
8635    ParsingError.prototype.constructor = ParsingError; // ParsingError metadata for acceptable ParsingErrors.
8636  
8637    ParsingError.Errors = {
8638      BadSignature: {
8639        code: 0,
8640        message: "Malformed WebVTT signature."
8641      },
8642      BadTimeStamp: {
8643        code: 1,
8644        message: "Malformed time stamp."
8645      }
8646    }; // Try to parse input as a time stamp.
8647  
8648    function parseTimeStamp(input) {
8649      function computeSeconds(h, m, s, f) {
8650        return (h | 0) * 3600 + (m | 0) * 60 + (s | 0) + (f | 0) / 1000;
8651      }
8652  
8653      var m = input.match(/^(\d+):(\d{1,2})(:\d{1,2})?\.(\d{3})/);
8654  
8655      if (!m) {
8656        return null;
8657      }
8658  
8659      if (m[3]) {
8660        // Timestamp takes the form of [hours]:[minutes]:[seconds].[milliseconds]
8661        return computeSeconds(m[1], m[2], m[3].replace(":", ""), m[4]);
8662      } else if (m[1] > 59) {
8663        // Timestamp takes the form of [hours]:[minutes].[milliseconds]
8664        // First position is hours as it's over 59.
8665        return computeSeconds(m[1], m[2], 0, m[4]);
8666      } else {
8667        // Timestamp takes the form of [minutes]:[seconds].[milliseconds]
8668        return computeSeconds(0, m[1], m[2], m[4]);
8669      }
8670    } // A settings object holds key/value pairs and will ignore anything but the first
8671    // assignment to a specific key.
8672  
8673  
8674    function Settings() {
8675      this.values = _objCreate(null);
8676    }
8677  
8678    Settings.prototype = {
8679      // Only accept the first assignment to any key.
8680      set: function set(k, v) {
8681        if (!this.get(k) && v !== "") {
8682          this.values[k] = v;
8683        }
8684      },
8685      // Return the value for a key, or a default value.
8686      // If 'defaultKey' is passed then 'dflt' is assumed to be an object with
8687      // a number of possible default values as properties where 'defaultKey' is
8688      // the key of the property that will be chosen; otherwise it's assumed to be
8689      // a single value.
8690      get: function get(k, dflt, defaultKey) {
8691        if (defaultKey) {
8692          return this.has(k) ? this.values[k] : dflt[defaultKey];
8693        }
8694  
8695        return this.has(k) ? this.values[k] : dflt;
8696      },
8697      // Check whether we have a value for a key.
8698      has: function has(k) {
8699        return k in this.values;
8700      },
8701      // Accept a setting if its one of the given alternatives.
8702      alt: function alt(k, v, a) {
8703        for (var n = 0; n < a.length; ++n) {
8704          if (v === a[n]) {
8705            this.set(k, v);
8706            break;
8707          }
8708        }
8709      },
8710      // Accept a setting if its a valid (signed) integer.
8711      integer: function integer(k, v) {
8712        if (/^-?\d+$/.test(v)) {
8713          // integer
8714          this.set(k, parseInt(v, 10));
8715        }
8716      },
8717      // Accept a setting if its a valid percentage.
8718      percent: function percent(k, v) {
8719        var m;
8720  
8721        if (m = v.match(/^([\d]{1,3})(\.[\d]*)?%$/)) {
8722          v = parseFloat(v);
8723  
8724          if (v >= 0 && v <= 100) {
8725            this.set(k, v);
8726            return true;
8727          }
8728        }
8729  
8730        return false;
vendor: 30,370 bytes, lines 8731-9592
8731      }
8732    }; // Helper function to parse input into groups separated by 'groupDelim', and
8733    // interprete each group as a key/value pair separated by 'keyValueDelim'.
8734  
8735    function parseOptions(input, callback, keyValueDelim, groupDelim) {
8736      var groups = groupDelim ? input.split(groupDelim) : [input];
8737  
8738      for (var i in groups) {
8739        if (typeof groups[i] !== "string") {
8740          continue;
8741        }
8742  
8743        var kv = groups[i].split(keyValueDelim);
8744  
8745        if (kv.length !== 2) {
8746          continue;
8747        }
8748  
8749        var k = kv[0];
8750        var v = kv[1];
8751        callback(k, v);
8752      }
8753    }
8754  
8755    function parseCue(input, cue, regionList) {
8756      // Remember the original input if we need to throw an error.
8757      var oInput = input; // 4.1 WebVTT timestamp
8758  
8759      function consumeTimeStamp() {
8760        var ts = parseTimeStamp(input);
8761  
8762        if (ts === null) {
8763          throw new ParsingError(ParsingError.Errors.BadTimeStamp, "Malformed timestamp: " + oInput);
8764        } // Remove time stamp from input.
8765  
8766  
8767        input = input.replace(/^[^\sa-zA-Z-]+/, "");
8768        return ts;
8769      } // 4.4.2 WebVTT cue settings
8770  
8771  
8772      function consumeCueSettings(input, cue) {
8773        var settings = new Settings();
8774        parseOptions(input, function (k, v) {
8775          switch (k) {
8776            case "region":
8777              // Find the last region we parsed with the same region id.
8778              for (var i = regionList.length - 1; i >= 0; i--) {
8779                if (regionList[i].id === v) {
8780                  settings.set(k, regionList[i].region);
8781                  break;
8782                }
8783              }
8784  
8785              break;
8786  
8787            case "vertical":
8788              settings.alt(k, v, ["rl", "lr"]);
8789              break;
8790  
8791            case "line":
8792              var vals = v.split(","),
8793                  vals0 = vals[0];
8794              settings.integer(k, vals0);
8795              settings.percent(k, vals0) ? settings.set("snapToLines", false) : null;
8796              settings.alt(k, vals0, ["auto"]);
8797  
8798              if (vals.length === 2) {
8799                settings.alt("lineAlign", vals[1], ["start", "center", "end"]);
8800              }
8801  
8802              break;
8803  
8804            case "position":
8805              vals = v.split(",");
8806              settings.percent(k, vals[0]);
8807  
8808              if (vals.length === 2) {
8809                settings.alt("positionAlign", vals[1], ["start", "center", "end"]);
8810              }
8811  
8812              break;
8813  
8814            case "size":
8815              settings.percent(k, v);
8816              break;
8817  
8818            case "align":
8819              settings.alt(k, v, ["start", "center", "end", "left", "right"]);
8820              break;
8821          }
8822        }, /:/, /\s/); // Apply default values for any missing fields.
8823  
8824        cue.region = settings.get("region", null);
8825        cue.vertical = settings.get("vertical", "");
8826  
8827        try {
8828          cue.line = settings.get("line", "auto");
8829        } catch (e) {}
8830  
8831        cue.lineAlign = settings.get("lineAlign", "start");
8832        cue.snapToLines = settings.get("snapToLines", true);
8833        cue.size = settings.get("size", 100); // Safari still uses the old middle value and won't accept center
8834  
8835        try {
8836          cue.align = settings.get("align", "center");
8837        } catch (e) {
8838          cue.align = settings.get("align", "middle");
8839        }
8840  
8841        try {
8842          cue.position = settings.get("position", "auto");
8843        } catch (e) {
8844          cue.position = settings.get("position", {
8845            start: 0,
8846            left: 0,
8847            center: 50,
8848            middle: 50,
8849            end: 100,
8850            right: 100
8851          }, cue.align);
8852        }
8853  
8854        cue.positionAlign = settings.get("positionAlign", {
8855          start: "start",
8856          left: "start",
8857          center: "center",
8858          middle: "center",
8859          end: "end",
8860          right: "end"
8861        }, cue.align);
8862      }
8863  
8864      function skipWhitespace() {
8865        input = input.replace(/^\s+/, "");
8866      } // 4.1 WebVTT cue timings.
8867  
8868  
8869      skipWhitespace();
8870      cue.startTime = consumeTimeStamp(); // (1) collect cue start time
8871  
8872      skipWhitespace();
8873  
8874      if (input.substr(0, 3) !== "-->") {
8875        // (3) next characters must match "-->"
8876        throw new ParsingError(ParsingError.Errors.BadTimeStamp, "Malformed time stamp (time stamps must be separated by '-->'): " + oInput);
8877      }
8878  
8879      input = input.substr(3);
8880      skipWhitespace();
8881      cue.endTime = consumeTimeStamp(); // (5) collect cue end time
8882      // 4.1 WebVTT cue settings list.
8883  
8884      skipWhitespace();
8885      consumeCueSettings(input, cue);
8886    }
8887  
8888    var TEXTAREA_ELEMENT = document.createElement("textarea");
8889    var TAG_NAME = {
8890      c: "span",
8891      i: "i",
8892      b: "b",
8893      u: "u",
8894      ruby: "ruby",
8895      rt: "rt",
8896      v: "span",
8897      lang: "span"
8898    }; // 5.1 default text color
8899    // 5.2 default text background color is equivalent to text color with bg_ prefix
8900  
8901    var DEFAULT_COLOR_CLASS = {
8902      white: 'rgba(255,255,255,1)',
8903      lime: 'rgba(0,255,0,1)',
8904      cyan: 'rgba(0,255,255,1)',
8905      red: 'rgba(255,0,0,1)',
8906      yellow: 'rgba(255,255,0,1)',
8907      magenta: 'rgba(255,0,255,1)',
8908      blue: 'rgba(0,0,255,1)',
8909      black: 'rgba(0,0,0,1)'
8910    };
8911    var TAG_ANNOTATION = {
8912      v: "title",
8913      lang: "lang"
8914    };
8915    var NEEDS_PARENT = {
8916      rt: "ruby"
8917    }; // Parse content into a document fragment.
8918  
8919    function parseContent(window, input) {
8920      function nextToken() {
8921        // Check for end-of-string.
8922        if (!input) {
8923          return null;
8924        } // Consume 'n' characters from the input.
8925  
8926  
8927        function consume(result) {
8928          input = input.substr(result.length);
8929          return result;
8930        }
8931  
8932        var m = input.match(/^([^<]*)(<[^>]*>?)?/); // If there is some text before the next tag, return it, otherwise return
8933        // the tag.
8934  
8935        return consume(m[1] ? m[1] : m[2]);
8936      }
8937  
8938      function unescape(s) {
8939        TEXTAREA_ELEMENT.innerHTML = s;
8940        s = TEXTAREA_ELEMENT.textContent;
8941        TEXTAREA_ELEMENT.textContent = "";
8942        return s;
8943      }
8944  
8945      function shouldAdd(current, element) {
8946        return !NEEDS_PARENT[element.localName] || NEEDS_PARENT[element.localName] === current.localName;
8947      } // Create an element for this tag.
8948  
8949  
8950      function createElement(type, annotation) {
8951        var tagName = TAG_NAME[type];
8952  
8953        if (!tagName) {
8954          return null;
8955        }
8956  
8957        var element = window.document.createElement(tagName);
8958        var name = TAG_ANNOTATION[type];
8959  
8960        if (name && annotation) {
8961          element[name] = annotation.trim();
8962        }
8963  
8964        return element;
8965      }
8966  
8967      var rootDiv = window.document.createElement("div"),
8968          current = rootDiv,
8969          t,
8970          tagStack = [];
8971  
8972      while ((t = nextToken()) !== null) {
8973        if (t[0] === '<') {
8974          if (t[1] === "/") {
8975            // If the closing tag matches, move back up to the parent node.
8976            if (tagStack.length && tagStack[tagStack.length - 1] === t.substr(2).replace(">", "")) {
8977              tagStack.pop();
8978              current = current.parentNode;
8979            } // Otherwise just ignore the end tag.
8980  
8981  
8982            continue;
8983          }
8984  
8985          var ts = parseTimeStamp(t.substr(1, t.length - 2));
8986          var node;
8987  
8988          if (ts) {
8989            // Timestamps are lead nodes as well.
8990            node = window.document.createProcessingInstruction("timestamp", ts);
8991            current.appendChild(node);
8992            continue;
8993          }
8994  
8995          var m = t.match(/^<([^.\s/0-9>]+)(\.[^\s\\>]+)?([^>\\]+)?(\\?)>?$/); // If we can't parse the tag, skip to the next tag.
8996  
8997          if (!m) {
8998            continue;
8999          } // Try to construct an element, and ignore the tag if we couldn't.
9000  
9001  
9002          node = createElement(m[1], m[3]);
9003  
9004          if (!node) {
9005            continue;
9006          } // Determine if the tag should be added based on the context of where it
9007          // is placed in the cuetext.
9008  
9009  
9010          if (!shouldAdd(current, node)) {
9011            continue;
9012          } // Set the class list (as a list of classes, separated by space).
9013  
9014  
9015          if (m[2]) {
9016            var classes = m[2].split('.');
9017            classes.forEach(function (cl) {
9018              var bgColor = /^bg_/.test(cl); // slice out `bg_` if it's a background color
9019  
9020              var colorName = bgColor ? cl.slice(3) : cl;
9021  
9022              if (DEFAULT_COLOR_CLASS.hasOwnProperty(colorName)) {
9023                var propName = bgColor ? 'background-color' : 'color';
9024                var propValue = DEFAULT_COLOR_CLASS[colorName];
9025                node.style[propName] = propValue;
9026              }
9027            });
9028            node.className = classes.join(' ');
9029          } // Append the node to the current node, and enter the scope of the new
9030          // node.
9031  
9032  
9033          tagStack.push(m[1]);
9034          current.appendChild(node);
9035          current = node;
9036          continue;
9037        } // Text nodes are leaf nodes.
9038  
9039  
9040        current.appendChild(window.document.createTextNode(unescape(t)));
9041      }
9042  
9043      return rootDiv;
9044    } // This is a list of all the Unicode characters that have a strong
9045    // right-to-left category. What this means is that these characters are
9046    // written right-to-left for sure. It was generated by pulling all the strong
9047    // right-to-left characters out of the Unicode data table. That table can
9048    // found at: http://www.unicode.org/Public/UNIDATA/UnicodeData.txt
9049  
9050  
9051    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]];
9052  
9053    function isStrongRTLChar(charCode) {
9054      for (var i = 0; i < strongRTLRanges.length; i++) {
9055        var currentRange = strongRTLRanges[i];
9056  
9057        if (charCode >= currentRange[0] && charCode <= currentRange[1]) {
9058          return true;
9059        }
9060      }
9061  
9062      return false;
9063    }
9064  
9065    function determineBidi(cueDiv) {
9066      var nodeStack = [],
9067          text = "",
9068          charCode;
9069  
9070      if (!cueDiv || !cueDiv.childNodes) {
9071        return "ltr";
9072      }
9073  
9074      function pushNodes(nodeStack, node) {
9075        for (var i = node.childNodes.length - 1; i >= 0; i--) {
9076          nodeStack.push(node.childNodes[i]);
9077        }
9078      }
9079  
9080      function nextTextNode(nodeStack) {
9081        if (!nodeStack || !nodeStack.length) {
9082          return null;
9083        }
9084  
9085        var node = nodeStack.pop(),
9086            text = node.textContent || node.innerText;
9087  
9088        if (text) {
9089          // TODO: This should match all unicode type B characters (paragraph
9090          // separator characters). See issue #115.
9091          var m = text.match(/^.*(\n|\r)/);
9092  
9093          if (m) {
9094            nodeStack.length = 0;
9095            return m[0];
9096          }
9097  
9098          return text;
9099        }
9100  
9101        if (node.tagName === "ruby") {
9102          return nextTextNode(nodeStack);
9103        }
9104  
9105        if (node.childNodes) {
9106          pushNodes(nodeStack, node);
9107          return nextTextNode(nodeStack);
9108        }
9109      }
9110  
9111      pushNodes(nodeStack, cueDiv);
9112  
9113      while (text = nextTextNode(nodeStack)) {
9114        for (var i = 0; i < text.length; i++) {
9115          charCode = text.charCodeAt(i);
9116  
9117          if (isStrongRTLChar(charCode)) {
9118            return "rtl";
9119          }
9120        }
9121      }
9122  
9123      return "ltr";
9124    }
9125  
9126    function computeLinePos(cue) {
9127      if (typeof cue.line === "number" && (cue.snapToLines || cue.line >= 0 && cue.line <= 100)) {
9128        return cue.line;
9129      }
9130  
9131      if (!cue.track || !cue.track.textTrackList || !cue.track.textTrackList.mediaElement) {
9132        return -1;
9133      }
9134  
9135      var track = cue.track,
9136          trackList = track.textTrackList,
9137          count = 0;
9138  
9139      for (var i = 0; i < trackList.length && trackList[i] !== track; i++) {
9140        if (trackList[i].mode === "showing") {
9141          count++;
9142        }
9143      }
9144  
9145      return ++count * -1;
9146    }
9147  
9148    function StyleBox() {} // Apply styles to a div. If there is no div passed then it defaults to the
9149    // div on 'this'.
9150  
9151  
9152    StyleBox.prototype.applyStyles = function (styles, div) {
9153      div = div || this.div;
9154  
9155      for (var prop in styles) {
9156        if (styles.hasOwnProperty(prop)) {
9157          div.style[prop] = styles[prop];
9158        }
9159      }
9160    };
9161  
9162    StyleBox.prototype.formatStyle = function (val, unit) {
9163      return val === 0 ? 0 : val + unit;
9164    }; // Constructs the computed display state of the cue (a div). Places the div
9165    // into the overlay which should be a block level element (usually a div).
9166  
9167  
9168    function CueStyleBox(window, cue, styleOptions) {
9169      StyleBox.call(this);
9170      this.cue = cue; // Parse our cue's text into a DOM tree rooted at 'cueDiv'. This div will
9171      // have inline positioning and will function as the cue background box.
9172  
9173      this.cueDiv = parseContent(window, cue.text);
9174      var styles = {
9175        color: "rgba(255, 255, 255, 1)",
9176        backgroundColor: "rgba(0, 0, 0, 0.8)",
9177        position: "relative",
9178        left: 0,
9179        right: 0,
9180        top: 0,
9181        bottom: 0,
9182        display: "inline",
9183        writingMode: cue.vertical === "" ? "horizontal-tb" : cue.vertical === "lr" ? "vertical-lr" : "vertical-rl",
9184        unicodeBidi: "plaintext"
9185      };
9186      this.applyStyles(styles, this.cueDiv); // Create an absolutely positioned div that will be used to position the cue
9187      // div. Note, all WebVTT cue-setting alignments are equivalent to the CSS
9188      // mirrors of them except middle instead of center on Safari.
9189  
9190      this.div = window.document.createElement("div");
9191      styles = {
9192        direction: determineBidi(this.cueDiv),
9193        writingMode: cue.vertical === "" ? "horizontal-tb" : cue.vertical === "lr" ? "vertical-lr" : "vertical-rl",
9194        unicodeBidi: "plaintext",
9195        textAlign: cue.align === "middle" ? "center" : cue.align,
9196        font: styleOptions.font,
9197        whiteSpace: "pre-line",
9198        position: "absolute"
9199      };
9200      this.applyStyles(styles);
9201      this.div.appendChild(this.cueDiv); // Calculate the distance from the reference edge of the viewport to the text
9202      // position of the cue box. The reference edge will be resolved later when
9203      // the box orientation styles are applied.
9204  
9205      var textPos = 0;
9206  
9207      switch (cue.positionAlign) {
9208        case "start":
9209          textPos = cue.position;
9210          break;
9211  
9212        case "center":
9213          textPos = cue.position - cue.size / 2;
9214          break;
9215  
9216        case "end":
9217          textPos = cue.position - cue.size;
9218          break;
9219      } // Horizontal box orientation; textPos is the distance from the left edge of the
9220      // area to the left edge of the box and cue.size is the distance extending to
9221      // the right from there.
9222  
9223  
9224      if (cue.vertical === "") {
9225        this.applyStyles({
9226          left: this.formatStyle(textPos, "%"),
9227          width: this.formatStyle(cue.size, "%")
9228        }); // Vertical box orientation; textPos is the distance from the top edge of the
9229        // area to the top edge of the box and cue.size is the height extending
9230        // downwards from there.
9231      } else {
9232        this.applyStyles({
9233          top: this.formatStyle(textPos, "%"),
9234          height: this.formatStyle(cue.size, "%")
9235        });
9236      }
9237  
9238      this.move = function (box) {
9239        this.applyStyles({
9240          top: this.formatStyle(box.top, "px"),
9241          bottom: this.formatStyle(box.bottom, "px"),
9242          left: this.formatStyle(box.left, "px"),
9243          right: this.formatStyle(box.right, "px"),
9244          height: this.formatStyle(box.height, "px"),
9245          width: this.formatStyle(box.width, "px")
9246        });
9247      };
9248    }
9249  
9250    CueStyleBox.prototype = _objCreate(StyleBox.prototype);
9251    CueStyleBox.prototype.constructor = CueStyleBox; // Represents the co-ordinates of an Element in a way that we can easily
9252    // compute things with such as if it overlaps or intersects with another Element.
9253    // Can initialize it with either a StyleBox or another BoxPosition.
9254  
9255    function BoxPosition(obj) {
9256      // Either a BoxPosition was passed in and we need to copy it, or a StyleBox
9257      // was passed in and we need to copy the results of 'getBoundingClientRect'
9258      // as the object returned is readonly. All co-ordinate values are in reference
9259      // to the viewport origin (top left).
9260      var lh, height, width, top;
9261  
9262      if (obj.div) {
9263        height = obj.div.offsetHeight;
9264        width = obj.div.offsetWidth;
9265        top = obj.div.offsetTop;
9266        var rects = (rects = obj.div.childNodes) && (rects = rects[0]) && rects.getClientRects && rects.getClientRects();
9267        obj = obj.div.getBoundingClientRect(); // In certain cases the outter div will be slightly larger then the sum of
9268        // the inner div's lines. This could be due to bold text, etc, on some platforms.
9269        // In this case we should get the average line height and use that. This will
9270        // result in the desired behaviour.
9271  
9272        lh = rects ? Math.max(rects[0] && rects[0].height || 0, obj.height / rects.length) : 0;
9273      }
9274  
9275      this.left = obj.left;
9276      this.right = obj.right;
9277      this.top = obj.top || top;
9278      this.height = obj.height || height;
9279      this.bottom = obj.bottom || top + (obj.height || height);
9280      this.width = obj.width || width;
9281      this.lineHeight = lh !== undefined ? lh : obj.lineHeight;
9282    } // Move the box along a particular axis. Optionally pass in an amount to move
9283    // the box. If no amount is passed then the default is the line height of the
9284    // box.
9285  
9286  
9287    BoxPosition.prototype.move = function (axis, toMove) {
9288      toMove = toMove !== undefined ? toMove : this.lineHeight;
9289  
9290      switch (axis) {
9291        case "+x":
9292          this.left += toMove;
9293          this.right += toMove;
9294          break;
9295  
9296        case "-x":
9297          this.left -= toMove;
9298          this.right -= toMove;
9299          break;
9300  
9301        case "+y":
9302          this.top += toMove;
9303          this.bottom += toMove;
9304          break;
9305  
9306        case "-y":
9307          this.top -= toMove;
9308          this.bottom -= toMove;
9309          break;
9310      }
9311    }; // Check if this box overlaps another box, b2.
9312  
9313  
9314    BoxPosition.prototype.overlaps = function (b2) {
9315      return this.left < b2.right && this.right > b2.left && this.top < b2.bottom && this.bottom > b2.top;
9316    }; // Check if this box overlaps any other boxes in boxes.
9317  
9318  
9319    BoxPosition.prototype.overlapsAny = function (boxes) {
9320      for (var i = 0; i < boxes.length; i++) {
9321        if (this.overlaps(boxes[i])) {
9322          return true;
9323        }
9324      }
9325  
9326      return false;
9327    }; // Check if this box is within another box.
9328  
9329  
9330    BoxPosition.prototype.within = function (container) {
9331      return this.top >= container.top && this.bottom <= container.bottom && this.left >= container.left && this.right <= container.right;
9332    }; // Check if this box is entirely within the container or it is overlapping
9333    // on the edge opposite of the axis direction passed. For example, if "+x" is
9334    // passed and the box is overlapping on the left edge of the container, then
9335    // return true.
9336  
9337  
9338    BoxPosition.prototype.overlapsOppositeAxis = function (container, axis) {
9339      switch (axis) {
9340        case "+x":
9341          return this.left < container.left;
9342  
9343        case "-x":
9344          return this.right > container.right;
9345  
9346        case "+y":
9347          return this.top < container.top;
9348  
9349        case "-y":
9350          return this.bottom > container.bottom;
9351      }
9352    }; // Find the percentage of the area that this box is overlapping with another
9353    // box.
9354  
9355  
9356    BoxPosition.prototype.intersectPercentage = function (b2) {
9357      var x = Math.max(0, Math.min(this.right, b2.right) - Math.max(this.left, b2.left)),
9358          y = Math.max(0, Math.min(this.bottom, b2.bottom) - Math.max(this.top, b2.top)),
9359          intersectArea = x * y;
9360      return intersectArea / (this.height * this.width);
9361    }; // Convert the positions from this box to CSS compatible positions using
9362    // the reference container's positions. This has to be done because this
9363    // box's positions are in reference to the viewport origin, whereas, CSS
9364    // values are in referecne to their respective edges.
9365  
9366  
9367    BoxPosition.prototype.toCSSCompatValues = function (reference) {
9368      return {
9369        top: this.top - reference.top,
9370        bottom: reference.bottom - this.bottom,
9371        left: this.left - reference.left,
9372        right: reference.right - this.right,
9373        height: this.height,
9374        width: this.width
9375      };
9376    }; // Get an object that represents the box's position without anything extra.
9377    // Can pass a StyleBox, HTMLElement, or another BoxPositon.
9378  
9379  
9380    BoxPosition.getSimpleBoxPosition = function (obj) {
9381      var height = obj.div ? obj.div.offsetHeight : obj.tagName ? obj.offsetHeight : 0;
9382      var width = obj.div ? obj.div.offsetWidth : obj.tagName ? obj.offsetWidth : 0;
9383      var top = obj.div ? obj.div.offsetTop : obj.tagName ? obj.offsetTop : 0;
9384      obj = obj.div ? obj.div.getBoundingClientRect() : obj.tagName ? obj.getBoundingClientRect() : obj;
9385      var ret = {
9386        left: obj.left,
9387        right: obj.right,
9388        top: obj.top || top,
9389        height: obj.height || height,
9390        bottom: obj.bottom || top + (obj.height || height),
9391        width: obj.width || width
9392      };
9393      return ret;
9394    }; // Move a StyleBox to its specified, or next best, position. The containerBox
9395    // is the box that contains the StyleBox, such as a div. boxPositions are
9396    // a list of other boxes that the styleBox can't overlap with.
9397  
9398  
9399    function moveBoxToLinePosition(window, styleBox, containerBox, boxPositions) {
9400      // Find the best position for a cue box, b, on the video. The axis parameter
9401      // is a list of axis, the order of which, it will move the box along. For example:
9402      // Passing ["+x", "-x"] will move the box first along the x axis in the positive
9403      // direction. If it doesn't find a good position for it there it will then move
9404      // it along the x axis in the negative direction.
9405      function findBestPosition(b, axis) {
9406        var bestPosition,
9407            specifiedPosition = new BoxPosition(b),
9408            percentage = 1; // Highest possible so the first thing we get is better.
9409  
9410        for (var i = 0; i < axis.length; i++) {
9411          while (b.overlapsOppositeAxis(containerBox, axis[i]) || b.within(containerBox) && b.overlapsAny(boxPositions)) {
9412            b.move(axis[i]);
9413          } // We found a spot where we aren't overlapping anything. This is our
9414          // best position.
9415  
9416  
9417          if (b.within(containerBox)) {
9418            return b;
9419          }
9420  
9421          var p = b.intersectPercentage(containerBox); // If we're outside the container box less then we were on our last try
9422          // then remember this position as the best position.
9423  
9424          if (percentage > p) {
9425            bestPosition = new BoxPosition(b);
9426            percentage = p;
9427          } // Reset the box position to the specified position.
9428  
9429  
9430          b = new BoxPosition(specifiedPosition);
9431        }
9432  
9433        return bestPosition || specifiedPosition;
9434      }
9435  
9436      var boxPosition = new BoxPosition(styleBox),
9437          cue = styleBox.cue,
9438          linePos = computeLinePos(cue),
9439          axis = []; // If we have a line number to align the cue to.
9440  
9441      if (cue.snapToLines) {
9442        var size;
9443  
9444        switch (cue.vertical) {
9445          case "":
9446            axis = ["+y", "-y"];
9447            size = "height";
9448            break;
9449  
9450          case "rl":
9451            axis = ["+x", "-x"];
9452            size = "width";
9453            break;
9454  
9455          case "lr":
9456            axis = ["-x", "+x"];
9457            size = "width";
9458            break;
9459        }
9460  
9461        var step = boxPosition.lineHeight,
9462            position = step * Math.round(linePos),
9463            maxPosition = containerBox[size] + step,
9464            initialAxis = axis[0]; // If the specified intial position is greater then the max position then
9465        // clamp the box to the amount of steps it would take for the box to
9466        // reach the max position.
9467  
9468        if (Math.abs(position) > maxPosition) {
9469          position = position < 0 ? -1 : 1;
9470          position *= Math.ceil(maxPosition / step) * step;
9471        } // If computed line position returns negative then line numbers are
9472        // relative to the bottom of the video instead of the top. Therefore, we
9473        // need to increase our initial position by the length or width of the
9474        // video, depending on the writing direction, and reverse our axis directions.
9475  
9476  
9477        if (linePos < 0) {
9478          position += cue.vertical === "" ? containerBox.height : containerBox.width;
9479          axis = axis.reverse();
9480        } // Move the box to the specified position. This may not be its best
9481        // position.
9482  
9483  
9484        boxPosition.move(initialAxis, position);
9485      } else {
9486        // If we have a percentage line value for the cue.
9487        var calculatedPercentage = boxPosition.lineHeight / containerBox.height * 100;
9488  
9489        switch (cue.lineAlign) {
9490          case "center":
9491            linePos -= calculatedPercentage / 2;
9492            break;
9493  
9494          case "end":
9495            linePos -= calculatedPercentage;
9496            break;
9497        } // Apply initial line position to the cue box.
9498  
9499  
9500        switch (cue.vertical) {
9501          case "":
9502            styleBox.applyStyles({
9503              top: styleBox.formatStyle(linePos, "%")
9504            });
9505            break;
9506  
9507          case "rl":
9508            styleBox.applyStyles({
9509              left: styleBox.formatStyle(linePos, "%")
9510            });
9511            break;
9512  
9513          case "lr":
9514            styleBox.applyStyles({
9515              right: styleBox.formatStyle(linePos, "%")
9516            });
9517            break;
9518        }
9519  
9520        axis = ["+y", "-x", "+x", "-y"]; // Get the box position again after we've applied the specified positioning
9521        // to it.
9522  
9523        boxPosition = new BoxPosition(styleBox);
9524      }
9525  
9526      var bestPosition = findBestPosition(boxPosition, axis);
9527      styleBox.move(bestPosition.toCSSCompatValues(containerBox));
9528    }
9529  
9530    function WebVTT$1() {} // Nothing
9531    // Helper to allow strings to be decoded instead of the default binary utf8 data.
9532  
9533  
9534    WebVTT$1.StringDecoder = function () {
9535      return {
9536        decode: function decode(data) {
9537          if (!data) {
9538            return "";
9539          }
9540  
9541          if (typeof data !== "string") {
9542            throw new Error("Error - expected string data.");
9543          }
9544  
9545          return decodeURIComponent(encodeURIComponent(data));
9546        }
9547      };
9548    };
9549  
9550    WebVTT$1.convertCueToDOMTree = function (window, cuetext) {
9551      if (!window || !cuetext) {
9552        return null;
9553      }
9554  
9555      return parseContent(window, cuetext);
9556    };
9557  
9558    var FONT_SIZE_PERCENT = 0.05;
9559    var FONT_STYLE = "sans-serif";
9560    var CUE_BACKGROUND_PADDING = "1.5%"; // Runs the processing model over the cues and regions passed to it.
9561    // @param overlay A block level element (usually a div) that the computed cues
9562    //                and regions will be placed into.
9563  
9564    WebVTT$1.processCues = function (window, cues, overlay) {
9565      if (!window || !cues || !overlay) {
9566        return null;
9567      } // Remove all previous children.
9568  
9569  
9570      while (overlay.firstChild) {
9571        overlay.removeChild(overlay.firstChild);
9572      }
9573  
9574      var paddedOverlay = window.document.createElement("div");
9575      paddedOverlay.style.position = "absolute";
9576      paddedOverlay.style.left = "0";
9577      paddedOverlay.style.right = "0";
9578      paddedOverlay.style.top = "0";
9579      paddedOverlay.style.bottom = "0";
9580      paddedOverlay.style.margin = CUE_BACKGROUND_PADDING;
9581      overlay.appendChild(paddedOverlay); // Determine if we need to compute the display states of the cues. This could
9582      // be the case if a cue's state has been changed since the last computation or
9583      // if it has not been computed yet.
9584  
9585      function shouldCompute(cues) {
9586        for (var i = 0; i < cues.length; i++) {
9587          if (cues[i].hasBeenReset || !cues[i].displayState) {
9588            return true;
9589          }
9590        }
9591  
9592        return false;
vendor: 5,444 bytes, lines 9593-9758
9593      } // We don't need to recompute the cues' display states. Just reuse them.
9594  
9595  
9596      if (!shouldCompute(cues)) {
9597        for (var i = 0; i < cues.length; i++) {
9598          paddedOverlay.appendChild(cues[i].displayState);
9599        }
9600  
9601        return;
9602      }
9603  
9604      var boxPositions = [],
9605          containerBox = BoxPosition.getSimpleBoxPosition(paddedOverlay),
9606          fontSize = Math.round(containerBox.height * FONT_SIZE_PERCENT * 100) / 100;
9607      var styleOptions = {
9608        font: fontSize + "px " + FONT_STYLE
9609      };
9610  
9611      (function () {
9612        var styleBox, cue;
9613  
9614        for (var i = 0; i < cues.length; i++) {
9615          cue = cues[i]; // Compute the intial position and styles of the cue div.
9616  
9617          styleBox = new CueStyleBox(window, cue, styleOptions);
9618          paddedOverlay.appendChild(styleBox.div); // Move the cue div to it's correct line position.
9619  
9620          moveBoxToLinePosition(window, styleBox, containerBox, boxPositions); // Remember the computed div so that we don't have to recompute it later
9621          // if we don't have too.
9622  
9623          cue.displayState = styleBox.div;
9624          boxPositions.push(BoxPosition.getSimpleBoxPosition(styleBox));
9625        }
9626      })();
9627    };
9628  
9629    WebVTT$1.Parser = function (window, vttjs, decoder) {
9630      if (!decoder) {
9631        decoder = vttjs;
9632        vttjs = {};
9633      }
9634  
9635      if (!vttjs) {
9636        vttjs = {};
9637      }
9638  
9639      this.window = window;
9640      this.vttjs = vttjs;
9641      this.state = "INITIAL";
9642      this.buffer = "";
9643      this.decoder = decoder || new TextDecoder("utf8");
9644      this.regionList = [];
9645    };
9646  
9647    WebVTT$1.Parser.prototype = {
9648      // If the error is a ParsingError then report it to the consumer if
9649      // possible. If it's not a ParsingError then throw it like normal.
9650      reportOrThrowError: function reportOrThrowError(e) {
9651        if (e instanceof ParsingError) {
9652          this.onparsingerror && this.onparsingerror(e);
9653        } else {
9654          throw e;
9655        }
9656      },
9657      parse: function parse(data) {
9658        var self = this; // If there is no data then we won't decode it, but will just try to parse
9659        // whatever is in buffer already. This may occur in circumstances, for
9660        // example when flush() is called.
9661  
9662        if (data) {
9663          // Try to decode the data that we received.
9664          self.buffer += self.decoder.decode(data, {
9665            stream: true
9666          });
9667        }
9668  
9669        function collectNextLine() {
9670          var buffer = self.buffer;
9671          var pos = 0;
9672  
9673          while (pos < buffer.length && buffer[pos] !== '\r' && buffer[pos] !== '\n') {
9674            ++pos;
9675          }
9676  
9677          var line = buffer.substr(0, pos); // Advance the buffer early in case we fail below.
9678  
9679          if (buffer[pos] === '\r') {
9680            ++pos;
9681          }
9682  
9683          if (buffer[pos] === '\n') {
9684            ++pos;
9685          }
9686  
9687          self.buffer = buffer.substr(pos);
9688          return line;
9689        } // 3.4 WebVTT region and WebVTT region settings syntax
9690  
9691  
9692        function parseRegion(input) {
9693          var settings = new Settings();
9694          parseOptions(input, function (k, v) {
9695            switch (k) {
9696              case "id":
9697                settings.set(k, v);
9698                break;
9699  
9700              case "width":
9701                settings.percent(k, v);
9702                break;
9703  
9704              case "lines":
9705                settings.integer(k, v);
9706                break;
9707  
9708              case "regionanchor":
9709              case "viewportanchor":
9710                var xy = v.split(',');
9711  
9712                if (xy.length !== 2) {
9713                  break;
9714                } // We have to make sure both x and y parse, so use a temporary
9715                // settings object here.
9716  
9717  
9718                var anchor = new Settings();
9719                anchor.percent("x", xy[0]);
9720                anchor.percent("y", xy[1]);
9721  
9722                if (!anchor.has("x") || !anchor.has("y")) {
9723                  break;
9724                }
9725  
9726                settings.set(k + "X", anchor.get("x"));
9727                settings.set(k + "Y", anchor.get("y"));
9728                break;
9729  
9730              case "scroll":
9731                settings.alt(k, v, ["up"]);
9732                break;
9733            }
9734          }, /=/, /\s/); // Create the region, using default values for any values that were not
9735          // specified.
9736  
9737          if (settings.has("id")) {
9738            var region = new (self.vttjs.VTTRegion || self.window.VTTRegion)();
9739            region.width = settings.get("width", 100);
9740            region.lines = settings.get("lines", 3);
9741            region.regionAnchorX = settings.get("regionanchorX", 0);
9742            region.regionAnchorY = settings.get("regionanchorY", 100);
9743            region.viewportAnchorX = settings.get("viewportanchorX", 0);
9744            region.viewportAnchorY = settings.get("viewportanchorY", 100);
9745            region.scroll = settings.get("scroll", ""); // Register the region.
9746  
9747            self.onregion && self.onregion(region); // Remember the VTTRegion for later in case we parse any VTTCues that
9748            // reference it.
9749  
9750            self.regionList.push({
9751              id: settings.get("id"),
9752              region: region
9753            });
9754          }
9755        } // draft-pantos-http-live-streaming-20
9756        // https://tools.ietf.org/html/draft-pantos-http-live-streaming-20#section-3.5
9757        // 3.5 WebVTT
9758  
vendor: 10,158 bytes, lines 9759-10092
9759  
9760        function parseTimestampMap(input) {
9761          var settings = new Settings();
9762          parseOptions(input, function (k, v) {
9763            switch (k) {
9764              case "MPEGT":
9765                settings.integer(k + 'S', v);
9766                break;
9767  
9768              case "LOCA":
9769                settings.set(k + 'L', parseTimeStamp(v));
9770                break;
9771            }
9772          }, /[^\d]:/, /,/);
9773          self.ontimestampmap && self.ontimestampmap({
9774            "MPEGTS": settings.get("MPEGTS"),
9775            "LOCAL": settings.get("LOCAL")
9776          });
9777        } // 3.2 WebVTT metadata header syntax
9778  
9779  
9780        function parseHeader(input) {
9781          if (input.match(/X-TIMESTAMP-MAP/)) {
9782            // This line contains HLS X-TIMESTAMP-MAP metadata
9783            parseOptions(input, function (k, v) {
9784              switch (k) {
9785                case "X-TIMESTAMP-MAP":
9786                  parseTimestampMap(v);
9787                  break;
9788              }
9789            }, /=/);
9790          } else {
9791            parseOptions(input, function (k, v) {
9792              switch (k) {
9793                case "Region":
9794                  // 3.3 WebVTT region metadata header syntax
9795                  parseRegion(v);
9796                  break;
9797              }
9798            }, /:/);
9799          }
9800        } // 5.1 WebVTT file parsing.
9801  
9802  
9803        try {
9804          var line;
9805  
9806          if (self.state === "INITIAL") {
9807            // We can't start parsing until we have the first line.
9808            if (!/\r\n|\n/.test(self.buffer)) {
9809              return this;
9810            }
9811  
9812            line = collectNextLine();
9813            var m = line.match(/^WEBVTT([ \t].*)?$/);
9814  
9815            if (!m || !m[0]) {
9816              throw new ParsingError(ParsingError.Errors.BadSignature);
9817            }
9818  
9819            self.state = "HEADER";
9820          }
9821  
9822          var alreadyCollectedLine = false;
9823  
9824          while (self.buffer) {
9825            // We can't parse a line until we have the full line.
9826            if (!/\r\n|\n/.test(self.buffer)) {
9827              return this;
9828            }
9829  
9830            if (!alreadyCollectedLine) {
9831              line = collectNextLine();
9832            } else {
9833              alreadyCollectedLine = false;
9834            }
9835  
9836            switch (self.state) {
9837              case "HEADER":
9838                // 13-18 - Allow a header (metadata) under the WEBVTT line.
9839                if (/:/.test(line)) {
9840                  parseHeader(line);
9841                } else if (!line) {
9842                  // An empty line terminates the header and starts the body (cues).
9843                  self.state = "ID";
9844                }
9845  
9846                continue;
9847  
9848              case "NOTE":
9849                // Ignore NOTE blocks.
9850                if (!line) {
9851                  self.state = "ID";
9852                }
9853  
9854                continue;
9855  
9856              case "ID":
9857                // Check for the start of NOTE blocks.
9858                if (/^NOTE($|[ \t])/.test(line)) {
9859                  self.state = "NOTE";
9860                  break;
9861                } // 19-29 - Allow any number of line terminators, then initialize new cue values.
9862  
9863  
9864                if (!line) {
9865                  continue;
9866                }
9867  
9868                self.cue = new (self.vttjs.VTTCue || self.window.VTTCue)(0, 0, ""); // Safari still uses the old middle value and won't accept center
9869  
9870                try {
9871                  self.cue.align = "center";
9872                } catch (e) {
9873                  self.cue.align = "middle";
9874                }
9875  
9876                self.state = "CUE"; // 30-39 - Check if self line contains an optional identifier or timing data.
9877  
9878                if (line.indexOf("-->") === -1) {
9879                  self.cue.id = line;
9880                  continue;
9881                }
9882  
9883              // Process line as start of a cue.
9884  
9885              /*falls through*/
9886  
9887              case "CUE":
9888                // 40 - Collect cue timings and settings.
9889                try {
9890                  parseCue(line, self.cue, self.regionList);
9891                } catch (e) {
9892                  self.reportOrThrowError(e); // In case of an error ignore rest of the cue.
9893  
9894                  self.cue = null;
9895                  self.state = "BADCUE";
9896                  continue;
9897                }
9898  
9899                self.state = "CUETEXT";
9900                continue;
9901  
9902              case "CUETEXT":
9903                var hasSubstring = line.indexOf("-->") !== -1; // 34 - If we have an empty line then report the cue.
9904                // 35 - If we have the special substring '-->' then report the cue,
9905                // but do not collect the line as we need to process the current
9906                // one as a new cue.
9907  
9908                if (!line || hasSubstring && (alreadyCollectedLine = true)) {
9909                  // We are done parsing self cue.
9910                  self.oncue && self.oncue(self.cue);
9911                  self.cue = null;
9912                  self.state = "ID";
9913                  continue;
9914                }
9915  
9916                if (self.cue.text) {
9917                  self.cue.text += "\n";
9918                }
9919  
9920                self.cue.text += line.replace(/\u2028/g, '\n').replace(/u2029/g, '\n');
9921                continue;
9922  
9923              case "BADCUE":
9924                // BADCUE
9925                // 54-62 - Collect and discard the remaining cue.
9926                if (!line) {
9927                  self.state = "ID";
9928                }
9929  
9930                continue;
9931            }
9932          }
9933        } catch (e) {
9934          self.reportOrThrowError(e); // If we are currently parsing a cue, report what we have.
9935  
9936          if (self.state === "CUETEXT" && self.cue && self.oncue) {
9937            self.oncue(self.cue);
9938          }
9939  
9940          self.cue = null; // Enter BADWEBVTT state if header was not parsed correctly otherwise
9941          // another exception occurred so enter BADCUE state.
9942  
9943          self.state = self.state === "INITIAL" ? "BADWEBVTT" : "BADCUE";
9944        }
9945  
9946        return this;
9947      },
9948      flush: function flush() {
9949        var self = this;
9950  
9951        try {
9952          // Finish decoding the stream.
9953          self.buffer += self.decoder.decode(); // Synthesize the end of the current cue or region.
9954  
9955          if (self.cue || self.state === "HEADER") {
9956            self.buffer += "\n\n";
9957            self.parse();
9958          } // If we've flushed, parsed, and we're still on the INITIAL state then
9959          // that means we don't have enough of the stream to parse the first
9960          // line.
9961  
9962  
9963          if (self.state === "INITIAL") {
9964            throw new ParsingError(ParsingError.Errors.BadSignature);
9965          }
9966        } catch (e) {
9967          self.reportOrThrowError(e);
9968        }
9969  
9970        self.onflush && self.onflush();
9971        return this;
9972      }
9973    };
9974    var vtt = WebVTT$1;
9975  
9976    /**
9977     * Copyright 2013 vtt.js Contributors
9978     *
9979     * Licensed under the Apache License, Version 2.0 (the "License");
9980     * you may not use this file except in compliance with the License.
9981     * You may obtain a copy of the License at
9982     *
9983     *   http://www.apache.org/licenses/LICENSE-2.0
9984     *
9985     * Unless required by applicable law or agreed to in writing, software
9986     * distributed under the License is distributed on an "AS IS" BASIS,
9987     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
9988     * See the License for the specific language governing permissions and
9989     * limitations under the License.
9990     */
9991    var autoKeyword = "auto";
9992    var directionSetting = {
9993      "": 1,
9994      "lr": 1,
9995      "rl": 1
9996    };
9997    var alignSetting = {
9998      "start": 1,
9999      "center": 1,
10000      "end": 1,
10001      "left": 1,
10002      "right": 1,
10003      "auto": 1,
10004      "line-left": 1,
10005      "line-right": 1
10006    };
10007  
10008    function findDirectionSetting(value) {
10009      if (typeof value !== "string") {
10010        return false;
10011      }
10012  
10013      var dir = directionSetting[value.toLowerCase()];
10014      return dir ? value.toLowerCase() : false;
10015    }
10016  
10017    function findAlignSetting(value) {
10018      if (typeof value !== "string") {
10019        return false;
10020      }
10021  
10022      var align = alignSetting[value.toLowerCase()];
10023      return align ? value.toLowerCase() : false;
10024    }
10025  
10026    function VTTCue(startTime, endTime, text) {
10027      /**
10028       * Shim implementation specific properties. These properties are not in
10029       * the spec.
10030       */
10031      // Lets us know when the VTTCue's data has changed in such a way that we need
10032      // to recompute its display state. This lets us compute its display state
10033      // lazily.
10034      this.hasBeenReset = false;
10035      /**
10036       * VTTCue and TextTrackCue properties
10037       * http://dev.w3.org/html5/webvtt/#vttcue-interface
10038       */
10039  
10040      var _id = "";
10041      var _pauseOnExit = false;
10042      var _startTime = startTime;
10043      var _endTime = endTime;
10044      var _text = text;
10045      var _region = null;
10046      var _vertical = "";
10047      var _snapToLines = true;
10048      var _line = "auto";
10049      var _lineAlign = "start";
10050      var _position = "auto";
10051      var _positionAlign = "auto";
10052      var _size = 100;
10053      var _align = "center";
10054      Object.defineProperties(this, {
10055        "id": {
10056          enumerable: true,
10057          get: function get() {
10058            return _id;
10059          },
10060          set: function set(value) {
10061            _id = "" + value;
10062          }
10063        },
10064        "pauseOnExit": {
10065          enumerable: true,
10066          get: function get() {
10067            return _pauseOnExit;
10068          },
10069          set: function set(value) {
10070            _pauseOnExit = !!value;
10071          }
10072        },
10073        "startTime": {
10074          enumerable: true,
10075          get: function get() {
10076            return _startTime;
10077          },
10078          set: function set(value) {
10079            if (typeof value !== "number") {
10080              throw new TypeError("Start time must be set to a number.");
10081            }
10082  
10083            _startTime = value;
10084            this.hasBeenReset = true;
10085          }
10086        },
10087        "endTime": {
10088          enumerable: true,
10089          get: function get() {
10090            return _endTime;
10091          },
10092          set: function set(value) {
10093            if (typeof value !== "number") {
10094              throw new TypeError("End time must be set to a number.");
10095            }
10096  
10097            _endTime = value;
10098            this.hasBeenReset = true;
10099          }
10100        },
10101        "text": {
10102          enumerable: true,
10103          get: function get() {
10104            return _text;
10105          },
10106          set: function set(value) {
10107            _text = "" + value;
10108            this.hasBeenReset = true;
10109          }
10110        },
10111        "region": {
10112          enumerable: true,
10113          get: function get() {
10114            return _region;
10115          },
10116          set: function set(value) {
10117            _region = value;
10118            this.hasBeenReset = true;
10119          }
10120        },
10121        "vertical": {
10122          enumerable: true,
10123          get: function get() {
10124            return _vertical;
10125          },
10126          set: function set(value) {
10127            var setting = findDirectionSetting(value); // Have to check for false because the setting an be an empty string.
10128  
10129            if (setting === false) {
10130              throw new SyntaxError("Vertical: an invalid or illegal direction string was specified.");
10131            }
10132  
10133            _vertical = setting;
10134            this.hasBeenReset = true;
10135          }
10136        },
10137        "snapToLines": {
10138          enumerable: true,
10139          get: function get() {
10140            return _snapToLines;
10141          },
10142          set: function set(value) {
10143            _snapToLines = !!value;
10144            this.hasBeenReset = true;
10145          }
10146        },
10147        "line": {
10148          enumerable: true,
10149          get: function get() {
10150            return _line;
10151          },
10152          set: function set(value) {
10153            if (typeof value !== "number" && value !== autoKeyword) {
10154              throw new SyntaxError("Line: an invalid number or illegal string was specified.");
10155            }
10156  
10157            _line = value;
10158            this.hasBeenReset = true;
10159          }
10160        },
10161        "lineAlign": {
10162          enumerable: true,
10163          get: function get() {
10164            return _lineAlign;
10165          },
10166          set: function set(value) {
10167            var setting = findAlignSetting(value);
10168  
10169            if (!setting) {
10170              console.warn("lineAlign: an invalid or illegal string was specified.");
10171            } else {
10172              _lineAlign = setting;
10173              this.hasBeenReset = true;
10174            }
10175          }
10176        },
10177        "position": {
10178          enumerable: true,
10179          get: function get() {
10180            return _position;
10181          },
10182          set: function set(value) {
10183            if (value < 0 || value > 100) {
10184              throw new Error("Position must be between 0 and 100.");
10185            }
10186  
10187            _position = value;
10188            this.hasBeenReset = true;
10189          }
10190        },
10191        "positionAlign": {
10192          enumerable: true,
10193          get: function get() {
10194            return _positionAlign;
10195          },
10196          set: function set(value) {
10197            var setting = findAlignSetting(value);
10198  
10199            if (!setting) {
10200              console.warn("positionAlign: an invalid or illegal string was specified.");
10201            } else {
10202              _positionAlign = setting;
10203              this.hasBeenReset = true;
10204            }
10205          }
10206        },
10207        "size": {
10208          enumerable: true,
10209          get: function get() {
10210            return _size;
10211          },
10212          set: function set(value) {
10213            if (value < 0 || value > 100) {
10214              throw new Error("Size must be between 0 and 100.");
10215            }
10216  
10217            _size = value;
10218            this.hasBeenReset = true;
10219          }
10220        },
10221        "align": {
10222          enumerable: true,
10223          get: function get() {
10224            return _align;
10225          },
10226          set: function set(value) {
10227            var setting = findAlignSetting(value);
10228  
10229            if (!setting) {
10230              throw new SyntaxError("align: an invalid or illegal alignment string was specified.");
10231            }
10232  
10233            _align = setting;
10234            this.hasBeenReset = true;
10235          }
10236        }
10237      });
10238      /**
10239       * Other <track> spec defined properties
10240       */
10241      // http://www.whatwg.org/specs/web-apps/current-work/multipage/the-video-element.html#text-track-cue-display-state
10242  
10243      this.displayState = undefined;
10244    }
10245    /**
10246     * VTTCue methods
10247     */
10248  
10249  
10250    VTTCue.prototype.getCueAsHTML = function () {
10251      // Assume WebVTT.convertCueToDOMTree is on the global.
10252      return WebVTT.convertCueToDOMTree(window, this.text);
10253    };
10254  
10255    var vttcue = VTTCue;
10256  
10257    /**
10258     * Copyright 2013 vtt.js Contributors
10259     *
10260     * Licensed under the Apache License, Version 2.0 (the "License");
10261     * you may not use this file except in compliance with the License.
10262     * You may obtain a copy of the License at
10263     *
10264     *   http://www.apache.org/licenses/LICENSE-2.0
10265     *
10266     * Unless required by applicable law or agreed to in writing, software
10267     * distributed under the License is distributed on an "AS IS" BASIS,
10268     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
10269     * See the License for the specific language governing permissions and
10270     * limitations under the License.
10271     */
10272    var scrollSetting = {
10273      "": true,
10274      "up": true
10275    };
10276  
10277    function findScrollSetting(value) {
10278      if (typeof value !== "string") {
10279        return false;
10280      }
10281  
10282      var scroll = scrollSetting[value.toLowerCase()];
10283      return scroll ? value.toLowerCase() : false;
10284    }
10285  
10286    function isValidPercentValue(value) {
10287      return typeof value === "number" && value >= 0 && value <= 100;
10288    } // VTTRegion shim http://dev.w3.org/html5/webvtt/#vttregion-interface
10289  
10290  
10291    function VTTRegion() {
10292      var _width = 100;
10293      var _lines = 3;
10294      var _regionAnchorX = 0;
10295      var _regionAnchorY = 100;
10296      var _viewportAnchorX = 0;
10297      var _viewportAnchorY = 100;
10298      var _scroll = "";
10299      Object.defineProperties(this, {
10300        "width": {
10301          enumerable: true,
10302          get: function get() {
10303            return _width;
10304          },
10305          set: function set(value) {
10306            if (!isValidPercentValue(value)) {
10307              throw new Error("Width must be between 0 and 100.");
10308            }
10309  
10310            _width = value;
10311          }
10312        },
10313        "lines": {
10314          enumerable: true,
10315          get: function get() {
10316            return _lines;
10317          },
10318          set: function set(value) {
10319            if (typeof value !== "number") {
10320              throw new TypeError("Lines must be set to a number.");
10321            }
10322  
10323            _lines = value;
10324          }
10325        },
10326        "regionAnchorY": {
10327          enumerable: true,
10328          get: function get() {
10329            return _regionAnchorY;
10330          },
10331          set: function set(value) {
10332            if (!isValidPercentValue(value)) {
10333              throw new Error("RegionAnchorX must be between 0 and 100.");
10334            }
10335  
10336            _regionAnchorY = value;
10337          }
10338        },
10339        "regionAnchorX": {
10340          enumerable: true,
10341          get: function get() {
10342            return _regionAnchorX;
10343          },
10344          set: function set(value) {
10345            if (!isValidPercentValue(value)) {
10346              throw new Error("RegionAnchorY must be between 0 and 100.");
10347            }
10348  
10349            _regionAnchorX = value;
10350          }
10351        },
10352        "viewportAnchorY": {
10353          enumerable: true,
10354          get: function get() {
10355            return _viewportAnchorY;
10356          },
10357          set: function set(value) {
10358            if (!isValidPercentValue(value)) {
10359              throw new Error("ViewportAnchorY must be between 0 and 100.");
10360            }
10361  
10362            _viewportAnchorY = value;
10363          }
10364        },
10365        "viewportAnchorX": {
10366          enumerable: true,
10367          get: function get() {
10368            return _viewportAnchorX;
10369          },
10370          set: function set(value) {
10371            if (!isValidPercentValue(value)) {
10372              throw new Error("ViewportAnchorX must be between 0 and 100.");
10373            }
10374  
10375            _viewportAnchorX = value;
10376          }
10377        },
10378        "scroll": {
10379          enumerable: true,
10380          get: function get() {
10381            return _scroll;
10382          },
10383          set: function set(value) {
10384            var setting = findScrollSetting(value); // Have to check for false as an empty string is a legal value.
10385  
10386            if (setting === false) {
10387              console.warn("Scroll: an invalid or illegal string was specified.");
10388            } else {
10389              _scroll = setting;
10390            }
10391          }
10392        }
10393      });
10394    }
10395  
10396    var vttregion = VTTRegion;
10397  
10398    var browserIndex = createCommonjsModule(function (module) {
10399      /**
10400       * Copyright 2013 vtt.js Contributors
10401       *
10402       * Licensed under the Apache License, Version 2.0 (the "License");
10403       * you may not use this file except in compliance with the License.
10404       * You may obtain a copy of the License at
10405       *
10406       *   http://www.apache.org/licenses/LICENSE-2.0
10407       *
10408       * Unless required by applicable law or agreed to in writing, software
10409       * distributed under the License is distributed on an "AS IS" BASIS,
10410       * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
10411       * See the License for the specific language governing permissions and
10412       * limitations under the License.
10413       */
10414      // Default exports for Node. Export the extended versions of VTTCue and
10415      // VTTRegion in Node since we likely want the capability to convert back and
10416      // forth between JSON. If we don't then it's not that big of a deal since we're
10417      // off browser.
10418      var vttjs = module.exports = {
10419        WebVTT: vtt,
10420        VTTCue: vttcue,
10421        VTTRegion: vttregion
10422      };
10423      window$3.vttjs = vttjs;
10424      window$3.WebVTT = vttjs.WebVTT;
10425      var cueShim = vttjs.VTTCue;
10426      var regionShim = vttjs.VTTRegion;
10427      var nativeVTTCue = window$3.VTTCue;
10428      var nativeVTTRegion = window$3.VTTRegion;
10429  
10430      vttjs.shim = function () {
10431        window$3.VTTCue = cueShim;
10432        window$3.VTTRegion = regionShim;
10433      };
10434  
10435      vttjs.restore = function () {
10436        window$3.VTTCue = nativeVTTCue;
10437        window$3.VTTRegion = nativeVTTRegion;
10438      };
10439  
10440      if (!window$3.VTTCue) {
10441        vttjs.shim();
10442      }
10443    });
10444    var browserIndex_1 = browserIndex.WebVTT;
10445    var browserIndex_2 = browserIndex.VTTCue;
10446    var browserIndex_3 = browserIndex.VTTRegion;
10447  
10448    /**
10449     * An Object containing a structure like: `{src: 'url', type: 'mimetype'}` or string
10450     * that just contains the src url alone.
10451     * * `var SourceObject = {src: 'http://ex.com/video.mp4', type: 'video/mp4'};`
10452       * `var SourceString = 'http://example.com/some-video.mp4';`
10453     *
10454     * @typedef {Object|string} Tech~SourceObject
10455     *
10456     * @property {string} src
10457     *           The url to the source
10458     *
10459     * @property {string} type
10460     *           The mime type of the source
10461     */
10462  
10463    /**
10464     * A function used by {@link Tech} to create a new {@link TextTrack}.
10465     *
10466     * @private
10467     *
10468     * @param {Tech} self
10469     *        An instance of the Tech class.
10470     *
10471     * @param {string} kind
10472     *        `TextTrack` kind (subtitles, captions, descriptions, chapters, or metadata)
10473     *
10474     * @param {string} [label]
10475     *        Label to identify the text track
10476     *
10477     * @param {string} [language]
10478     *        Two letter language abbreviation
10479     *
10480     * @param {Object} [options={}]
10481     *        An object with additional text track options
10482     *
10483     * @return {TextTrack}
10484     *          The text track that was created.
10485     */
10486  
10487    function createTrackHelper(self, kind, label, language, options) {
10488      if (options === void 0) {
10489        options = {};
10490      }
10491  
10492      var tracks = self.textTracks();
10493      options.kind = kind;
10494  
10495      if (label) {
10496        options.label = label;
10497      }
10498  
10499      if (language) {
10500        options.language = language;
10501      }
10502  
10503      options.tech = self;
10504      var track = new ALL.text.TrackClass(options);
10505      tracks.addTrack(track);
10506      return track;
10507    }
10508    /**
10509     * This is the base class for media playback technology controllers, such as
10510     * {@link Flash} and {@link HTML5}
10511     *
10512     * @extends Component
10513     */
10514  
10515  
10516    var Tech = /*#__PURE__*/function (_Component) {
10517      inheritsLoose(Tech, _Component);
10518  
10519      /**
10520      * Create an instance of this Tech.
10521      *
10522      * @param {Object} [options]
10523      *        The key/value store of player options.
10524      *
10525      * @param {Component~ReadyCallback} ready
10526      *        Callback function to call when the `HTML5` Tech is ready.
10527      */
10528      function Tech(options, ready) {
10529        var _this;
10530  
10531        if (options === void 0) {
10532          options = {};
10533        }
10534  
10535        if (ready === void 0) {
10536          ready = function ready() {};
10537        }
10538  
10539        // we don't want the tech to report user activity automatically.
vendor: 63,023 bytes, lines 10540-12628
10540        // This is done manually in addControlsListeners
10541        options.reportTouchActivity = false;
10542        _this = _Component.call(this, null, options, ready) || this; // keep track of whether the current source has played at all to
10543        // implement a very limited played()
10544  
10545        _this.hasStarted_ = false;
10546  
10547        _this.on('playing', function () {
10548          this.hasStarted_ = true;
10549        });
10550  
10551        _this.on('loadstart', function () {
10552          this.hasStarted_ = false;
10553        });
10554  
10555        ALL.names.forEach(function (name) {
10556          var props = ALL[name];
10557  
10558          if (options && options[props.getterName]) {
10559            _this[props.privateName] = options[props.getterName];
10560          }
10561        }); // Manually track progress in cases where the browser/flash player doesn't report it.
10562  
10563        if (!_this.featuresProgressEvents) {
10564          _this.manualProgressOn();
10565        } // Manually track timeupdates in cases where the browser/flash player doesn't report it.
10566  
10567  
10568        if (!_this.featuresTimeupdateEvents) {
10569          _this.manualTimeUpdatesOn();
10570        }
10571  
10572        ['Text', 'Audio', 'Video'].forEach(function (track) {
10573          if (options["native" + track + "Tracks"] === false) {
10574            _this["featuresNative" + track + "Tracks"] = false;
10575          }
10576        });
10577  
10578        if (options.nativeCaptions === false || options.nativeTextTracks === false) {
10579          _this.featuresNativeTextTracks = false;
10580        } else if (options.nativeCaptions === true || options.nativeTextTracks === true) {
10581          _this.featuresNativeTextTracks = true;
10582        }
10583  
10584        if (!_this.featuresNativeTextTracks) {
10585          _this.emulateTextTracks();
10586        }
10587  
10588        _this.preloadTextTracks = options.preloadTextTracks !== false;
10589        _this.autoRemoteTextTracks_ = new ALL.text.ListClass();
10590  
10591        _this.initTrackListeners(); // Turn on component tap events only if not using native controls
10592  
10593  
10594        if (!options.nativeControlsForTouch) {
10595          _this.emitTapEvents();
10596        }
10597  
10598        if (_this.constructor) {
10599          _this.name_ = _this.constructor.name || 'Unknown Tech';
10600        }
10601  
10602        return _this;
10603      }
10604      /**
10605       * A special function to trigger source set in a way that will allow player
10606       * to re-trigger if the player or tech are not ready yet.
10607       *
10608       * @fires Tech#sourceset
10609       * @param {string} src The source string at the time of the source changing.
10610       */
10611  
10612  
10613      var _proto = Tech.prototype;
10614  
10615      _proto.triggerSourceset = function triggerSourceset(src) {
10616        var _this2 = this;
10617  
10618        if (!this.isReady_) {
10619          // on initial ready we have to trigger source set
10620          // 1ms after ready so that player can watch for it.
10621          this.one('ready', function () {
10622            return _this2.setTimeout(function () {
10623              return _this2.triggerSourceset(src);
10624            }, 1);
10625          });
10626        }
10627        /**
10628         * Fired when the source is set on the tech causing the media element
10629         * to reload.
10630         *
10631         * @see {@link Player#event:sourceset}
10632         * @event Tech#sourceset
10633         * @type {EventTarget~Event}
10634         */
10635  
10636  
10637        this.trigger({
10638          src: src,
10639          type: 'sourceset'
10640        });
10641      }
10642      /* Fallbacks for unsupported event types
10643      ================================================================================ */
10644  
10645      /**
10646       * Polyfill the `progress` event for browsers that don't support it natively.
10647       *
10648       * @see {@link Tech#trackProgress}
10649       */
10650      ;
10651  
10652      _proto.manualProgressOn = function manualProgressOn() {
10653        this.on('durationchange', this.onDurationChange);
10654        this.manualProgress = true; // Trigger progress watching when a source begins loading
10655  
10656        this.one('ready', this.trackProgress);
10657      }
10658      /**
10659       * Turn off the polyfill for `progress` events that was created in
10660       * {@link Tech#manualProgressOn}
10661       */
10662      ;
10663  
10664      _proto.manualProgressOff = function manualProgressOff() {
10665        this.manualProgress = false;
10666        this.stopTrackingProgress();
10667        this.off('durationchange', this.onDurationChange);
10668      }
10669      /**
10670       * This is used to trigger a `progress` event when the buffered percent changes. It
10671       * sets an interval function that will be called every 500 milliseconds to check if the
10672       * buffer end percent has changed.
10673       *
10674       * > This function is called by {@link Tech#manualProgressOn}
10675       *
10676       * @param {EventTarget~Event} event
10677       *        The `ready` event that caused this to run.
10678       *
10679       * @listens Tech#ready
10680       * @fires Tech#progress
10681       */
10682      ;
10683  
10684      _proto.trackProgress = function trackProgress(event) {
10685        this.stopTrackingProgress();
10686        this.progressInterval = this.setInterval(bind(this, function () {
10687          // Don't trigger unless buffered amount is greater than last time
10688          var numBufferedPercent = this.bufferedPercent();
10689  
10690          if (this.bufferedPercent_ !== numBufferedPercent) {
10691            /**
10692             * See {@link Player#progress}
10693             *
10694             * @event Tech#progress
10695             * @type {EventTarget~Event}
10696             */
10697            this.trigger('progress');
10698          }
10699  
10700          this.bufferedPercent_ = numBufferedPercent;
10701  
10702          if (numBufferedPercent === 1) {
10703            this.stopTrackingProgress();
10704          }
10705        }), 500);
10706      }
10707      /**
10708       * Update our internal duration on a `durationchange` event by calling
10709       * {@link Tech#duration}.
10710       *
10711       * @param {EventTarget~Event} event
10712       *        The `durationchange` event that caused this to run.
10713       *
10714       * @listens Tech#durationchange
10715       */
10716      ;
10717  
10718      _proto.onDurationChange = function onDurationChange(event) {
10719        this.duration_ = this.duration();
10720      }
10721      /**
10722       * Get and create a `TimeRange` object for buffering.
10723       *
10724       * @return {TimeRange}
10725       *         The time range object that was created.
10726       */
10727      ;
10728  
10729      _proto.buffered = function buffered() {
10730        return createTimeRanges(0, 0);
10731      }
10732      /**
10733       * Get the percentage of the current video that is currently buffered.
10734       *
10735       * @return {number}
10736       *         A number from 0 to 1 that represents the decimal percentage of the
10737       *         video that is buffered.
10738       *
10739       */
10740      ;
10741  
10742      _proto.bufferedPercent = function bufferedPercent$1() {
10743        return bufferedPercent(this.buffered(), this.duration_);
10744      }
10745      /**
10746       * Turn off the polyfill for `progress` events that was created in
10747       * {@link Tech#manualProgressOn}
10748       * Stop manually tracking progress events by clearing the interval that was set in
10749       * {@link Tech#trackProgress}.
10750       */
10751      ;
10752  
10753      _proto.stopTrackingProgress = function stopTrackingProgress() {
10754        this.clearInterval(this.progressInterval);
10755      }
10756      /**
10757       * Polyfill the `timeupdate` event for browsers that don't support it.
10758       *
10759       * @see {@link Tech#trackCurrentTime}
10760       */
10761      ;
10762  
10763      _proto.manualTimeUpdatesOn = function manualTimeUpdatesOn() {
10764        this.manualTimeUpdates = true;
10765        this.on('play', this.trackCurrentTime);
10766        this.on('pause', this.stopTrackingCurrentTime);
10767      }
10768      /**
10769       * Turn off the polyfill for `timeupdate` events that was created in
10770       * {@link Tech#manualTimeUpdatesOn}
10771       */
10772      ;
10773  
10774      _proto.manualTimeUpdatesOff = function manualTimeUpdatesOff() {
10775        this.manualTimeUpdates = false;
10776        this.stopTrackingCurrentTime();
10777        this.off('play', this.trackCurrentTime);
10778        this.off('pause', this.stopTrackingCurrentTime);
10779      }
10780      /**
10781       * Sets up an interval function to track current time and trigger `timeupdate` every
10782       * 250 milliseconds.
10783       *
10784       * @listens Tech#play
10785       * @triggers Tech#timeupdate
10786       */
10787      ;
10788  
10789      _proto.trackCurrentTime = function trackCurrentTime() {
10790        if (this.currentTimeInterval) {
10791          this.stopTrackingCurrentTime();
10792        }
10793  
10794        this.currentTimeInterval = this.setInterval(function () {
10795          /**
10796           * Triggered at an interval of 250ms to indicated that time is passing in the video.
10797           *
10798           * @event Tech#timeupdate
10799           * @type {EventTarget~Event}
10800           */
10801          this.trigger({
10802            type: 'timeupdate',
10803            target: this,
10804            manuallyTriggered: true
10805          }); // 42 = 24 fps // 250 is what Webkit uses // FF uses 15
10806        }, 250);
10807      }
10808      /**
10809       * Stop the interval function created in {@link Tech#trackCurrentTime} so that the
10810       * `timeupdate` event is no longer triggered.
10811       *
10812       * @listens {Tech#pause}
10813       */
10814      ;
10815  
10816      _proto.stopTrackingCurrentTime = function stopTrackingCurrentTime() {
10817        this.clearInterval(this.currentTimeInterval); // #1002 - if the video ends right before the next timeupdate would happen,
10818        // the progress bar won't make it all the way to the end
10819  
10820        this.trigger({
10821          type: 'timeupdate',
10822          target: this,
10823          manuallyTriggered: true
10824        });
10825      }
10826      /**
10827       * Turn off all event polyfills, clear the `Tech`s {@link AudioTrackList},
10828       * {@link VideoTrackList}, and {@link TextTrackList}, and dispose of this Tech.
10829       *
10830       * @fires Component#dispose
10831       */
10832      ;
10833  
10834      _proto.dispose = function dispose() {
10835        // clear out all tracks because we can't reuse them between techs
10836        this.clearTracks(NORMAL.names); // Turn off any manual progress or timeupdate tracking
10837  
10838        if (this.manualProgress) {
10839          this.manualProgressOff();
10840        }
10841  
10842        if (this.manualTimeUpdates) {
10843          this.manualTimeUpdatesOff();
10844        }
10845  
10846        _Component.prototype.dispose.call(this);
10847      }
10848      /**
10849       * Clear out a single `TrackList` or an array of `TrackLists` given their names.
10850       *
10851       * > Note: Techs without source handlers should call this between sources for `video`
10852       *         & `audio` tracks. You don't want to use them between tracks!
10853       *
10854       * @param {string[]|string} types
10855       *        TrackList names to clear, valid names are `video`, `audio`, and
10856       *        `text`.
10857       */
10858      ;
10859  
10860      _proto.clearTracks = function clearTracks(types) {
10861        var _this3 = this;
10862  
10863        types = [].concat(types); // clear out all tracks because we can't reuse them between techs
10864  
10865        types.forEach(function (type) {
10866          var list = _this3[type + "Tracks"]() || [];
10867          var i = list.length;
10868  
10869          while (i--) {
10870            var track = list[i];
10871  
10872            if (type === 'text') {
10873              _this3.removeRemoteTextTrack(track);
10874            }
10875  
10876            list.removeTrack(track);
10877          }
10878        });
10879      }
10880      /**
10881       * Remove any TextTracks added via addRemoteTextTrack that are
10882       * flagged for automatic garbage collection
10883       */
10884      ;
10885  
10886      _proto.cleanupAutoTextTracks = function cleanupAutoTextTracks() {
10887        var list = this.autoRemoteTextTracks_ || [];
10888        var i = list.length;
10889  
10890        while (i--) {
10891          var track = list[i];
10892          this.removeRemoteTextTrack(track);
10893        }
10894      }
10895      /**
10896       * Reset the tech, which will removes all sources and reset the internal readyState.
10897       *
10898       * @abstract
10899       */
10900      ;
10901  
10902      _proto.reset = function reset() {}
10903      /**
10904       * Get or set an error on the Tech.
10905       *
10906       * @param {MediaError} [err]
10907       *        Error to set on the Tech
10908       *
10909       * @return {MediaError|null}
10910       *         The current error object on the tech, or null if there isn't one.
10911       */
10912      ;
10913  
10914      _proto.error = function error(err) {
10915        if (err !== undefined) {
10916          this.error_ = new MediaError(err);
10917          this.trigger('error');
10918        }
10919  
10920        return this.error_;
10921      }
10922      /**
10923       * Returns the `TimeRange`s that have been played through for the current source.
10924       *
10925       * > NOTE: This implementation is incomplete. It does not track the played `TimeRange`.
10926       *         It only checks whether the source has played at all or not.
10927       *
10928       * @return {TimeRange}
10929       *         - A single time range if this video has played
10930       *         - An empty set of ranges if not.
10931       */
10932      ;
10933  
10934      _proto.played = function played() {
10935        if (this.hasStarted_) {
10936          return createTimeRanges(0, 0);
10937        }
10938  
10939        return createTimeRanges();
10940      }
10941      /**
10942       * Causes a manual time update to occur if {@link Tech#manualTimeUpdatesOn} was
10943       * previously called.
10944       *
10945       * @fires Tech#timeupdate
10946       */
10947      ;
10948  
10949      _proto.setCurrentTime = function setCurrentTime() {
10950        // improve the accuracy of manual timeupdates
10951        if (this.manualTimeUpdates) {
10952          /**
10953           * A manual `timeupdate` event.
10954           *
10955           * @event Tech#timeupdate
10956           * @type {EventTarget~Event}
10957           */
10958          this.trigger({
10959            type: 'timeupdate',
10960            target: this,
10961            manuallyTriggered: true
10962          });
10963        }
10964      }
10965      /**
10966       * Turn on listeners for {@link VideoTrackList}, {@link {AudioTrackList}, and
10967       * {@link TextTrackList} events.
10968       *
10969       * This adds {@link EventTarget~EventListeners} for `addtrack`, and  `removetrack`.
10970       *
10971       * @fires Tech#audiotrackchange
10972       * @fires Tech#videotrackchange
10973       * @fires Tech#texttrackchange
10974       */
10975      ;
10976  
10977      _proto.initTrackListeners = function initTrackListeners() {
10978        var _this4 = this;
10979  
10980        /**
10981          * Triggered when tracks are added or removed on the Tech {@link AudioTrackList}
10982          *
10983          * @event Tech#audiotrackchange
10984          * @type {EventTarget~Event}
10985          */
10986  
10987        /**
10988          * Triggered when tracks are added or removed on the Tech {@link VideoTrackList}
10989          *
10990          * @event Tech#videotrackchange
10991          * @type {EventTarget~Event}
10992          */
10993  
10994        /**
10995          * Triggered when tracks are added or removed on the Tech {@link TextTrackList}
10996          *
10997          * @event Tech#texttrackchange
10998          * @type {EventTarget~Event}
10999          */
11000        NORMAL.names.forEach(function (name) {
11001          var props = NORMAL[name];
11002  
11003          var trackListChanges = function trackListChanges() {
11004            _this4.trigger(name + "trackchange");
11005          };
11006  
11007          var tracks = _this4[props.getterName]();
11008  
11009          tracks.addEventListener('removetrack', trackListChanges);
11010          tracks.addEventListener('addtrack', trackListChanges);
11011  
11012          _this4.on('dispose', function () {
11013            tracks.removeEventListener('removetrack', trackListChanges);
11014            tracks.removeEventListener('addtrack', trackListChanges);
11015          });
11016        });
11017      }
11018      /**
11019       * Emulate TextTracks using vtt.js if necessary
11020       *
11021       * @fires Tech#vttjsloaded
11022       * @fires Tech#vttjserror
11023       */
11024      ;
11025  
11026      _proto.addWebVttScript_ = function addWebVttScript_() {
11027        var _this5 = this;
11028  
11029        if (window$3.WebVTT) {
11030          return;
11031        } // Initially, Tech.el_ is a child of a dummy-div wait until the Component system
11032        // signals that the Tech is ready at which point Tech.el_ is part of the DOM
11033        // before inserting the WebVTT script
11034  
11035  
11036        if (document.body.contains(this.el())) {
11037          // load via require if available and vtt.js script location was not passed in
11038          // as an option. novtt builds will turn the above require call into an empty object
11039          // which will cause this if check to always fail.
11040          if (!this.options_['vtt.js'] && isPlain(browserIndex) && Object.keys(browserIndex).length > 0) {
11041            this.trigger('vttjsloaded');
11042            return;
11043          } // load vtt.js via the script location option or the cdn of no location was
11044          // passed in
11045  
11046  
11047          var script = document.createElement('script');
11048          script.src = this.options_['vtt.js'] || 'https://vjs.zencdn.net/vttjs/0.14.1/vtt.min.js';
11049  
11050          script.onload = function () {
11051            /**
11052             * Fired when vtt.js is loaded.
11053             *
11054             * @event Tech#vttjsloaded
11055             * @type {EventTarget~Event}
11056             */
11057            _this5.trigger('vttjsloaded');
11058          };
11059  
11060          script.onerror = function () {
11061            /**
11062             * Fired when vtt.js was not loaded due to an error
11063             *
11064             * @event Tech#vttjsloaded
11065             * @type {EventTarget~Event}
11066             */
11067            _this5.trigger('vttjserror');
11068          };
11069  
11070          this.on('dispose', function () {
11071            script.onload = null;
11072            script.onerror = null;
11073          }); // but have not loaded yet and we set it to true before the inject so that
11074          // we don't overwrite the injected window.WebVTT if it loads right away
11075  
11076          window$3.WebVTT = true;
11077          this.el().parentNode.appendChild(script);
11078        } else {
11079          this.ready(this.addWebVttScript_);
11080        }
11081      }
11082      /**
11083       * Emulate texttracks
11084       *
11085       */
11086      ;
11087  
11088      _proto.emulateTextTracks = function emulateTextTracks() {
11089        var _this6 = this;
11090  
11091        var tracks = this.textTracks();
11092        var remoteTracks = this.remoteTextTracks();
11093  
11094        var handleAddTrack = function handleAddTrack(e) {
11095          return tracks.addTrack(e.track);
11096        };
11097  
11098        var handleRemoveTrack = function handleRemoveTrack(e) {
11099          return tracks.removeTrack(e.track);
11100        };
11101  
11102        remoteTracks.on('addtrack', handleAddTrack);
11103        remoteTracks.on('removetrack', handleRemoveTrack);
11104        this.addWebVttScript_();
11105  
11106        var updateDisplay = function updateDisplay() {
11107          return _this6.trigger('texttrackchange');
11108        };
11109  
11110        var textTracksChanges = function textTracksChanges() {
11111          updateDisplay();
11112  
11113          for (var i = 0; i < tracks.length; i++) {
11114            var track = tracks[i];
11115            track.removeEventListener('cuechange', updateDisplay);
11116  
11117            if (track.mode === 'showing') {
11118              track.addEventListener('cuechange', updateDisplay);
11119            }
11120          }
11121        };
11122  
11123        textTracksChanges();
11124        tracks.addEventListener('change', textTracksChanges);
11125        tracks.addEventListener('addtrack', textTracksChanges);
11126        tracks.addEventListener('removetrack', textTracksChanges);
11127        this.on('dispose', function () {
11128          remoteTracks.off('addtrack', handleAddTrack);
11129          remoteTracks.off('removetrack', handleRemoveTrack);
11130          tracks.removeEventListener('change', textTracksChanges);
11131          tracks.removeEventListener('addtrack', textTracksChanges);
11132          tracks.removeEventListener('removetrack', textTracksChanges);
11133  
11134          for (var i = 0; i < tracks.length; i++) {
11135            var track = tracks[i];
11136            track.removeEventListener('cuechange', updateDisplay);
11137          }
11138        });
11139      }
11140      /**
11141       * Create and returns a remote {@link TextTrack} object.
11142       *
11143       * @param {string} kind
11144       *        `TextTrack` kind (subtitles, captions, descriptions, chapters, or metadata)
11145       *
11146       * @param {string} [label]
11147       *        Label to identify the text track
11148       *
11149       * @param {string} [language]
11150       *        Two letter language abbreviation
11151       *
11152       * @return {TextTrack}
11153       *         The TextTrack that gets created.
11154       */
11155      ;
11156  
11157      _proto.addTextTrack = function addTextTrack(kind, label, language) {
11158        if (!kind) {
11159          throw new Error('TextTrack kind is required but was not provided');
11160        }
11161  
11162        return createTrackHelper(this, kind, label, language);
11163      }
11164      /**
11165       * Create an emulated TextTrack for use by addRemoteTextTrack
11166       *
11167       * This is intended to be overridden by classes that inherit from
11168       * Tech in order to create native or custom TextTracks.
11169       *
11170       * @param {Object} options
11171       *        The object should contain the options to initialize the TextTrack with.
11172       *
11173       * @param {string} [options.kind]
11174       *        `TextTrack` kind (subtitles, captions, descriptions, chapters, or metadata).
11175       *
11176       * @param {string} [options.label].
11177       *        Label to identify the text track
11178       *
11179       * @param {string} [options.language]
11180       *        Two letter language abbreviation.
11181       *
11182       * @return {HTMLTrackElement}
11183       *         The track element that gets created.
11184       */
11185      ;
11186  
11187      _proto.createRemoteTextTrack = function createRemoteTextTrack(options) {
11188        var track = mergeOptions(options, {
11189          tech: this
11190        });
11191        return new REMOTE.remoteTextEl.TrackClass(track);
11192      }
11193      /**
11194       * Creates a remote text track object and returns an html track element.
11195       *
11196       * > Note: This can be an emulated {@link HTMLTrackElement} or a native one.
11197       *
11198       * @param {Object} options
11199       *        See {@link Tech#createRemoteTextTrack} for more detailed properties.
11200       *
11201       * @param {boolean} [manualCleanup=true]
11202       *        - When false: the TextTrack will be automatically removed from the video
11203       *          element whenever the source changes
11204       *        - When True: The TextTrack will have to be cleaned up manually
11205       *
11206       * @return {HTMLTrackElement}
11207       *         An Html Track Element.
11208       *
11209       * @deprecated The default functionality for this function will be equivalent
11210       *             to "manualCleanup=false" in the future. The manualCleanup parameter will
11211       *             also be removed.
11212       */
11213      ;
11214  
11215      _proto.addRemoteTextTrack = function addRemoteTextTrack(options, manualCleanup) {
11216        var _this7 = this;
11217  
11218        if (options === void 0) {
11219          options = {};
11220        }
11221  
11222        var htmlTrackElement = this.createRemoteTextTrack(options);
11223  
11224        if (manualCleanup !== true && manualCleanup !== false) {
11225          // deprecation warning
11226          log.warn('Calling addRemoteTextTrack without explicitly setting the "manualCleanup" parameter to `true` is deprecated and default to `false` in future version of video.js');
11227          manualCleanup = true;
11228        } // store HTMLTrackElement and TextTrack to remote list
11229  
11230  
11231        this.remoteTextTrackEls().addTrackElement_(htmlTrackElement);
11232        this.remoteTextTracks().addTrack(htmlTrackElement.track);
11233  
11234        if (manualCleanup !== true) {
11235          // create the TextTrackList if it doesn't exist
11236          this.ready(function () {
11237            return _this7.autoRemoteTextTracks_.addTrack(htmlTrackElement.track);
11238          });
11239        }
11240  
11241        return htmlTrackElement;
11242      }
11243      /**
11244       * Remove a remote text track from the remote `TextTrackList`.
11245       *
11246       * @param {TextTrack} track
11247       *        `TextTrack` to remove from the `TextTrackList`
11248       */
11249      ;
11250  
11251      _proto.removeRemoteTextTrack = function removeRemoteTextTrack(track) {
11252        var trackElement = this.remoteTextTrackEls().getTrackElementByTrack_(track); // remove HTMLTrackElement and TextTrack from remote list
11253  
11254        this.remoteTextTrackEls().removeTrackElement_(trackElement);
11255        this.remoteTextTracks().removeTrack(track);
11256        this.autoRemoteTextTracks_.removeTrack(track);
11257      }
11258      /**
11259       * Gets available media playback quality metrics as specified by the W3C's Media
11260       * Playback Quality API.
11261       *
11262       * @see [Spec]{@link https://wicg.github.io/media-playback-quality}
11263       *
11264       * @return {Object}
11265       *         An object with supported media playback quality metrics
11266       *
11267       * @abstract
11268       */
11269      ;
11270  
11271      _proto.getVideoPlaybackQuality = function getVideoPlaybackQuality() {
11272        return {};
11273      }
11274      /**
11275       * Attempt to create a floating video window always on top of other windows
11276       * so that users may continue consuming media while they interact with other
11277       * content sites, or applications on their device.
11278       *
11279       * @see [Spec]{@link https://wicg.github.io/picture-in-picture}
11280       *
11281       * @return {Promise|undefined}
11282       *         A promise with a Picture-in-Picture window if the browser supports
11283       *         Promises (or one was passed in as an option). It returns undefined
11284       *         otherwise.
11285       *
11286       * @abstract
11287       */
11288      ;
11289  
11290      _proto.requestPictureInPicture = function requestPictureInPicture() {
11291        var PromiseClass = this.options_.Promise || window$3.Promise;
11292  
11293        if (PromiseClass) {
11294          return PromiseClass.reject();
11295        }
11296      }
11297      /**
11298       * A method to set a poster from a `Tech`.
11299       *
11300       * @abstract
11301       */
11302      ;
11303  
11304      _proto.setPoster = function setPoster() {}
11305      /**
11306       * A method to check for the presence of the 'playsinline' <video> attribute.
11307       *
11308       * @abstract
11309       */
11310      ;
11311  
11312      _proto.playsinline = function playsinline() {}
11313      /**
11314       * A method to set or unset the 'playsinline' <video> attribute.
11315       *
11316       * @abstract
11317       */
11318      ;
11319  
11320      _proto.setPlaysinline = function setPlaysinline() {}
11321      /**
11322       * Attempt to force override of native audio tracks.
11323       *
11324       * @param {boolean} override - If set to true native audio will be overridden,
11325       * otherwise native audio will potentially be used.
11326       *
11327       * @abstract
11328       */
11329      ;
11330  
11331      _proto.overrideNativeAudioTracks = function overrideNativeAudioTracks() {}
11332      /**
11333       * Attempt to force override of native video tracks.
11334       *
11335       * @param {boolean} override - If set to true native video will be overridden,
11336       * otherwise native video will potentially be used.
11337       *
11338       * @abstract
11339       */
11340      ;
11341  
11342      _proto.overrideNativeVideoTracks = function overrideNativeVideoTracks() {}
11343      /*
11344       * Check if the tech can support the given mime-type.
11345       *
11346       * The base tech does not support any type, but source handlers might
11347       * overwrite this.
11348       *
11349       * @param  {string} type
11350       *         The mimetype to check for support
11351       *
11352       * @return {string}
11353       *         'probably', 'maybe', or empty string
11354       *
11355       * @see [Spec]{@link https://developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement/canPlayType}
11356       *
11357       * @abstract
11358       */
11359      ;
11360  
11361      _proto.canPlayType = function canPlayType() {
11362        return '';
11363      }
11364      /**
11365       * Check if the type is supported by this tech.
11366       *
11367       * The base tech does not support any type, but source handlers might
11368       * overwrite this.
11369       *
11370       * @param {string} type
11371       *        The media type to check
11372       * @return {string} Returns the native video element's response
11373       */
11374      ;
11375  
11376      Tech.canPlayType = function canPlayType() {
11377        return '';
11378      }
11379      /**
11380       * Check if the tech can support the given source
11381       *
11382       * @param {Object} srcObj
11383       *        The source object
11384       * @param {Object} options
11385       *        The options passed to the tech
11386       * @return {string} 'probably', 'maybe', or '' (empty string)
11387       */
11388      ;
11389  
11390      Tech.canPlaySource = function canPlaySource(srcObj, options) {
11391        return Tech.canPlayType(srcObj.type);
11392      }
11393      /*
11394       * Return whether the argument is a Tech or not.
11395       * Can be passed either a Class like `Html5` or a instance like `player.tech_`
11396       *
11397       * @param {Object} component
11398       *        The item to check
11399       *
11400       * @return {boolean}
11401       *         Whether it is a tech or not
11402       *         - True if it is a tech
11403       *         - False if it is not
11404       */
11405      ;
11406  
11407      Tech.isTech = function isTech(component) {
11408        return component.prototype instanceof Tech || component instanceof Tech || component === Tech;
11409      }
11410      /**
11411       * Registers a `Tech` into a shared list for videojs.
11412       *
11413       * @param {string} name
11414       *        Name of the `Tech` to register.
11415       *
11416       * @param {Object} tech
11417       *        The `Tech` class to register.
11418       */
11419      ;
11420  
11421      Tech.registerTech = function registerTech(name, tech) {
11422        if (!Tech.techs_) {
11423          Tech.techs_ = {};
11424        }
11425  
11426        if (!Tech.isTech(tech)) {
11427          throw new Error("Tech " + name + " must be a Tech");
11428        }
11429  
11430        if (!Tech.canPlayType) {
11431          throw new Error('Techs must have a static canPlayType method on them');
11432        }
11433  
11434        if (!Tech.canPlaySource) {
11435          throw new Error('Techs must have a static canPlaySource method on them');
11436        }
11437  
11438        name = toTitleCase(name);
11439        Tech.techs_[name] = tech;
11440        Tech.techs_[toLowerCase(name)] = tech;
11441  
11442        if (name !== 'Tech') {
11443          // camel case the techName for use in techOrder
11444          Tech.defaultTechOrder_.push(name);
11445        }
11446  
11447        return tech;
11448      }
11449      /**
11450       * Get a `Tech` from the shared list by name.
11451       *
11452       * @param {string} name
11453       *        `camelCase` or `TitleCase` name of the Tech to get
11454       *
11455       * @return {Tech|undefined}
11456       *         The `Tech` or undefined if there was no tech with the name requested.
11457       */
11458      ;
11459  
11460      Tech.getTech = function getTech(name) {
11461        if (!name) {
11462          return;
11463        }
11464  
11465        if (Tech.techs_ && Tech.techs_[name]) {
11466          return Tech.techs_[name];
11467        }
11468  
11469        name = toTitleCase(name);
11470  
11471        if (window$3 && window$3.videojs && window$3.videojs[name]) {
11472          log.warn("The " + name + " tech was added to the videojs object when it should be registered using videojs.registerTech(name, tech)");
11473          return window$3.videojs[name];
11474        }
11475      };
11476  
11477      return Tech;
11478    }(Component);
11479    /**
11480     * Get the {@link VideoTrackList}
11481     *
11482     * @returns {VideoTrackList}
11483     * @method Tech.prototype.videoTracks
11484     */
11485  
11486    /**
11487     * Get the {@link AudioTrackList}
11488     *
11489     * @returns {AudioTrackList}
11490     * @method Tech.prototype.audioTracks
11491     */
11492  
11493    /**
11494     * Get the {@link TextTrackList}
11495     *
11496     * @returns {TextTrackList}
11497     * @method Tech.prototype.textTracks
11498     */
11499  
11500    /**
11501     * Get the remote element {@link TextTrackList}
11502     *
11503     * @returns {TextTrackList}
11504     * @method Tech.prototype.remoteTextTracks
11505     */
11506  
11507    /**
11508     * Get the remote element {@link HtmlTrackElementList}
11509     *
11510     * @returns {HtmlTrackElementList}
11511     * @method Tech.prototype.remoteTextTrackEls
11512     */
11513  
11514  
11515    ALL.names.forEach(function (name) {
11516      var props = ALL[name];
11517  
11518      Tech.prototype[props.getterName] = function () {
11519        this[props.privateName] = this[props.privateName] || new props.ListClass();
11520        return this[props.privateName];
11521      };
11522    });
11523    /**
11524     * List of associated text tracks
11525     *
11526     * @type {TextTrackList}
11527     * @private
11528     * @property Tech#textTracks_
11529     */
11530  
11531    /**
11532     * List of associated audio tracks.
11533     *
11534     * @type {AudioTrackList}
11535     * @private
11536     * @property Tech#audioTracks_
11537     */
11538  
11539    /**
11540     * List of associated video tracks.
11541     *
11542     * @type {VideoTrackList}
11543     * @private
11544     * @property Tech#videoTracks_
11545     */
11546  
11547    /**
11548     * Boolean indicating whether the `Tech` supports volume control.
11549     *
11550     * @type {boolean}
11551     * @default
11552     */
11553  
11554    Tech.prototype.featuresVolumeControl = true;
11555    /**
11556     * Boolean indicating whether the `Tech` supports muting volume.
11557     *
11558     * @type {bolean}
11559     * @default
11560     */
11561  
11562    Tech.prototype.featuresMuteControl = true;
11563    /**
11564     * Boolean indicating whether the `Tech` supports fullscreen resize control.
11565     * Resizing plugins using request fullscreen reloads the plugin
11566     *
11567     * @type {boolean}
11568     * @default
11569     */
11570  
11571    Tech.prototype.featuresFullscreenResize = false;
11572    /**
11573     * Boolean indicating whether the `Tech` supports changing the speed at which the video
11574     * plays. Examples:
11575     *   - Set player to play 2x (twice) as fast
11576     *   - Set player to play 0.5x (half) as fast
11577     *
11578     * @type {boolean}
11579     * @default
11580     */
11581  
11582    Tech.prototype.featuresPlaybackRate = false;
11583    /**
11584     * Boolean indicating whether the `Tech` supports the `progress` event. This is currently
11585     * not triggered by video-js-swf. This will be used to determine if
11586     * {@link Tech#manualProgressOn} should be called.
11587     *
11588     * @type {boolean}
11589     * @default
11590     */
11591  
11592    Tech.prototype.featuresProgressEvents = false;
11593    /**
11594     * Boolean indicating whether the `Tech` supports the `sourceset` event.
11595     *
11596     * A tech should set this to `true` and then use {@link Tech#triggerSourceset}
11597     * to trigger a {@link Tech#event:sourceset} at the earliest time after getting
11598     * a new source.
11599     *
11600     * @type {boolean}
11601     * @default
11602     */
11603  
11604    Tech.prototype.featuresSourceset = false;
11605    /**
11606     * Boolean indicating whether the `Tech` supports the `timeupdate` event. This is currently
11607     * not triggered by video-js-swf. This will be used to determine if
11608     * {@link Tech#manualTimeUpdates} should be called.
11609     *
11610     * @type {boolean}
11611     * @default
11612     */
11613  
11614    Tech.prototype.featuresTimeupdateEvents = false;
11615    /**
11616     * Boolean indicating whether the `Tech` supports the native `TextTrack`s.
11617     * This will help us integrate with native `TextTrack`s if the browser supports them.
11618     *
11619     * @type {boolean}
11620     * @default
11621     */
11622  
11623    Tech.prototype.featuresNativeTextTracks = false;
11624    /**
11625     * A functional mixin for techs that want to use the Source Handler pattern.
11626     * Source handlers are scripts for handling specific formats.
11627     * The source handler pattern is used for adaptive formats (HLS, DASH) that
11628     * manually load video data and feed it into a Source Buffer (Media Source Extensions)
11629     * Example: `Tech.withSourceHandlers.call(MyTech);`
11630     *
11631     * @param {Tech} _Tech
11632     *        The tech to add source handler functions to.
11633     *
11634     * @mixes Tech~SourceHandlerAdditions
11635     */
11636  
11637    Tech.withSourceHandlers = function (_Tech) {
11638      /**
11639       * Register a source handler
11640       *
11641       * @param {Function} handler
11642       *        The source handler class
11643       *
11644       * @param {number} [index]
11645       *        Register it at the following index
11646       */
11647      _Tech.registerSourceHandler = function (handler, index) {
11648        var handlers = _Tech.sourceHandlers;
11649  
11650        if (!handlers) {
11651          handlers = _Tech.sourceHandlers = [];
11652        }
11653  
11654        if (index === undefined) {
11655          // add to the end of the list
11656          index = handlers.length;
11657        }
11658  
11659        handlers.splice(index, 0, handler);
11660      };
11661      /**
11662       * Check if the tech can support the given type. Also checks the
11663       * Techs sourceHandlers.
11664       *
11665       * @param {string} type
11666       *         The mimetype to check.
11667       *
11668       * @return {string}
11669       *         'probably', 'maybe', or '' (empty string)
11670       */
11671  
11672  
11673      _Tech.canPlayType = function (type) {
11674        var handlers = _Tech.sourceHandlers || [];
11675        var can;
11676  
11677        for (var i = 0; i < handlers.length; i++) {
11678          can = handlers[i].canPlayType(type);
11679  
11680          if (can) {
11681            return can;
11682          }
11683        }
11684  
11685        return '';
11686      };
11687      /**
11688       * Returns the first source handler that supports the source.
11689       *
11690       * TODO: Answer question: should 'probably' be prioritized over 'maybe'
11691       *
11692       * @param {Tech~SourceObject} source
11693       *        The source object
11694       *
11695       * @param {Object} options
11696       *        The options passed to the tech
11697       *
11698       * @return {SourceHandler|null}
11699       *          The first source handler that supports the source or null if
11700       *          no SourceHandler supports the source
11701       */
11702  
11703  
11704      _Tech.selectSourceHandler = function (source, options) {
11705        var handlers = _Tech.sourceHandlers || [];
11706        var can;
11707  
11708        for (var i = 0; i < handlers.length; i++) {
11709          can = handlers[i].canHandleSource(source, options);
11710  
11711          if (can) {
11712            return handlers[i];
11713          }
11714        }
11715  
11716        return null;
11717      };
11718      /**
11719       * Check if the tech can support the given source.
11720       *
11721       * @param {Tech~SourceObject} srcObj
11722       *        The source object
11723       *
11724       * @param {Object} options
11725       *        The options passed to the tech
11726       *
11727       * @return {string}
11728       *         'probably', 'maybe', or '' (empty string)
11729       */
11730  
11731  
11732      _Tech.canPlaySource = function (srcObj, options) {
11733        var sh = _Tech.selectSourceHandler(srcObj, options);
11734  
11735        if (sh) {
11736          return sh.canHandleSource(srcObj, options);
11737        }
11738  
11739        return '';
11740      };
11741      /**
11742       * When using a source handler, prefer its implementation of
11743       * any function normally provided by the tech.
11744       */
11745  
11746  
11747      var deferrable = ['seekable', 'seeking', 'duration'];
11748      /**
11749       * A wrapper around {@link Tech#seekable} that will call a `SourceHandler`s seekable
11750       * function if it exists, with a fallback to the Techs seekable function.
11751       *
11752       * @method _Tech.seekable
11753       */
11754  
11755      /**
11756       * A wrapper around {@link Tech#duration} that will call a `SourceHandler`s duration
11757       * function if it exists, otherwise it will fallback to the techs duration function.
11758       *
11759       * @method _Tech.duration
11760       */
11761  
11762      deferrable.forEach(function (fnName) {
11763        var originalFn = this[fnName];
11764  
11765        if (typeof originalFn !== 'function') {
11766          return;
11767        }
11768  
11769        this[fnName] = function () {
11770          if (this.sourceHandler_ && this.sourceHandler_[fnName]) {
11771            return this.sourceHandler_[fnName].apply(this.sourceHandler_, arguments);
11772          }
11773  
11774          return originalFn.apply(this, arguments);
11775        };
11776      }, _Tech.prototype);
11777      /**
11778       * Create a function for setting the source using a source object
11779       * and source handlers.
11780       * Should never be called unless a source handler was found.
11781       *
11782       * @param {Tech~SourceObject} source
11783       *        A source object with src and type keys
11784       */
11785  
11786      _Tech.prototype.setSource = function (source) {
11787        var sh = _Tech.selectSourceHandler(source, this.options_);
11788  
11789        if (!sh) {
11790          // Fall back to a native source hander when unsupported sources are
11791          // deliberately set
11792          if (_Tech.nativeSourceHandler) {
11793            sh = _Tech.nativeSourceHandler;
11794          } else {
11795            log.error('No source handler found for the current source.');
11796          }
11797        } // Dispose any existing source handler
11798  
11799  
11800        this.disposeSourceHandler();
11801        this.off('dispose', this.disposeSourceHandler);
11802  
11803        if (sh !== _Tech.nativeSourceHandler) {
11804          this.currentSource_ = source;
11805        }
11806  
11807        this.sourceHandler_ = sh.handleSource(source, this, this.options_);
11808        this.one('dispose', this.disposeSourceHandler);
11809      };
11810      /**
11811       * Clean up any existing SourceHandlers and listeners when the Tech is disposed.
11812       *
11813       * @listens Tech#dispose
11814       */
11815  
11816  
11817      _Tech.prototype.disposeSourceHandler = function () {
11818        // if we have a source and get another one
11819        // then we are loading something new
11820        // than clear all of our current tracks
11821        if (this.currentSource_) {
11822          this.clearTracks(['audio', 'video']);
11823          this.currentSource_ = null;
11824        } // always clean up auto-text tracks
11825  
11826  
11827        this.cleanupAutoTextTracks();
11828  
11829        if (this.sourceHandler_) {
11830          if (this.sourceHandler_.dispose) {
11831            this.sourceHandler_.dispose();
11832          }
11833  
11834          this.sourceHandler_ = null;
11835        }
11836      };
11837    }; // The base Tech class needs to be registered as a Component. It is the only
11838    // Tech that can be registered as a Component.
11839  
11840  
11841    Component.registerComponent('Tech', Tech);
11842    Tech.registerTech('Tech', Tech);
11843    /**
11844     * A list of techs that should be added to techOrder on Players
11845     *
11846     * @private
11847     */
11848  
11849    Tech.defaultTechOrder_ = [];
11850  
11851    /**
11852     * @file middleware.js
11853     * @module middleware
11854     */
11855    var middlewares = {};
11856    var middlewareInstances = {};
11857    var TERMINATOR = {};
11858    /**
11859     * A middleware object is a plain JavaScript object that has methods that
11860     * match the {@link Tech} methods found in the lists of allowed
11861     * {@link module:middleware.allowedGetters|getters},
11862     * {@link module:middleware.allowedSetters|setters}, and
11863     * {@link module:middleware.allowedMediators|mediators}.
11864     *
11865     * @typedef {Object} MiddlewareObject
11866     */
11867  
11868    /**
11869     * A middleware factory function that should return a
11870     * {@link module:middleware~MiddlewareObject|MiddlewareObject}.
11871     *
11872     * This factory will be called for each player when needed, with the player
11873     * passed in as an argument.
11874     *
11875     * @callback MiddlewareFactory
11876     * @param {Player} player
11877     *        A Video.js player.
11878     */
11879  
11880    /**
11881     * Define a middleware that the player should use by way of a factory function
11882     * that returns a middleware object.
11883     *
11884     * @param  {string} type
11885     *         The MIME type to match or `"*"` for all MIME types.
11886     *
11887     * @param  {MiddlewareFactory} middleware
11888     *         A middleware factory function that will be executed for
11889     *         matching types.
11890     */
11891  
11892    function use(type, middleware) {
11893      middlewares[type] = middlewares[type] || [];
11894      middlewares[type].push(middleware);
11895    }
11896    /**
11897     * Asynchronously sets a source using middleware by recursing through any
11898     * matching middlewares and calling `setSource` on each, passing along the
11899     * previous returned value each time.
11900     *
11901     * @param  {Player} player
11902     *         A {@link Player} instance.
11903     *
11904     * @param  {Tech~SourceObject} src
11905     *         A source object.
11906     *
11907     * @param  {Function}
11908     *         The next middleware to run.
11909     */
11910  
11911    function setSource(player, src, next) {
11912      player.setTimeout(function () {
11913        return setSourceHelper(src, middlewares[src.type], next, player);
11914      }, 1);
11915    }
11916    /**
11917     * When the tech is set, passes the tech to each middleware's `setTech` method.
11918     *
11919     * @param {Object[]} middleware
11920     *        An array of middleware instances.
11921     *
11922     * @param {Tech} tech
11923     *        A Video.js tech.
11924     */
11925  
11926    function setTech(middleware, tech) {
11927      middleware.forEach(function (mw) {
11928        return mw.setTech && mw.setTech(tech);
11929      });
11930    }
11931    /**
11932     * Calls a getter on the tech first, through each middleware
11933     * from right to left to the player.
11934     *
11935     * @param  {Object[]} middleware
11936     *         An array of middleware instances.
11937     *
11938     * @param  {Tech} tech
11939     *         The current tech.
11940     *
11941     * @param  {string} method
11942     *         A method name.
11943     *
11944     * @return {Mixed}
11945     *         The final value from the tech after middleware has intercepted it.
11946     */
11947  
11948    function get(middleware, tech, method) {
11949      return middleware.reduceRight(middlewareIterator(method), tech[method]());
11950    }
11951    /**
11952     * Takes the argument given to the player and calls the setter method on each
11953     * middleware from left to right to the tech.
11954     *
11955     * @param  {Object[]} middleware
11956     *         An array of middleware instances.
11957     *
11958     * @param  {Tech} tech
11959     *         The current tech.
11960     *
11961     * @param  {string} method
11962     *         A method name.
11963     *
11964     * @param  {Mixed} arg
11965     *         The value to set on the tech.
11966     *
11967     * @return {Mixed}
11968     *         The return value of the `method` of the `tech`.
11969     */
11970  
11971    function set(middleware, tech, method, arg) {
11972      return tech[method](middleware.reduce(middlewareIterator(method), arg));
11973    }
11974    /**
11975     * Takes the argument given to the player and calls the `call` version of the
11976     * method on each middleware from left to right.
11977     *
11978     * Then, call the passed in method on the tech and return the result unchanged
11979     * back to the player, through middleware, this time from right to left.
11980     *
11981     * @param  {Object[]} middleware
11982     *         An array of middleware instances.
11983     *
11984     * @param  {Tech} tech
11985     *         The current tech.
11986     *
11987     * @param  {string} method
11988     *         A method name.
11989     *
11990     * @param  {Mixed} arg
11991     *         The value to set on the tech.
11992     *
11993     * @return {Mixed}
11994     *         The return value of the `method` of the `tech`, regardless of the
11995     *         return values of middlewares.
11996     */
11997  
11998    function mediate(middleware, tech, method, arg) {
11999      if (arg === void 0) {
12000        arg = null;
12001      }
12002  
12003      var callMethod = 'call' + toTitleCase(method);
12004      var middlewareValue = middleware.reduce(middlewareIterator(callMethod), arg);
12005      var terminated = middlewareValue === TERMINATOR; // deprecated. The `null` return value should instead return TERMINATOR to
12006      // prevent confusion if a techs method actually returns null.
12007  
12008      var returnValue = terminated ? null : tech[method](middlewareValue);
12009      executeRight(middleware, method, returnValue, terminated);
12010      return returnValue;
12011    }
12012    /**
12013     * Enumeration of allowed getters where the keys are method names.
12014     *
12015     * @type {Object}
12016     */
12017  
12018    var allowedGetters = {
12019      buffered: 1,
12020      currentTime: 1,
12021      duration: 1,
12022      muted: 1,
12023      played: 1,
12024      paused: 1,
12025      seekable: 1,
12026      volume: 1
12027    };
12028    /**
12029     * Enumeration of allowed setters where the keys are method names.
12030     *
12031     * @type {Object}
12032     */
12033  
12034    var allowedSetters = {
12035      setCurrentTime: 1,
12036      setMuted: 1,
12037      setVolume: 1
12038    };
12039    /**
12040     * Enumeration of allowed mediators where the keys are method names.
12041     *
12042     * @type {Object}
12043     */
12044  
12045    var allowedMediators = {
12046      play: 1,
12047      pause: 1
12048    };
12049  
12050    function middlewareIterator(method) {
12051      return function (value, mw) {
12052        // if the previous middleware terminated, pass along the termination
12053        if (value === TERMINATOR) {
12054          return TERMINATOR;
12055        }
12056  
12057        if (mw[method]) {
12058          return mw[method](value);
12059        }
12060  
12061        return value;
12062      };
12063    }
12064  
12065    function executeRight(mws, method, value, terminated) {
12066      for (var i = mws.length - 1; i >= 0; i--) {
12067        var mw = mws[i];
12068  
12069        if (mw[method]) {
12070          mw[method](terminated, value);
12071        }
12072      }
12073    }
12074    /**
12075     * Clear the middleware cache for a player.
12076     *
12077     * @param  {Player} player
12078     *         A {@link Player} instance.
12079     */
12080  
12081  
12082    function clearCacheForPlayer(player) {
12083      middlewareInstances[player.id()] = null;
12084    }
12085    /**
12086     * {
12087     *  [playerId]: [[mwFactory, mwInstance], ...]
12088     * }
12089     *
12090     * @private
12091     */
12092  
12093    function getOrCreateFactory(player, mwFactory) {
12094      var mws = middlewareInstances[player.id()];
12095      var mw = null;
12096  
12097      if (mws === undefined || mws === null) {
12098        mw = mwFactory(player);
12099        middlewareInstances[player.id()] = [[mwFactory, mw]];
12100        return mw;
12101      }
12102  
12103      for (var i = 0; i < mws.length; i++) {
12104        var _mws$i = mws[i],
12105            mwf = _mws$i[0],
12106            mwi = _mws$i[1];
12107  
12108        if (mwf !== mwFactory) {
12109          continue;
12110        }
12111  
12112        mw = mwi;
12113      }
12114  
12115      if (mw === null) {
12116        mw = mwFactory(player);
12117        mws.push([mwFactory, mw]);
12118      }
12119  
12120      return mw;
12121    }
12122  
12123    function setSourceHelper(src, middleware, next, player, acc, lastRun) {
12124      if (src === void 0) {
12125        src = {};
12126      }
12127  
12128      if (middleware === void 0) {
12129        middleware = [];
12130      }
12131  
12132      if (acc === void 0) {
12133        acc = [];
12134      }
12135  
12136      if (lastRun === void 0) {
12137        lastRun = false;
12138      }
12139  
12140      var _middleware = middleware,
12141          mwFactory = _middleware[0],
12142          mwrest = _middleware.slice(1); // if mwFactory is a string, then we're at a fork in the road
12143  
12144  
12145      if (typeof mwFactory === 'string') {
12146        setSourceHelper(src, middlewares[mwFactory], next, player, acc, lastRun); // if we have an mwFactory, call it with the player to get the mw,
12147        // then call the mw's setSource method
12148      } else if (mwFactory) {
12149        var mw = getOrCreateFactory(player, mwFactory); // if setSource isn't present, implicitly select this middleware
12150  
12151        if (!mw.setSource) {
12152          acc.push(mw);
12153          return setSourceHelper(src, mwrest, next, player, acc, lastRun);
12154        }
12155  
12156        mw.setSource(assign({}, src), function (err, _src) {
12157          // something happened, try the next middleware on the current level
12158          // make sure to use the old src
12159          if (err) {
12160            return setSourceHelper(src, mwrest, next, player, acc, lastRun);
12161          } // we've succeeded, now we need to go deeper
12162  
12163  
12164          acc.push(mw); // if it's the same type, continue down the current chain
12165          // otherwise, we want to go down the new chain
12166  
12167          setSourceHelper(_src, src.type === _src.type ? mwrest : middlewares[_src.type], next, player, acc, lastRun);
12168        });
12169      } else if (mwrest.length) {
12170        setSourceHelper(src, mwrest, next, player, acc, lastRun);
12171      } else if (lastRun) {
12172        next(src, acc);
12173      } else {
12174        setSourceHelper(src, middlewares['*'], next, player, acc, true);
12175      }
12176    }
12177  
12178    /**
12179     * Mimetypes
12180     *
12181     * @see http://hul.harvard.edu/ois/////systems/wax/wax-public-help/mimetypes.htm
12182     * @typedef Mimetypes~Kind
12183     * @enum
12184     */
12185  
12186    var MimetypesKind = {
12187      opus: 'video/ogg',
12188      ogv: 'video/ogg',
12189      mp4: 'video/mp4',
12190      mov: 'video/mp4',
12191      m4v: 'video/mp4',
12192      mkv: 'video/x-matroska',
12193      m4a: 'audio/mp4',
12194      mp3: 'audio/mpeg',
12195      aac: 'audio/aac',
12196      oga: 'audio/ogg',
12197      m3u8: 'application/x-mpegURL',
12198      jpg: 'image/jpeg',
12199      jpeg: 'image/jpeg',
12200      gif: 'image/gif',
12201      png: 'image/png',
12202      svg: 'image/svg+xml',
12203      webp: 'image/webp'
12204    };
12205    /**
12206     * Get the mimetype of a given src url if possible
12207     *
12208     * @param {string} src
12209     *        The url to the src
12210     *
12211     * @return {string}
12212     *         return the mimetype if it was known or empty string otherwise
12213     */
12214  
12215    var getMimetype = function getMimetype(src) {
12216      if (src === void 0) {
12217        src = '';
12218      }
12219  
12220      var ext = getFileExtension(src);
12221      var mimetype = MimetypesKind[ext.toLowerCase()];
12222      return mimetype || '';
12223    };
12224    /**
12225     * Find the mime type of a given source string if possible. Uses the player
12226     * source cache.
12227     *
12228     * @param {Player} player
12229     *        The player object
12230     *
12231     * @param {string} src
12232     *        The source string
12233     *
12234     * @return {string}
12235     *         The type that was found
12236     */
12237  
12238    var findMimetype = function findMimetype(player, src) {
12239      if (!src) {
12240        return '';
12241      } // 1. check for the type in the `source` cache
12242  
12243  
12244      if (player.cache_.source.src === src && player.cache_.source.type) {
12245        return player.cache_.source.type;
12246      } // 2. see if we have this source in our `currentSources` cache
12247  
12248  
12249      var matchingSources = player.cache_.sources.filter(function (s) {
12250        return s.src === src;
12251      });
12252  
12253      if (matchingSources.length) {
12254        return matchingSources[0].type;
12255      } // 3. look for the src url in source elements and use the type there
12256  
12257  
12258      var sources = player.$$('source');
12259  
12260      for (var i = 0; i < sources.length; i++) {
12261        var s = sources[i];
12262  
12263        if (s.type && s.src && s.src === src) {
12264          return s.type;
12265        }
12266      } // 4. finally fallback to our list of mime types based on src url extension
12267  
12268  
12269      return getMimetype(src);
12270    };
12271  
12272    /**
12273     * @module filter-source
12274     */
12275    /**
12276     * Filter out single bad source objects or multiple source objects in an
12277     * array. Also flattens nested source object arrays into a 1 dimensional
12278     * array of source objects.
12279     *
12280     * @param {Tech~SourceObject|Tech~SourceObject[]} src
12281     *        The src object to filter
12282     *
12283     * @return {Tech~SourceObject[]}
12284     *         An array of sourceobjects containing only valid sources
12285     *
12286     * @private
12287     */
12288  
12289    var filterSource = function filterSource(src) {
12290      // traverse array
12291      if (Array.isArray(src)) {
12292        var newsrc = [];
12293        src.forEach(function (srcobj) {
12294          srcobj = filterSource(srcobj);
12295  
12296          if (Array.isArray(srcobj)) {
12297            newsrc = newsrc.concat(srcobj);
12298          } else if (isObject(srcobj)) {
12299            newsrc.push(srcobj);
12300          }
12301        });
12302        src = newsrc;
12303      } else if (typeof src === 'string' && src.trim()) {
12304        // convert string into object
12305        src = [fixSource({
12306          src: src
12307        })];
12308      } else if (isObject(src) && typeof src.src === 'string' && src.src && src.src.trim()) {
12309        // src is already valid
12310        src = [fixSource(src)];
12311      } else {
12312        // invalid source, turn it into an empty array
12313        src = [];
12314      }
12315  
12316      return src;
12317    };
12318    /**
12319     * Checks src mimetype, adding it when possible
12320     *
12321     * @param {Tech~SourceObject} src
12322     *        The src object to check
12323     * @return {Tech~SourceObject}
12324     *        src Object with known type
12325     */
12326  
12327  
12328    function fixSource(src) {
12329      if (!src.type) {
12330        var mimetype = getMimetype(src.src);
12331  
12332        if (mimetype) {
12333          src.type = mimetype;
12334        }
12335      }
12336  
12337      return src;
12338    }
12339  
12340    /**
12341     * The `MediaLoader` is the `Component` that decides which playback technology to load
12342     * when a player is initialized.
12343     *
12344     * @extends Component
12345     */
12346  
12347    var MediaLoader = /*#__PURE__*/function (_Component) {
12348      inheritsLoose(MediaLoader, _Component);
12349  
12350      /**
12351       * Create an instance of this class.
12352       *
12353       * @param {Player} player
12354       *        The `Player` that this class should attach to.
12355       *
12356       * @param {Object} [options]
12357       *        The key/value store of player options.
12358       *
12359       * @param {Component~ReadyCallback} [ready]
12360       *        The function that is run when this component is ready.
12361       */
12362      function MediaLoader(player, options, ready) {
12363        var _this;
12364  
12365        // MediaLoader has no element
12366        var options_ = mergeOptions({
12367          createEl: false
12368        }, options);
12369        _this = _Component.call(this, player, options_, ready) || this; // If there are no sources when the player is initialized,
12370        // load the first supported playback technology.
12371  
12372        if (!options.playerOptions.sources || options.playerOptions.sources.length === 0) {
12373          for (var i = 0, j = options.playerOptions.techOrder; i < j.length; i++) {
12374            var techName = toTitleCase(j[i]);
12375            var tech = Tech.getTech(techName); // Support old behavior of techs being registered as components.
12376            // Remove once that deprecated behavior is removed.
12377  
12378            if (!techName) {
12379              tech = Component.getComponent(techName);
12380            } // Check if the browser supports this technology
12381  
12382  
12383            if (tech && tech.isSupported()) {
12384              player.loadTech_(techName);
12385              break;
12386            }
12387          }
12388        } else {
12389          // Loop through playback technologies (HTML5, Flash) and check for support.
12390          // Then load the best source.
12391          // A few assumptions here:
12392          //   All playback technologies respect preload false.
12393          player.src(options.playerOptions.sources);
12394        }
12395  
12396        return _this;
12397      }
12398  
12399      return MediaLoader;
12400    }(Component);
12401  
12402    Component.registerComponent('MediaLoader', MediaLoader);
12403  
12404    /**
12405     * Component which is clickable or keyboard actionable, but is not a
12406     * native HTML button.
12407     *
12408     * @extends Component
12409     */
12410  
12411    var ClickableComponent = /*#__PURE__*/function (_Component) {
12412      inheritsLoose(ClickableComponent, _Component);
12413  
12414      /**
12415       * Creates an instance of this class.
12416       *
12417       * @param  {Player} player
12418       *         The `Player` that this class should be attached to.
12419       *
12420       * @param  {Object} [options]
12421       *         The key/value store of player options.
12422       *
12423       * @param  {function} [options.clickHandler]
12424       *         The function to call when the button is clicked / activated
12425       */
12426      function ClickableComponent(player, options) {
12427        var _this;
12428  
12429        _this = _Component.call(this, player, options) || this;
12430  
12431        _this.emitTapEvents();
12432  
12433        _this.enable();
12434  
12435        return _this;
12436      }
12437      /**
12438       * Create the `ClickableComponent`s DOM element.
12439       *
12440       * @param {string} [tag=div]
12441       *        The element's node type.
12442       *
12443       * @param {Object} [props={}]
12444       *        An object of properties that should be set on the element.
12445       *
12446       * @param {Object} [attributes={}]
12447       *        An object of attributes that should be set on the element.
12448       *
12449       * @return {Element}
12450       *         The element that gets created.
12451       */
12452  
12453  
12454      var _proto = ClickableComponent.prototype;
12455  
12456      _proto.createEl = function createEl(tag, props, attributes) {
12457        if (tag === void 0) {
12458          tag = 'div';
12459        }
12460  
12461        if (props === void 0) {
12462          props = {};
12463        }
12464  
12465        if (attributes === void 0) {
12466          attributes = {};
12467        }
12468  
12469        props = assign({
12470          innerHTML: '<span aria-hidden="true" class="vjs-icon-placeholder"></span>',
12471          className: this.buildCSSClass(),
12472          tabIndex: 0
12473        }, props);
12474  
12475        if (tag === 'button') {
12476          log.error("Creating a ClickableComponent with an HTML element of " + tag + " is not supported; use a Button instead.");
12477        } // Add ARIA attributes for clickable element which is not a native HTML button
12478  
12479  
12480        attributes = assign({
12481          role: 'button'
12482        }, attributes);
12483        this.tabIndex_ = props.tabIndex;
12484  
12485        var el = _Component.prototype.createEl.call(this, tag, props, attributes);
12486  
12487        this.createControlTextEl(el);
12488        return el;
12489      };
12490  
12491      _proto.dispose = function dispose() {
12492        // remove controlTextEl_ on dispose
12493        this.controlTextEl_ = null;
12494  
12495        _Component.prototype.dispose.call(this);
12496      }
12497      /**
12498       * Create a control text element on this `ClickableComponent`
12499       *
12500       * @param {Element} [el]
12501       *        Parent element for the control text.
12502       *
12503       * @return {Element}
12504       *         The control text element that gets created.
12505       */
12506      ;
12507  
12508      _proto.createControlTextEl = function createControlTextEl(el) {
12509        this.controlTextEl_ = createEl('span', {
12510          className: 'vjs-control-text'
12511        }, {
12512          // let the screen reader user know that the text of the element may change
12513          'aria-live': 'polite'
12514        });
12515  
12516        if (el) {
12517          el.appendChild(this.controlTextEl_);
12518        }
12519  
12520        this.controlText(this.controlText_, el);
12521        return this.controlTextEl_;
12522      }
12523      /**
12524       * Get or set the localize text to use for the controls on the `ClickableComponent`.
12525       *
12526       * @param {string} [text]
12527       *        Control text for element.
12528       *
12529       * @param {Element} [el=this.el()]
12530       *        Element to set the title on.
12531       *
12532       * @return {string}
12533       *         - The control text when getting
12534       */
12535      ;
12536  
12537      _proto.controlText = function controlText(text, el) {
12538        if (el === void 0) {
12539          el = this.el();
12540        }
12541  
12542        if (text === undefined) {
12543          return this.controlText_ || 'Need Text';
12544        }
12545  
12546        var localizedText = this.localize(text);
12547        this.controlText_ = text;
12548        textContent(this.controlTextEl_, localizedText);
12549  
12550        if (!this.nonIconControl) {
12551          // Set title attribute if only an icon is shown
12552          el.setAttribute('title', localizedText);
12553        }
12554      }
12555      /**
12556       * Builds the default DOM `className`.
12557       *
12558       * @return {string}
12559       *         The DOM `className` for this object.
12560       */
12561      ;
12562  
12563      _proto.buildCSSClass = function buildCSSClass() {
12564        return "vjs-control vjs-button " + _Component.prototype.buildCSSClass.call(this);
12565      }
12566      /**
12567       * Enable this `ClickableComponent`
12568       */
12569      ;
12570  
12571      _proto.enable = function enable() {
12572        if (!this.enabled_) {
12573          this.enabled_ = true;
12574          this.removeClass('vjs-disabled');
12575          this.el_.setAttribute('aria-disabled', 'false');
12576  
12577          if (typeof this.tabIndex_ !== 'undefined') {
12578            this.el_.setAttribute('tabIndex', this.tabIndex_);
12579          }
12580  
12581          this.on(['tap', 'click'], this.handleClick);
12582          this.on('keydown', this.handleKeyDown);
12583        }
12584      }
12585      /**
12586       * Disable this `ClickableComponent`
12587       */
12588      ;
12589  
12590      _proto.disable = function disable() {
12591        this.enabled_ = false;
12592        this.addClass('vjs-disabled');
12593        this.el_.setAttribute('aria-disabled', 'true');
12594  
12595        if (typeof this.tabIndex_ !== 'undefined') {
12596          this.el_.removeAttribute('tabIndex');
12597        }
12598  
12599        this.off('mouseover', this.handleMouseOver);
12600        this.off('mouseout', this.handleMouseOut);
12601        this.off(['tap', 'click'], this.handleClick);
12602        this.off('keydown', this.handleKeyDown);
12603      }
12604      /**
12605       * Event handler that is called when a `ClickableComponent` receives a
12606       * `click` or `tap` event.
12607       *
12608       * @param {EventTarget~Event} event
12609       *        The `tap` or `click` event that caused this function to be called.
12610       *
12611       * @listens tap
12612       * @listens click
12613       * @abstract
12614       */
12615      ;
12616  
12617      _proto.handleClick = function handleClick(event) {
12618        if (this.options_.clickHandler) {
12619          this.options_.clickHandler.call(this, arguments);
12620        }
12621      }
12622      /**
12623       * Event handler that is called when a `ClickableComponent` receives a
12624       * `keydown` event.
12625       *
12626       * By default, if the key is Space or Enter, it will trigger a `click` event.
12627       *
12628       * @param {EventTarget~Event} event
12629       *        The `keydown` event that caused this function to be called.
12630       *
12631       * @listens keydown
12632       */
12633      ;
12634  
12635      _proto.handleKeyDown = function handleKeyDown(event) {
12636        // Support Space or Enter key operation to fire a click event. Also,
12637        // prevent the event from propagating through the DOM and triggering
12638        // Player hotkeys.
12639        if (keycode.isEventKey(event, 'Space') || keycode.isEventKey(event, 'Enter')) {
12640          event.preventDefault();
12641          event.stopPropagation();
12642          this.trigger('click');
12643        } else {
12644          // Pass keypress handling up for unsupported keys
12645          _Component.prototype.handleKeyDown.call(this, event);
12646        }
12647      };
12648  
12649      return ClickableComponent;
12650    }(Component);
12651  
12652    Component.registerComponent('ClickableComponent', ClickableComponent);
12653  
12654    /**
12655     * A `ClickableComponent` that handles showing the poster image for the player.
12656     *
12657     * @extends ClickableComponent
12658     */
12659  
12660    var PosterImage = /*#__PURE__*/function (_ClickableComponent) {
12661      inheritsLoose(PosterImage, _ClickableComponent);
12662  
12663      /**
12664       * Create an instance of this class.
12665       *
12666       * @param {Player} player
12667       *        The `Player` that this class should attach to.
12668       *
12669       * @param {Object} [options]
12670       *        The key/value store of player options.
12671       */
12672      function PosterImage(player, options) {
12673        var _this;
12674  
12675        _this = _ClickableComponent.call(this, player, options) || this;
12676  
12677        _this.update();
12678  
12679        player.on('posterchange', bind(assertThisInitialized(_this), _this.update));
12680        return _this;
12681      }
12682      /**
12683       * Clean up and dispose of the `PosterImage`.
12684       */
12685  
12686  
12687      var _proto = PosterImage.prototype;
12688  
12689      _proto.dispose = function dispose() {
12690        this.player().off('posterchange', this.update);
12691  
12692        _ClickableComponent.prototype.dispose.call(this);
12693      }
12694      /**
12695       * Create the `PosterImage`s DOM element.
12696       *
12697       * @return {Element}
12698       *         The element that gets created.
12699       */
12700      ;
12701  
12702      _proto.createEl = function createEl$1() {
12703        var el = createEl('div', {
12704          className: 'vjs-poster',
12705          // Don't want poster to be tabbable.
12706          tabIndex: -1
12707        });
12708        return el;
12709      }
12710      /**
12711       * An {@link EventTarget~EventListener} for {@link Player#posterchange} events.
12712       *
12713       * @listens Player#posterchange
12714       *
12715       * @param {EventTarget~Event} [event]
12716       *        The `Player#posterchange` event that triggered this function.
12717       */
12718      ;
12719  
12720      _proto.update = function update(event) {
12721        var url = this.player().poster();
12722        this.setSrc(url); // If there's no poster source we should display:none on this component
12723        // so it's not still clickable or right-clickable
12724  
12725        if (url) {
12726          this.show();
12727        } else {
12728          this.hide();
12729        }
12730      }
12731      /**
12732       * Set the source of the `PosterImage` depending on the display method.
12733       *
12734       * @param {string} url
12735       *        The URL to the source for the `PosterImage`.
12736       */
12737      ;
12738  
12739      _proto.setSrc = function setSrc(url) {
12740        var backgroundImage = ''; // Any falsy value should stay as an empty string, otherwise
12741        // this will throw an extra error
12742  
12743        if (url) {
12744          backgroundImage = "url(\"" + url + "\")";
12745        }
12746  
12747        this.el_.style.backgroundImage = backgroundImage;
12748      }
12749      /**
12750       * An {@link EventTarget~EventListener} for clicks on the `PosterImage`. See
12751       * {@link ClickableComponent#handleClick} for instances where this will be triggered.
12752       *
12753       * @listens tap
12754       * @listens click
12755       * @listens keydown
12756       *
12757       * @param {EventTarget~Event} event
12758       +        The `click`, `tap` or `keydown` event that caused this function to be called.
12759       */
12760      ;
12761  
12762      _proto.handleClick = function handleClick(event) {
12763        // We don't want a click to trigger playback when controls are disabled
12764        if (!this.player_.controls()) {
12765          return;
12766        }
12767  
12768        var sourceIsEncrypted = this.player_.usingPlugin('eme') && this.player_.eme.sessions && this.player_.eme.sessions.length > 0;
12769  
12770        if (this.player_.tech(true) && // We've observed a bug in IE and Edge when playing back DRM content where
12771        // calling .focus() on the video element causes the video to go black,
12772        // so we avoid it in that specific case
12773        !((IE_VERSION || IS_EDGE) && sourceIsEncrypted)) {
12774          this.player_.tech(true).focus();
12775        }
12776  
12777        if (this.player_.paused()) {
12778          silencePromise(this.player_.play());
12779        } else {
12780          this.player_.pause();
12781        }
12782      };
12783  
12784      return PosterImage;
12785    }(ClickableComponent);
12786  
12787    Component.registerComponent('PosterImage', PosterImage);
12788  
12789    var darkGray = '#222';
12790    var lightGray = '#ccc';
12791    var fontMap = {
12792      monospace: 'monospace',
12793      sansSerif: 'sans-serif',
12794      serif: 'serif',
12795      monospaceSansSerif: '"Andale Mono", "Lucida Console", monospace',
12796      monospaceSerif: '"Courier New", monospace',
12797      proportionalSansSerif: 'sans-serif',
12798      proportionalSerif: 'serif',
12799      casual: '"Comic Sans MS", Impact, fantasy',
12800      script: '"Monotype Corsiva", cursive',
12801      smallcaps: '"Andale Mono", "Lucida Console", monospace, sans-serif'
12802    };
12803    /**
12804     * Construct an rgba color from a given hex color code.
12805     *
12806     * @param {number} color
12807     *        Hex number for color, like #f0e or #f604e2.
12808     *
12809     * @param {number} opacity
12810     *        Value for opacity, 0.0 - 1.0.
12811     *
12812     * @return {string}
12813     *         The rgba color that was created, like 'rgba(255, 0, 0, 0.3)'.
12814     */
12815  
12816    function constructColor(color, opacity) {
12817      var hex;
12818  
12819      if (color.length === 4) {
12820        // color looks like "#f0e"
12821        hex = color[1] + color[1] + color[2] + color[2] + color[3] + color[3];
12822      } else if (color.length === 7) {
12823        // color looks like "#f604e2"
12824        hex = color.slice(1);
12825      } else {
12826        throw new Error('Invalid color code provided, ' + color + '; must be formatted as e.g. #f0e or #f604e2.');
12827      }
12828  
12829      return 'rgba(' + parseInt(hex.slice(0, 2), 16) + ',' + parseInt(hex.slice(2, 4), 16) + ',' + parseInt(hex.slice(4, 6), 16) + ',' + opacity + ')';
12830    }
12831    /**
12832     * Try to update the style of a DOM element. Some style changes will throw an error,
12833     * particularly in IE8. Those should be noops.
12834     *
12835     * @param {Element} el
12836     *        The DOM element to be styled.
12837     *
12838     * @param {string} style
12839     *        The CSS property on the element that should be styled.
12840     *
12841     * @param {string} rule
12842     *        The style rule that should be applied to the property.
12843     *
12844     * @private
12845     */
12846  
12847    function tryUpdateStyle(el, style, rule) {
12848      try {
12849        el.style[style] = rule;
12850      } catch (e) {
12851        // Satisfies linter.
12852        return;
12853      }
12854    }
12855    /**
12856     * The component for displaying text track cues.
12857     *
12858     * @extends Component
12859     */
12860  
12861  
12862    var TextTrackDisplay = /*#__PURE__*/function (_Component) {
12863      inheritsLoose(TextTrackDisplay, _Component);
12864  
12865      /**
12866       * Creates an instance of this class.
12867       *
12868       * @param {Player} player
12869       *        The `Player` that this class should be attached to.
12870       *
12871       * @param {Object} [options]
12872       *        The key/value store of player options.
12873       *
12874       * @param {Component~ReadyCallback} [ready]
12875       *        The function to call when `TextTrackDisplay` is ready.
12876       */
12877      function TextTrackDisplay(player, options, ready) {
12878        var _this;
12879  
12880        _this = _Component.call(this, player, options, ready) || this;
12881        var updateDisplayHandler = bind(assertThisInitialized(_this), _this.updateDisplay);
12882        player.on('loadstart', bind(assertThisInitialized(_this), _this.toggleDisplay));
12883        player.on('texttrackchange', updateDisplayHandler);
12884        player.on('loadedmetadata', bind(assertThisInitialized(_this), _this.preselectTrack)); // This used to be called during player init, but was causing an error
12885        // if a track should show by default and the display hadn't loaded yet.
12886        // Should probably be moved to an external track loader when we support
12887        // tracks that don't need a display.
12888  
12889        player.ready(bind(assertThisInitialized(_this), function () {
12890          if (player.tech_ && player.tech_.featuresNativeTextTracks) {
12891            this.hide();
12892            return;
12893          }
12894  
12895          player.on('fullscreenchange', updateDisplayHandler);
12896          player.on('playerresize', updateDisplayHandler);
12897          window$3.addEventListener('orientationchange', updateDisplayHandler);
12898          player.on('dispose', function () {
12899            return window$3.removeEventListener('orientationchange', updateDisplayHandler);
12900          });
12901          var tracks = this.options_.playerOptions.tracks || [];
12902  
12903          for (var i = 0; i < tracks.length; i++) {
12904            this.player_.addRemoteTextTrack(tracks[i], true);
12905          }
12906  
12907          this.preselectTrack();
12908        }));
12909        return _this;
12910      }
12911      /**
12912      * Preselect a track following this precedence:
12913      * - matches the previously selected {@link TextTrack}'s language and kind
12914      * - matches the previously selected {@link TextTrack}'s language only
12915      * - is the first default captions track
12916      * - is the first default descriptions track
12917      *
12918      * @listens Player#loadstart
12919      */
12920  
12921  
12922      var _proto = TextTrackDisplay.prototype;
12923  
12924      _proto.preselectTrack = function preselectTrack() {
12925        var modes = {
12926          captions: 1,
12927          subtitles: 1
12928        };
12929        var trackList = this.player_.textTracks();
12930        var userPref = this.player_.cache_.selectedLanguage;
12931        var firstDesc;
12932        var firstCaptions;
12933        var preferredTrack;
12934  
12935        for (var i = 0; i < trackList.length; i++) {
12936          var track = trackList[i];
12937  
12938          if (userPref && userPref.enabled && userPref.language && userPref.language === track.language && track.kind in modes) {
12939            // Always choose the track that matches both language and kind
12940            if (track.kind === userPref.kind) {
12941              preferredTrack = track; // or choose the first track that matches language
12942            } else if (!preferredTrack) {
12943              preferredTrack = track;
12944            } // clear everything if offTextTrackMenuItem was clicked
12945  
12946          } else if (userPref && !userPref.enabled) {
12947            preferredTrack = null;
12948            firstDesc = null;
12949            firstCaptions = null;
12950          } else if (track["default"]) {
12951            if (track.kind === 'descriptions' && !firstDesc) {
12952              firstDesc = track;
12953            } else if (track.kind in modes && !firstCaptions) {
12954              firstCaptions = track;
12955            }
12956          }
12957        } // The preferredTrack matches the user preference and takes
12958        // precedence over all the other tracks.
12959        // So, display the preferredTrack before the first default track
12960        // and the subtitles/captions track before the descriptions track
12961  
12962  
12963        if (preferredTrack) {
12964          preferredTrack.mode = 'showing';
12965        } else if (firstCaptions) {
12966          firstCaptions.mode = 'showing';
12967        } else if (firstDesc) {
12968          firstDesc.mode = 'showing';
12969        }
12970      }
12971      /**
12972       * Turn display of {@link TextTrack}'s from the current state into the other state.
12973       * There are only two states:
12974       * - 'shown'
12975       * - 'hidden'
12976       *
12977       * @listens Player#loadstart
12978       */
12979      ;
12980  
12981      _proto.toggleDisplay = function toggleDisplay() {
12982        if (this.player_.tech_ && this.player_.tech_.featuresNativeTextTracks) {
12983          this.hide();
12984        } else {
12985          this.show();
12986        }
12987      }
12988      /**
12989       * Create the {@link Component}'s DOM element.
12990       *
12991       * @return {Element}
12992       *         The element that was created.
12993       */
12994      ;
12995  
12996      _proto.createEl = function createEl() {
12997        return _Component.prototype.createEl.call(this, 'div', {
12998          className: 'vjs-text-track-display'
12999        }, {
13000          'aria-live': 'off',
13001          'aria-atomic': 'true'
13002        });
13003      }
13004      /**
13005       * Clear all displayed {@link TextTrack}s.
13006       */
13007      ;
13008  
13009      _proto.clearDisplay = function clearDisplay() {
13010        if (typeof window$3.WebVTT === 'function') {
13011          window$3.WebVTT.processCues(window$3, [], this.el_);
13012        }
13013      }
13014      /**
13015       * Update the displayed TextTrack when a either a {@link Player#texttrackchange} or
13016       * a {@link Player#fullscreenchange} is fired.
13017       *
13018       * @listens Player#texttrackchange
13019       * @listens Player#fullscreenchange
13020       */
13021      ;
13022  
13023      _proto.updateDisplay = function updateDisplay() {
13024        var tracks = this.player_.textTracks();
13025        var allowMultipleShowingTracks = this.options_.allowMultipleShowingTracks;
13026        this.clearDisplay();
13027  
13028        if (allowMultipleShowingTracks) {
13029          var showingTracks = [];
13030  
13031          for (var _i = 0; _i < tracks.length; ++_i) {
13032            var track = tracks[_i];
13033  
13034            if (track.mode !== 'showing') {
13035              continue;
13036            }
13037  
13038            showingTracks.push(track);
13039          }
13040  
13041          this.updateForTrack(showingTracks);
13042          return;
13043        } //  Track display prioritization model: if multiple tracks are 'showing',
13044        //  display the first 'subtitles' or 'captions' track which is 'showing',
13045        //  otherwise display the first 'descriptions' track which is 'showing'
13046  
13047  
13048        var descriptionsTrack = null;
13049        var captionsSubtitlesTrack = null;
13050        var i = tracks.length;
13051  
13052        while (i--) {
13053          var _track = tracks[i];
13054  
13055          if (_track.mode === 'showing') {
13056            if (_track.kind === 'descriptions') {
13057              descriptionsTrack = _track;
13058            } else {
13059              captionsSubtitlesTrack = _track;
13060            }
13061          }
13062        }
13063  
13064        if (captionsSubtitlesTrack) {
13065          if (this.getAttribute('aria-live') !== 'off') {
13066            this.setAttribute('aria-live', 'off');
13067          }
13068  
13069          this.updateForTrack(captionsSubtitlesTrack);
13070        } else if (descriptionsTrack) {
13071          if (this.getAttribute('aria-live') !== 'assertive') {
13072            this.setAttribute('aria-live', 'assertive');
13073          }
13074  
13075          this.updateForTrack(descriptionsTrack);
13076        }
13077      }
13078      /**
13079       * Style {@Link TextTrack} activeCues according to {@Link TextTrackSettings}.
13080       *
13081       * @param {TextTrack} track
13082       *        Text track object containing active cues to style.
13083       */
13084      ;
13085  
13086      _proto.updateDisplayState = function updateDisplayState(track) {
13087        var overrides = this.player_.textTrackSettings.getValues();
13088        var cues = track.activeCues;
13089        var i = cues.length;
13090  
13091        while (i--) {
13092          var cue = cues[i];
13093  
13094          if (!cue) {
13095            continue;
13096          }
13097  
13098          var cueDiv = cue.displayState;
13099  
13100          if (overrides.color) {
13101            cueDiv.firstChild.style.color = overrides.color;
13102          }
13103  
13104          if (overrides.textOpacity) {
13105            tryUpdateStyle(cueDiv.firstChild, 'color', constructColor(overrides.color || '#fff', overrides.textOpacity));
13106          }
13107  
13108          if (overrides.backgroundColor) {
13109            cueDiv.firstChild.style.backgroundColor = overrides.backgroundColor;
13110          }
13111  
13112          if (overrides.backgroundOpacity) {
13113            tryUpdateStyle(cueDiv.firstChild, 'backgroundColor', constructColor(overrides.backgroundColor || '#000', overrides.backgroundOpacity));
13114          }
13115  
13116          if (overrides.windowColor) {
13117            if (overrides.windowOpacity) {
13118              tryUpdateStyle(cueDiv, 'backgroundColor', constructColor(overrides.windowColor, overrides.windowOpacity));
13119            } else {
13120              cueDiv.style.backgroundColor = overrides.windowColor;
13121            }
13122          }
13123  
13124          if (overrides.edgeStyle) {
13125            if (overrides.edgeStyle === 'dropshadow') {
13126              cueDiv.firstChild.style.textShadow = "2px 2px 3px " + darkGray + ", 2px 2px 4px " + darkGray + ", 2px 2px 5px " + darkGray;
13127            } else if (overrides.edgeStyle === 'raised') {
13128              cueDiv.firstChild.style.textShadow = "1px 1px " + darkGray + ", 2px 2px " + darkGray + ", 3px 3px " + darkGray;
13129            } else if (overrides.edgeStyle === 'depressed') {
13130              cueDiv.firstChild.style.textShadow = "1px 1px " + lightGray + ", 0 1px " + lightGray + ", -1px -1px " + darkGray + ", 0 -1px " + darkGray;
13131            } else if (overrides.edgeStyle === 'uniform') {
13132              cueDiv.firstChild.style.textShadow = "0 0 4px " + darkGray + ", 0 0 4px " + darkGray + ", 0 0 4px " + darkGray + ", 0 0 4px " + darkGray;
13133            }
13134          }
13135  
13136          if (overrides.fontPercent && overrides.fontPercent !== 1) {
13137            var fontSize = window$3.parseFloat(cueDiv.style.fontSize);
13138            cueDiv.style.fontSize = fontSize * overrides.fontPercent + 'px';
13139            cueDiv.style.height = 'auto';
13140            cueDiv.style.top = 'auto';
13141            cueDiv.style.bottom = '2px';
13142          }
13143  
13144          if (overrides.fontFamily && overrides.fontFamily !== 'default') {
13145            if (overrides.fontFamily === 'small-caps') {
13146              cueDiv.firstChild.style.fontVariant = 'small-caps';
13147            } else {
13148              cueDiv.firstChild.style.fontFamily = fontMap[overrides.fontFamily];
13149            }
13150          }
13151        }
13152      }
13153      /**
13154       * Add an {@link TextTrack} to to the {@link Tech}s {@link TextTrackList}.
13155       *
13156       * @param {TextTrack|TextTrack[]} tracks
13157       *        Text track object or text track array to be added to the list.
13158       */
13159      ;
13160  
13161      _proto.updateForTrack = function updateForTrack(tracks) {
13162        if (!Array.isArray(tracks)) {
13163          tracks = [tracks];
13164        }
13165  
13166        if (typeof window$3.WebVTT !== 'function' || tracks.every(function (track) {
13167          return !track.activeCues;
13168        })) {
13169          return;
13170        }
13171  
13172        var cues = []; // push all active track cues
13173  
13174        for (var i = 0; i < tracks.length; ++i) {
13175          var track = tracks[i];
13176  
13177          for (var j = 0; j < track.activeCues.length; ++j) {
13178            cues.push(track.activeCues[j]);
13179          }
13180        } // removes all cues before it processes new ones
13181  
13182  
13183        window$3.WebVTT.processCues(window$3, cues, this.el_); // add unique class to each language text track & add settings styling if necessary
13184  
13185        for (var _i2 = 0; _i2 < tracks.length; ++_i2) {
13186          var _track2 = tracks[_i2];
13187  
13188          for (var _j = 0; _j < _track2.activeCues.length; ++_j) {
13189            var cueEl = _track2.activeCues[_j].displayState;
13190            addClass(cueEl, 'vjs-text-track-cue');
13191            addClass(cueEl, 'vjs-text-track-cue-' + (_track2.language ? _track2.language : _i2));
13192          }
13193  
13194          if (this.player_.textTrackSettings) {
13195            this.updateDisplayState(_track2);
13196          }
13197        }
13198      };
13199  
13200      return TextTrackDisplay;
13201    }(Component);
13202  
13203    Component.registerComponent('TextTrackDisplay', TextTrackDisplay);
13204  
13205    /**
13206     * A loading spinner for use during waiting/loading events.
13207     *
13208     * @extends Component
13209     */
13210  
13211    var LoadingSpinner = /*#__PURE__*/function (_Component) {
13212      inheritsLoose(LoadingSpinner, _Component);
13213  
13214      function LoadingSpinner() {
13215        return _Component.apply(this, arguments) || this;
13216      }
13217  
13218      var _proto = LoadingSpinner.prototype;
13219  
13220      /**
13221       * Create the `LoadingSpinner`s DOM element.
13222       *
13223       * @return {Element}
13224       *         The dom element that gets created.
13225       */
13226      _proto.createEl = function createEl$1() {
13227        var isAudio = this.player_.isAudio();
13228        var playerType = this.localize(isAudio ? 'Audio Player' : 'Video Player');
13229        var controlText = createEl('span', {
13230          className: 'vjs-control-text',
13231          innerHTML: this.localize('{1} is loading.', [playerType])
13232        });
13233  
13234        var el = _Component.prototype.createEl.call(this, 'div', {
13235          className: 'vjs-loading-spinner',
13236          dir: 'ltr'
13237        });
13238  
13239        el.appendChild(controlText);
13240        return el;
13241      };
13242  
13243      return LoadingSpinner;
13244    }(Component);
13245  
13246    Component.registerComponent('LoadingSpinner', LoadingSpinner);
13247  
13248    /**
13249     * Base class for all buttons.
13250     *
13251     * @extends ClickableComponent
13252     */
13253  
13254    var Button = /*#__PURE__*/function (_ClickableComponent) {
13255      inheritsLoose(Button, _ClickableComponent);
13256  
13257      function Button() {
13258        return _ClickableComponent.apply(this, arguments) || this;
13259      }
13260  
13261      var _proto = Button.prototype;
13262  
13263      /**
13264       * Create the `Button`s DOM element.
13265       *
13266       * @param {string} [tag="button"]
13267       *        The element's node type. This argument is IGNORED: no matter what
13268       *        is passed, it will always create a `button` element.
13269       *
13270       * @param {Object} [props={}]
13271       *        An object of properties that should be set on the element.
13272       *
13273       * @param {Object} [attributes={}]
13274       *        An object of attributes that should be set on the element.
13275       *
13276       * @return {Element}
13277       *         The element that gets created.
13278       */
13279      _proto.createEl = function createEl(tag, props, attributes) {
13280        if (props === void 0) {
13281          props = {};
13282        }
13283  
13284        if (attributes === void 0) {
13285          attributes = {};
13286        }
13287  
13288        tag = 'button';
13289        props = assign({
13290          innerHTML: '<span aria-hidden="true" class="vjs-icon-placeholder"></span>',
13291          className: this.buildCSSClass()
13292        }, props); // Add attributes for button element
13293  
13294        attributes = assign({
13295          // Necessary since the default button type is "submit"
13296          type: 'button'
13297        }, attributes);
13298        var el = Component.prototype.createEl.call(this, tag, props, attributes);
13299        this.createControlTextEl(el);
13300        return el;
13301      }
13302      /**
13303       * Add a child `Component` inside of this `Button`.
13304       *
13305       * @param {string|Component} child
13306       *        The name or instance of a child to add.
13307       *
13308       * @param {Object} [options={}]
13309       *        The key/value store of options that will get passed to children of
13310       *        the child.
13311       *
13312       * @return {Component}
13313       *         The `Component` that gets added as a child. When using a string the
13314       *         `Component` will get created by this process.
13315       *
13316       * @deprecated since version 5
13317       */
13318      ;
13319  
13320      _proto.addChild = function addChild(child, options) {
13321        if (options === void 0) {
13322          options = {};
13323        }
13324  
13325        var className = this.constructor.name;
13326        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
13327  
13328        return Component.prototype.addChild.call(this, child, options);
13329      }
13330      /**
13331       * Enable the `Button` element so that it can be activated or clicked. Use this with
13332       * {@link Button#disable}.
13333       */
13334      ;
13335  
13336      _proto.enable = function enable() {
13337        _ClickableComponent.prototype.enable.call(this);
13338  
13339        this.el_.removeAttribute('disabled');
13340      }
13341      /**
13342       * Disable the `Button` element so that it cannot be activated or clicked. Use this with
13343       * {@link Button#enable}.
13344       */
13345      ;
13346  
13347      _proto.disable = function disable() {
13348        _ClickableComponent.prototype.disable.call(this);
13349  
13350        this.el_.setAttribute('disabled', 'disabled');
13351      }
13352      /**
13353       * This gets called when a `Button` has focus and `keydown` is triggered via a key
13354       * press.
13355       *
13356       * @param {EventTarget~Event} event
13357       *        The event that caused this function to get called.
13358       *
13359       * @listens keydown
13360       */
13361      ;
13362  
13363      _proto.handleKeyDown = function handleKeyDown(event) {
13364        // Ignore Space or Enter key operation, which is handled by the browser for
13365        // a button - though not for its super class, ClickableComponent. Also,
13366        // prevent the event from propagating through the DOM and triggering Player
13367        // hotkeys. We do not preventDefault here because we _want_ the browser to
13368        // handle it.
13369        if (keycode.isEventKey(event, 'Space') || keycode.isEventKey(event, 'Enter')) {
13370          event.stopPropagation();
13371          return;
13372        } // Pass keypress handling up for unsupported keys
13373  
13374  
13375        _ClickableComponent.prototype.handleKeyDown.call(this, event);
13376      };
13377  
13378      return Button;
13379    }(ClickableComponent);
13380  
13381    Component.registerComponent('Button', Button);
13382  
13383    /**
13384     * The initial play button that shows before the video has played. The hiding of the
13385     * `BigPlayButton` get done via CSS and `Player` states.
13386     *
13387     * @extends Button
13388     */
13389  
13390    var BigPlayButton = /*#__PURE__*/function (_Button) {
13391      inheritsLoose(BigPlayButton, _Button);
13392  
13393      function BigPlayButton(player, options) {
13394        var _this;
13395  
13396        _this = _Button.call(this, player, options) || this;
13397        _this.mouseused_ = false;
13398  
13399        _this.on('mousedown', _this.handleMouseDown);
13400  
13401        return _this;
13402      }
13403      /**
13404       * Builds the default DOM `className`.
13405       *
13406       * @return {string}
13407       *         The DOM `className` for this object. Always returns 'vjs-big-play-button'.
13408       */
13409  
13410  
13411      var _proto = BigPlayButton.prototype;
13412  
13413      _proto.buildCSSClass = function buildCSSClass() {
13414        return 'vjs-big-play-button';
13415      }
13416      /**
13417       * This gets called when a `BigPlayButton` "clicked". See {@link ClickableComponent}
13418       * for more detailed information on what a click can be.
13419       *
13420       * @param {EventTarget~Event} event
13421       *        The `keydown`, `tap`, or `click` event that caused this function to be
13422       *        called.
13423       *
13424       * @listens tap
13425       * @listens click
13426       */
13427      ;
13428  
13429      _proto.handleClick = function handleClick(event) {
13430        var playPromise = this.player_.play(); // exit early if clicked via the mouse
13431  
13432        if (this.mouseused_ && event.clientX && event.clientY) {
13433          var sourceIsEncrypted = this.player_.usingPlugin('eme') && this.player_.eme.sessions && this.player_.eme.sessions.length > 0;
13434          silencePromise(playPromise);
13435  
13436          if (this.player_.tech(true) && // We've observed a bug in IE and Edge when playing back DRM content where
13437          // calling .focus() on the video element causes the video to go black,
13438          // so we avoid it in that specific case
13439          !((IE_VERSION || IS_EDGE) && sourceIsEncrypted)) {
13440            this.player_.tech(true).focus();
13441          }
13442  
13443          return;
13444        }
13445  
13446        var cb = this.player_.getChild('controlBar');
13447        var playToggle = cb && cb.getChild('playToggle');
13448  
13449        if (!playToggle) {
13450          this.player_.tech(true).focus();
13451          return;
13452        }
13453  
13454        var playFocus = function playFocus() {
13455          return playToggle.focus();
13456        };
13457  
13458        if (isPromise(playPromise)) {
13459          playPromise.then(playFocus, function () {});
13460        } else {
13461          this.setTimeout(playFocus, 1);
13462        }
13463      };
13464  
13465      _proto.handleKeyDown = function handleKeyDown(event) {
13466        this.mouseused_ = false;
13467  
13468        _Button.prototype.handleKeyDown.call(this, event);
13469      };
13470  
13471      _proto.handleMouseDown = function handleMouseDown(event) {
13472        this.mouseused_ = true;
13473      };
13474  
13475      return BigPlayButton;
13476    }(Button);
13477    /**
13478     * The text that should display over the `BigPlayButton`s controls. Added to for localization.
13479     *
13480     * @type {string}
13481     * @private
13482     */
13483  
13484  
13485    BigPlayButton.prototype.controlText_ = 'Play Video';
13486    Component.registerComponent('BigPlayButton', BigPlayButton);
13487  
13488    /**
13489     * The `CloseButton` is a `{@link Button}` that fires a `close` event when
13490     * it gets clicked.
13491     *
13492     * @extends Button
13493     */
13494  
13495    var CloseButton = /*#__PURE__*/function (_Button) {
13496      inheritsLoose(CloseButton, _Button);
13497  
13498      /**
13499      * Creates an instance of the this class.
13500      *
13501      * @param  {Player} player
13502      *         The `Player` that this class should be attached to.
13503      *
13504      * @param  {Object} [options]
13505      *         The key/value store of player options.
13506      */
13507      function CloseButton(player, options) {
13508        var _this;
13509  
13510        _this = _Button.call(this, player, options) || this;
13511  
13512        _this.controlText(options && options.controlText || _this.localize('Close'));
13513  
13514        return _this;
13515      }
13516      /**
13517      * Builds the default DOM `className`.
13518      *
13519      * @return {string}
13520      *         The DOM `className` for this object.
13521      */
13522  
13523  
13524      var _proto = CloseButton.prototype;
13525  
13526      _proto.buildCSSClass = function buildCSSClass() {
13527        return "vjs-close-button " + _Button.prototype.buildCSSClass.call(this);
13528      }
13529      /**
13530       * This gets called when a `CloseButton` gets clicked. See
13531       * {@link ClickableComponent#handleClick} for more information on when
13532       * this will be triggered
13533       *
13534       * @param {EventTarget~Event} event
13535       *        The `keydown`, `tap`, or `click` event that caused this function to be
13536       *        called.
13537       *
13538       * @listens tap
13539       * @listens click
13540       * @fires CloseButton#close
13541       */
13542      ;
13543  
13544      _proto.handleClick = function handleClick(event) {
13545        /**
13546         * Triggered when the a `CloseButton` is clicked.
13547         *
13548         * @event CloseButton#close
13549         * @type {EventTarget~Event}
13550         *
13551         * @property {boolean} [bubbles=false]
13552         *           set to false so that the close event does not
13553         *           bubble up to parents if there is no listener
13554         */
13555        this.trigger({
13556          type: 'close',
13557          bubbles: false
13558        });
13559      }
13560      /**
13561       * Event handler that is called when a `CloseButton` receives a
13562       * `keydown` event.
13563       *
13564       * By default, if the key is Esc, it will trigger a `click` event.
13565       *
13566       * @param {EventTarget~Event} event
13567       *        The `keydown` event that caused this function to be called.
13568       *
13569       * @listens keydown
13570       */
13571      ;
13572  
13573      _proto.handleKeyDown = function handleKeyDown(event) {
13574        // Esc button will trigger `click` event
13575        if (keycode.isEventKey(event, 'Esc')) {
13576          event.preventDefault();
13577          event.stopPropagation();
13578          this.trigger('click');
13579        } else {
13580          // Pass keypress handling up for unsupported keys
13581          _Button.prototype.handleKeyDown.call(this, event);
13582        }
13583      };
13584  
13585      return CloseButton;
13586    }(Button);
13587  
13588    Component.registerComponent('CloseButton', CloseButton);
13589  
13590    /**
13591     * Button to toggle between play and pause.
13592     *
13593     * @extends Button
13594     */
13595  
13596    var PlayToggle = /*#__PURE__*/function (_Button) {
13597      inheritsLoose(PlayToggle, _Button);
13598  
13599      /**
13600       * Creates an instance of this class.
13601       *
13602       * @param {Player} player
13603       *        The `Player` that this class should be attached to.
13604       *
13605       * @param {Object} [options={}]
13606       *        The key/value store of player options.
13607       */
13608      function PlayToggle(player, options) {
13609        var _this;
13610  
13611        if (options === void 0) {
13612          options = {};
13613        }
13614  
13615        _this = _Button.call(this, player, options) || this; // show or hide replay icon
13616  
13617        options.replay = options.replay === undefined || options.replay;
13618  
13619        _this.on(player, 'play', _this.handlePlay);
13620  
13621        _this.on(player, 'pause', _this.handlePause);
13622  
13623        if (options.replay) {
13624          _this.on(player, 'ended', _this.handleEnded);
13625        }
13626  
13627        return _this;
13628      }
13629      /**
13630       * Builds the default DOM `className`.
13631       *
13632       * @return {string}
13633       *         The DOM `className` for this object.
13634       */
13635  
13636  
13637      var _proto = PlayToggle.prototype;
13638  
13639      _proto.buildCSSClass = function buildCSSClass() {
13640        return "vjs-play-control " + _Button.prototype.buildCSSClass.call(this);
13641      }
13642      /**
13643       * This gets called when an `PlayToggle` is "clicked". See
13644       * {@link ClickableComponent} for more detailed information on what a click can be.
13645       *
13646       * @param {EventTarget~Event} [event]
13647       *        The `keydown`, `tap`, or `click` event that caused this function to be
13648       *        called.
13649       *
13650       * @listens tap
13651       * @listens click
13652       */
13653      ;
13654  
13655      _proto.handleClick = function handleClick(event) {
13656        if (this.player_.paused()) {
13657          this.player_.play();
13658        } else {
13659          this.player_.pause();
13660        }
13661      }
13662      /**
13663       * This gets called once after the video has ended and the user seeks so that
13664       * we can change the replay button back to a play button.
13665       *
13666       * @param {EventTarget~Event} [event]
13667       *        The event that caused this function to run.
13668       *
13669       * @listens Player#seeked
13670       */
13671      ;
13672  
13673      _proto.handleSeeked = function handleSeeked(event) {
13674        this.removeClass('vjs-ended');
13675  
13676        if (this.player_.paused()) {
13677          this.handlePause(event);
13678        } else {
13679          this.handlePlay(event);
13680        }
13681      }
13682      /**
13683       * Add the vjs-playing class to the element so it can change appearance.
13684       *
13685       * @param {EventTarget~Event} [event]
13686       *        The event that caused this function to run.
13687       *
13688       * @listens Player#play
13689       */
13690      ;
13691  
13692      _proto.handlePlay = function handlePlay(event) {
13693        this.removeClass('vjs-ended');
13694        this.removeClass('vjs-paused');
13695        this.addClass('vjs-playing'); // change the button text to "Pause"
13696  
13697        this.controlText('Pause');
13698      }
13699      /**
13700       * Add the vjs-paused class to the element so it can change appearance.
13701       *
13702       * @param {EventTarget~Event} [event]
13703       *        The event that caused this function to run.
13704       *
13705       * @listens Player#pause
13706       */
13707      ;
13708  
13709      _proto.handlePause = function handlePause(event) {
13710        this.removeClass('vjs-playing');
13711        this.addClass('vjs-paused'); // change the button text to "Play"
13712  
13713        this.controlText('Play');
13714      }
13715      /**
13716       * Add the vjs-ended class to the element so it can change appearance
13717       *
13718       * @param {EventTarget~Event} [event]
13719       *        The event that caused this function to run.
13720       *
13721       * @listens Player#ended
13722       */
13723      ;
13724  
13725      _proto.handleEnded = function handleEnded(event) {
13726        this.removeClass('vjs-playing');
13727        this.addClass('vjs-ended'); // change the button text to "Replay"
13728  
13729        this.controlText('Replay'); // on the next seek remove the replay button
13730  
13731        this.one(this.player_, 'seeked', this.handleSeeked);
13732      };
13733  
13734      return PlayToggle;
13735    }(Button);
13736    /**
13737     * The text that should display over the `PlayToggle`s controls. Added for localization.
13738     *
13739     * @type {string}
13740     * @private
13741     */
13742  
13743  
13744    PlayToggle.prototype.controlText_ = 'Play';
13745    Component.registerComponent('PlayToggle', PlayToggle);
13746  
13747    /**
13748     * @file format-time.js
13749     * @module format-time
13750     */
13751  
13752    /**
13753     * Format seconds as a time string, H:MM:SS or M:SS. Supplying a guide (in
13754     * seconds) will force a number of leading zeros to cover the length of the
13755     * guide.
13756     *
13757     * @private
13758     * @param  {number} seconds
13759     *         Number of seconds to be turned into a string
13760     *
13761     * @param  {number} guide
13762     *         Number (in seconds) to model the string after
13763     *
13764     * @return {string}
13765     *         Time formatted as H:MM:SS or M:SS
13766     */
13767    var defaultImplementation = function defaultImplementation(seconds, guide) {
13768      seconds = seconds < 0 ? 0 : seconds;
13769      var s = Math.floor(seconds % 60);
13770      var m = Math.floor(seconds / 60 % 60);
13771      var h = Math.floor(seconds / 3600);
13772      var gm = Math.floor(guide / 60 % 60);
13773      var gh = Math.floor(guide / 3600); // handle invalid times
13774  
13775      if (isNaN(seconds) || seconds === Infinity) {
13776        // '-' is false for all relational operators (e.g. <, >
13776=) so this setting
13777        // will add the minimum number of fields specified by the guide
13778        h = m = s = '-';
13779      } // Check if we need to show hours
13780  
13781  
13782      h = h > 0 || gh > 0 ? h + ':' : ''; // If hours are showing, we may need to add a leading zero.
13783      // Always show at least one digit of minutes.
13784  
13785      m = ((h || gm >= 10) && m < 10 ? '0' + m : m) + ':'; // Check if leading zero is need for seconds
13786  
13787      s = s < 10 ? '0' + s : s;
13788      return h + m + s;
13789    }; // Internal pointer to the current implementation.
13790  
13791  
13792    var implementation = defaultImplementation;
13793    /**
13794     * Replaces the default formatTime implementation with a custom implementation.
13795     *
13796     * @param {Function} customImplementation
13797     *        A function which will be used in place of the default formatTime
13798     *        implementation. Will receive the current time in seconds and the
13799     *        guide (in seconds) as arguments.
13800     */
13801  
13802    function setFormatTime(customImplementation) {
13803      implementation = customImplementation;
13804    }
13805    /**
13806     * Resets formatTime to the default implementation.
13807     */
13808  
13809    function resetFormatTime() {
13810      implementation = defaultImplementation;
13811    }
13812    /**
13813     * Delegates to either the default time formatting function or a custom
13814     * function supplied via `setFormatTime`.
13815     *
13816     * Formats seconds as a time string (H:MM:SS or M:SS). Supplying a
13817     * guide (in seconds) will force a number of leading zeros to cover the
13818     * length of the guide.
13819     *
13820     * @static
13821     * @example  formatTime(125, 600) === "02:05"
13822     * @param    {number} seconds
13823     *           Number of seconds to be turned into a string
13824     *
13825     * @param    {number} guide
13826     *           Number (in seconds) to model the string after
13827     *
13828     * @return   {string}
13829     *           Time formatted as H:MM:SS or M:SS
13830     */
13831  
13832    function formatTime(seconds, guide) {
13833      if (guide === void 0) {
13834        guide = seconds;
13835      }
13836  
13837      return implementation(seconds, guide);
13838    }
13839  
13840    /**
13841     * Displays time information about the video
13842     *
13843     * @extends Component
13844     */
13845  
13846    var TimeDisplay = /*#__PURE__*/function (_Component) {
13847      inheritsLoose(TimeDisplay, _Component);
13848  
13849      /**
13850       * Creates an instance of this class.
13851       *
13852       * @param {Player} player
13853       *        The `Player` that this class should be attached to.
13854       *
13855       * @param {Object} [options]
13856       *        The key/value store of player options.
13857       */
13858      function TimeDisplay(player, options) {
13859        var _this;
13860  
13861        _this = _Component.call(this, player, options) || this;
13862  
13863        _this.on(player, ['timeupdate', 'ended'], _this.updateContent);
13864  
13865        _this.updateTextNode_();
13866  
13867        return _this;
13868      }
13869      /**
13870       * Create the `Component`'s DOM element
13871       *
13872       * @return {Element}
13873       *         The element that was created.
13874       */
13875  
13876  
13877      var _proto = TimeDisplay.prototype;
13878  
13879      _proto.createEl = function createEl$1() {
13880        var className = this.buildCSSClass();
13881  
13882        var el = _Component.prototype.createEl.call(this, 'div', {
13883          className: className + " vjs-time-control vjs-control",
13884          innerHTML: "<span class=\"vjs-control-text\" role=\"presentation\">" + this.localize(this.labelText_) + "\xA0</span>"
13885        });
13886  
13887        this.contentEl_ = createEl('span', {
13888          className: className + "-display"
13889        }, {
13890          // tell screen readers not to automatically read the time as it changes
13891          'aria-live': 'off',
13892          // span elements have no implicit role, but some screen readers (notably VoiceOver)
13893          // treat them as a break between items in the DOM when using arrow keys
13894          // (or left-to-right swipes on iOS) to read contents of a page. Using
13895          // role='presentation' causes VoiceOver to NOT treat this span as a break.
13896          'role': 'presentation'
13897        });
13898        el.appendChild(this.contentEl_);
13899        return el;
13900      };
13901  
13902      _proto.dispose = function dispose() {
13903        this.contentEl_ = null;
13904        this.textNode_ = null;
13905  
13906        _Component.prototype.dispose.call(this);
13907      }
13908      /**
13909       * Updates the time display text node with a new time
13910       *
13911       * @param {number} [time=0] the time to update to
13912       *
13913       * @private
13914       */
13915      ;
13916  
13917      _proto.updateTextNode_ = function updateTextNode_(time) {
13918        var _this2 = this;
13919  
13920        if (time === void 0) {
13921          time = 0;
13922        }
13923  
13924        time = formatTime(time);
13925  
13926        if (this.formattedTime_ === time) {
13927          return;
13928        }
13929  
13930        this.formattedTime_ = time;
13931        this.requestAnimationFrame(function () {
13932          if (!_this2.contentEl_) {
13933            return;
13934          }
13935  
13936          var oldNode = _this2.textNode_;
13937          _this2.textNode_ = document.createTextNode(_this2.formattedTime_);
13938  
13939          if (!_this2.textNode_) {
13940            return;
13941          }
13942  
13943          if (oldNode) {
13944            _this2.contentEl_.replaceChild(_this2.textNode_, oldNode);
13945          } else {
13946            _this2.contentEl_.appendChild(_this2.textNode_);
13947          }
13948        });
13949      }
13950      /**
13951       * To be filled out in the child class, should update the displayed time
13952       * in accordance with the fact that the current time has changed.
13953       *
13954       * @param {EventTarget~Event} [event]
13955       *        The `timeupdate`  event that caused this to run.
13956       *
13957       * @listens Player#timeupdate
13958       */
13959      ;
13960  
13961      _proto.updateContent = function updateContent(event) {};
13962  
13963      return TimeDisplay;
13964    }(Component);
13965    /**
13966     * The text that is added to the `TimeDisplay` for screen reader users.
13967     *
13968     * @type {string}
13969     * @private
13970     */
13971  
13972  
13973    TimeDisplay.prototype.labelText_ = 'Time';
13974    /**
13975     * The text that should display over the `TimeDisplay`s controls. Added to for localization.
13976     *
13977     * @type {string}
13978     * @private
13979     *
13980     * @deprecated in v7; controlText_ is not used in non-active display Components
13981     */
13982  
13983    TimeDisplay.prototype.controlText_ = 'Time';
13984    Component.registerComponent('TimeDisplay', TimeDisplay);
13985  
13986    /**
13987     * Displays the current time
13988     *
13989     * @extends Component
13990     */
13991  
13992    var CurrentTimeDisplay = /*#__PURE__*/function (_TimeDisplay) {
13993      inheritsLoose(CurrentTimeDisplay, _TimeDisplay);
13994  
13995      function CurrentTimeDisplay() {
13996        return _TimeDisplay.apply(this, arguments) || this;
13997      }
13998  
13999      var _proto = CurrentTimeDisplay.prototype;
14000  
14001      /**
14002       * Builds the default DOM `className`.
14003       *
14004       * @return {string}
14005       *         The DOM `className` for this object.
14006       */
14007      _proto.buildCSSClass = function buildCSSClass() {
14008        return 'vjs-current-time';
14009      }
14010      /**
14011       * Update current time display
14012       *
14013       * @param {EventTarget~Event} [event]
14014       *        The `timeupdate` event that caused this function to run.
14015       *
14016       * @listens Player#timeupdate
14017       */
14018      ;
14019  
14020      _proto.updateContent = function updateContent(event) {
14021        // Allows for smooth scrubbing, when player can't keep up.
14022        var time;
14023  
14024        if (this.player_.ended()) {
14025          time = this.player_.duration();
14026        } else {
14027          time = this.player_.scrubbing() ? this.player_.getCache().currentTime : this.player_.currentTime();
14028        }
14029  
14030        this.updateTextNode_(time);
14031      };
14032  
14033      return CurrentTimeDisplay;
14034    }(TimeDisplay);
14035    /**
14036     * The text that is added to the `CurrentTimeDisplay` for screen reader users.
14037     *
14038     * @type {string}
14039     * @private
14040     */
14041  
14042  
14043    CurrentTimeDisplay.prototype.labelText_ = 'Current Time';
14044    /**
14045     * The text that should display over the `CurrentTimeDisplay`s controls. Added to for localization.
14046     *
14047     * @type {string}
14048     * @private
14049     *
14050     * @deprecated in v7; controlText_ is not used in non-active display Components
14051     */
14052  
14053    CurrentTimeDisplay.prototype.controlText_ = 'Current Time';
14054    Component.registerComponent('CurrentTimeDisplay', CurrentTimeDisplay);
14055  
14056    /**
14057     * Displays the duration
14058     *
14059     * @extends Component
14060     */
14061  
14062    var DurationDisplay = /*#__PURE__*/function (_TimeDisplay) {
14063      inheritsLoose(DurationDisplay, _TimeDisplay);
14064  
14065      /**
14066       * Creates an instance of this class.
14067       *
14068       * @param {Player} player
14069       *        The `Player` that this class should be attached to.
14070       *
14071       * @param {Object} [options]
14072       *        The key/value store of player options.
14073       */
14074      function DurationDisplay(player, options) {
14075        var _this;
14076  
14077        _this = _TimeDisplay.call(this, player, options) || this; // we do not want to/need to throttle duration changes,
14078        // as they should always display the changed duration as
14079        // it has changed
14080  
14081        _this.on(player, 'durationchange', _this.updateContent); // Listen to loadstart because the player duration is reset when a new media element is loaded,
14082        // but the durationchange on the user agent will not fire.
14083        // @see [Spec]{@link https://www.w3.org/TR/2011/WD-html5-20110113/video.html#media-element-load-algorithm}
14084  
14085  
14086        _this.on(player, 'loadstart', _this.updateContent); // Also listen for timeupdate (in the parent) and loadedmetadata because removing those
14087        // listeners could have broken dependent applications/libraries. These
14088        // can likely be removed for 7.0.
14089  
14090  
14091        _this.on(player, 'loadedmetadata', _this.updateContent);
14092  
14093        return _this;
14094      }
14095      /**
14096       * Builds the default DOM `className`.
14097       *
14098       * @return {string}
14099       *         The DOM `className` for this object.
14100       */
14101  
14102  
14103      var _proto = DurationDisplay.prototype;
14104  
14105      _proto.buildCSSClass = function buildCSSClass() {
14106        return 'vjs-duration';
14107      }
14108      /**
14109       * Update duration time display.
14110       *
14111       * @param {EventTarget~Event} [event]
14112       *        The `durationchange`, `timeupdate`, or `loadedmetadata` event that caused
14113       *        this function to be called.
14114       *
14115       * @listens Player#durationchange
14116       * @listens Player#timeupdate
14117       * @listens Player#loadedmetadata
14118       */
14119      ;
14120  
14121      _proto.updateContent = function updateContent(event) {
14122        var duration = this.player_.duration();
14123        this.updateTextNode_(duration);
14124      };
14125  
14126      return DurationDisplay;
14127    }(TimeDisplay);
14128    /**
14129     * The text that is added to the `DurationDisplay` for screen reader users.
14130     *
14131     * @type {string}
14132     * @private
14133     */
14134  
14135  
14136    DurationDisplay.prototype.labelText_ = 'Duration';
14137    /**
14138     * The text that should display over the `DurationDisplay`s controls. Added to for localization.
14139     *
14140     * @type {string}
14141     * @private
14142     *
14143     * @deprecated in v7; controlText_ is not used in non-active display Components
14144     */
14145  
14146    DurationDisplay.prototype.controlText_ = 'Duration';
14147    Component.registerComponent('DurationDisplay', DurationDisplay);
14148  
14149    /**
14150     * The separator between the current time and duration.
14151     * Can be hidden if it's not needed in the design.
14152     *
14153     * @extends Component
14154     */
14155  
14156    var TimeDivider = /*#__PURE__*/function (_Component) {
14157      inheritsLoose(TimeDivider, _Component);
14158  
14159      function TimeDivider() {
14160        return _Component.apply(this, arguments) || this;
14161      }
14162  
14163      var _proto = TimeDivider.prototype;
14164  
14165      /**
14166       * Create the component's DOM element
14167       *
14168       * @return {Element}
14169       *         The element that was created.
14170       */
14171      _proto.createEl = function createEl() {
14172        return _Component.prototype.createEl.call(this, 'div', {
14173          className: 'vjs-time-control vjs-time-divider',
14174          innerHTML: '<div><span>/</span></div>'
14175        }, {
14176          // this element and its contents can be hidden from assistive techs since
14177          // it is made extraneous by the announcement of the control text
14178          // for the current time and duration displays
14179          'aria-hidden': true
14180        });
14181      };
14182  
14183      return TimeDivider;
14184    }(Component);
14185  
14186    Component.registerComponent('TimeDivider', TimeDivider);
14187  
14188    /**
14189     * Displays the time left in the video
14190     *
14191     * @extends Component
14192     */
14193  
14194    var RemainingTimeDisplay = /*#__PURE__*/function (_TimeDisplay) {
14195      inheritsLoose(RemainingTimeDisplay, _TimeDisplay);
14196  
14197      /**
14198       * Creates an instance of this class.
14199       *
14200       * @param {Player} player
14201       *        The `Player` that this class should be attached to.
14202       *
14203       * @param {Object} [options]
14204       *        The key/value store of player options.
14205       */
14206      function RemainingTimeDisplay(player, options) {
14207        var _this;
14208  
14209        _this = _TimeDisplay.call(this, player, options) || this;
14210  
14211        _this.on(player, 'durationchange', _this.updateContent);
14212  
14213        return _this;
14214      }
14215      /**
14216       * Builds the default DOM `className`.
14217       *
14218       * @return {string}
14219       *         The DOM `className` for this object.
14220       */
14221  
14222  
14223      var _proto = RemainingTimeDisplay.prototype;
14224  
14225      _proto.buildCSSClass = function buildCSSClass() {
14226        return 'vjs-remaining-time';
14227      }
14228      /**
14229       * Create the `Component`'s DOM element with the "minus" characted prepend to the time
14230       *
14231       * @return {Element}
14232       *         The element that was created.
14233       */
14234      ;
14235  
14236      _proto.createEl = function createEl$1() {
14237        var el = _TimeDisplay.prototype.createEl.call(this);
14238  
14239        el.insertBefore(createEl('span', {}, {
14240          'aria-hidden': true
14241        }, '-'), this.contentEl_);
14242        return el;
14243      }
14244      /**
14245       * Update remaining time display.
14246       *
14247       * @param {EventTarget~Event} [event]
14248       *        The `timeupdate` or `durationchange` event that caused this to run.
14249       *
14250       * @listens Player#timeupdate
14251       * @listens Player#durationchange
14252       */
14253      ;
14254  
14255      _proto.updateContent = function updateContent(event) {
14256        if (typeof this.player_.duration() !== 'number') {
14257          return;
14258        }
14259  
14260        var time; // @deprecated We should only use remainingTimeDisplay
14261        // as of video.js 7
14262  
14263        if (this.player_.ended()) {
14264          time = 0;
14265        } else if (this.player_.remainingTimeDisplay) {
14266          time = this.player_.remainingTimeDisplay();
14267        } else {
14268          time = this.player_.remainingTime();
14269        }
14270  
14271        this.updateTextNode_(time);
14272      };
14273  
14274      return RemainingTimeDisplay;
14275    }(TimeDisplay);
14276    /**
14277     * The text that is added to the `RemainingTimeDisplay` for screen reader users.
14278     *
14279     * @type {string}
14280     * @private
14281     */
14282  
14283  
14284    RemainingTimeDisplay.prototype.labelText_ = 'Remaining Time';
14285    /**
14286     * The text that should display over the `RemainingTimeDisplay`s controls. Added to for localization.
14287     *
14288     * @type {string}
14289     * @private
14290     *
14291     * @deprecated in v7; controlText_ is not used in non-active display Components
14292     */
14293  
14294    RemainingTimeDisplay.prototype.controlText_ = 'Remaining Time';
14295    Component.registerComponent('RemainingTimeDisplay', RemainingTimeDisplay);
14296  
14297    /**
14298     * Displays the live indicator when duration is Infinity.
14299     *
14300     * @extends Component
14301     */
14302  
14303    var LiveDisplay = /*#__PURE__*/function (_Component) {
14304      inheritsLoose(LiveDisplay, _Component);
14305  
14306      /**
14307       * Creates an instance of this class.
14308       *
14309       * @param {Player} player
14310       *        The `Player` that this class should be attached to.
14311       *
14312       * @param {Object} [options]
14313       *        The key/value store of player options.
14314       */
14315      function LiveDisplay(player, options) {
14316        var _this;
14317  
14318        _this = _Component.call(this, player, options) || this;
14319  
14320        _this.updateShowing();
14321  
14322        _this.on(_this.player(), 'durationchange', _this.updateShowing);
14323  
14324        return _this;
14325      }
14326      /**
14327       * Create the `Component`'s DOM element
14328       *
14329       * @return {Element}
14330       *         The element that was created.
14331       */
14332  
14333  
14334      var _proto = LiveDisplay.prototype;
14335  
14336      _proto.createEl = function createEl$1() {
14337        var el = _Component.prototype.createEl.call(this, 'div', {
14338          className: 'vjs-live-control vjs-control'
14339        });
14340  
14341        this.contentEl_ = createEl('div', {
14342          className: 'vjs-live-display',
14343          innerHTML: "<span class=\"vjs-control-text\">" + this.localize('Stream Type') + "\xA0</span>" + this.localize('LIVE')
14344        }, {
14345          'aria-live': 'off'
14346        });
14347        el.appendChild(this.contentEl_);
14348        return el;
14349      };
14350  
14351      _proto.dispose = function dispose() {
14352        this.contentEl_ = null;
14353  
14354        _Component.prototype.dispose.call(this);
14355      }
14356      /**
14357       * Check the duration to see if the LiveDisplay should be showing or not. Then show/hide
14358       * it accordingly
14359       *
14360       * @param {EventTarget~Event} [event]
14361       *        The {@link Player#durationchange} event that caused this function to run.
14362       *
14363       * @listens Player#durationchange
14364       */
14365      ;
14366  
14367      _proto.updateShowing = function updateShowing(event) {
14368        if (this.player().duration() === Infinity) {
14369          this.show();
14370        } else {
14371          this.hide();
14372        }
14373      };
14374  
14375      return LiveDisplay;
14376    }(Component);
14377  
14378    Component.registerComponent('LiveDisplay', LiveDisplay);
14379  
14380    /**
14381     * Displays the live indicator when duration is Infinity.
14382     *
14383     * @extends Component
14384     */
14385  
14386    var SeekToLive = /*#__PURE__*/function (_Button) {
14387      inheritsLoose(SeekToLive, _Button);
14388  
14389      /**
14390       * Creates an instance of this class.
14391       *
14392       * @param {Player} player
14393       *        The `Player` that this class should be attached to.
14394       *
14395       * @param {Object} [options]
14396       *        The key/value store of player options.
14397       */
14398      function SeekToLive(player, options) {
14399        var _this;
14400  
14401        _this = _Button.call(this, player, options) || this;
14402  
14403        _this.updateLiveEdgeStatus();
14404  
14405        if (_this.player_.liveTracker) {
14406          _this.on(_this.player_.liveTracker, 'liveedgechange', _this.updateLiveEdgeStatus);
14407        }
14408  
14409        return _this;
14410      }
14411      /**
14412       * Create the `Component`'s DOM element
14413       *
14414       * @return {Element}
14415       *         The element that was created.
14416       */
14417  
14418  
14419      var _proto = SeekToLive.prototype;
14420  
14421      _proto.createEl = function createEl$1() {
14422        var el = _Button.prototype.createEl.call(this, 'button', {
14423          className: 'vjs-seek-to-live-control vjs-control'
14424        });
14425  
14426        this.textEl_ = createEl('span', {
14427          className: 'vjs-seek-to-live-text',
14428          innerHTML: this.localize('LIVE')
14429        }, {
14430          'aria-hidden': 'true'
14431        });
14432        el.appendChild(this.textEl_);
14433        return el;
14434      }
14435      /**
14436       * Update the state of this button if we are at the live edge
14437       * or not
14438       */
14439      ;
14440  
14441      _proto.updateLiveEdgeStatus = function updateLiveEdgeStatus() {
14442        // default to live edge
14443        if (!this.player_.liveTracker || this.player_.liveTracker.atLiveEdge()) {
14444          this.setAttribute('aria-disabled', true);
14445          this.addClass('vjs-at-live-edge');
14446          this.controlText('Seek to live, currently playing live');
14447        } else {
14448          this.setAttribute('aria-disabled', false);
14449          this.removeClass('vjs-at-live-edge');
14450          this.controlText('Seek to live, currently behind live');
14451        }
14452      }
14453      /**
14454       * On click bring us as near to the live point as possible.
14455       * This requires that we wait for the next `live-seekable-change`
14456       * event which will happen every segment length seconds.
14457       */
14458      ;
14459  
14460      _proto.handleClick = function handleClick() {
14461        this.player_.liveTracker.seekToLiveEdge();
14462      }
14463      /**
14464       * Dispose of the element and stop tracking
14465       */
14466      ;
14467  
14468      _proto.dispose = function dispose() {
14469        if (this.player_.liveTracker) {
14470          this.off(this.player_.liveTracker, 'liveedgechange', this.updateLiveEdgeStatus);
14471        }
14472  
14473        this.textEl_ = null;
14474  
14475        _Button.prototype.dispose.call(this);
14476      };
14477  
14478      return SeekToLive;
14479    }(Button);
14480  
14481    SeekToLive.prototype.controlText_ = 'Seek to live, currently playing live';
14482    Component.registerComponent('SeekToLive', SeekToLive);
14483  
14484    /**
14485     * Keep a number between a min and a max value
14486     *
14487     * @param {number} number
14488     *        The number to clamp
14489     *
14490     * @param {number} min
14491     *        The minimum value
14492     * @param {number} max
14493     *        The maximum value
14494     *
14495     * @return {number}
14496     *         the clamped number
14497     */
14498    var clamp = function clamp(number, min, max) {
14499      number = Number(number);
14500      return Math.min(max, Math.max(min, isNaN(number) ? min : number));
14501    };
14502  
14503    /**
14504     * The base functionality for a slider. Can be vertical or horizontal.
14505     * For instance the volume bar or the seek bar on a video is a slider.
14506     *
14507     * @extends Component
14508     */
14509  
14510    var Slider = /*#__PURE__*/function (_Component) {
14511      inheritsLoose(Slider, _Component);
14512  
14513      /**
14514      * Create an instance of this class
14515      *
14516      * @param {Player} player
14517      *        The `Player` that this class should be attached to.
14518      *
14519      * @param {Object} [options]
14520      *        The key/value store of player options.
14521      */
14522      function Slider(player, options) {
14523        var _this;
14524  
14525        _this = _Component.call(this, player, options) || this; // Set property names to bar to match with the child Slider class is looking for
14526  
14527        _this.bar = _this.getChild(_this.options_.barName); // Set a horizontal or vertical class on the slider depending on the slider type
14528  
14529        _this.vertical(!!_this.options_.vertical);
14530  
14531        _this.enable();
14532  
14533        return _this;
14534      }
14535      /**
14536       * Are controls are currently enabled for this slider or not.
14537       *
14538       * @return {boolean}
14539       *         true if controls are enabled, false otherwise
14540       */
14541  
14542  
14543      var _proto = Slider.prototype;
14544  
14545      _proto.enabled = function enabled() {
14546        return this.enabled_;
14547      }
14548      /**
14549       * Enable controls for this slider if they are disabled
14550       */
14551      ;
14552  
14553      _proto.enable = function enable() {
14554        if (this.enabled()) {
14555          return;
14556        }
14557  
14558        this.on('mousedown', this.handleMouseDown);
14559        this.on('touchstart', this.handleMouseDown);
14560        this.on('keydown', this.handleKeyDown);
14561        this.on('click', this.handleClick); // TODO: deprecated, controlsvisible does not seem to be fired
14562  
14563        this.on(this.player_, 'controlsvisible', this.update);
14564  
14565        if (this.playerEvent) {
14566          this.on(this.player_, this.playerEvent, this.update);
14567        }
14568  
14569        this.removeClass('disabled');
14570        this.setAttribute('tabindex', 0);
14571        this.enabled_ = true;
14572      }
14573      /**
14574       * Disable controls for this slider if they are enabled
14575       */
14576      ;
14577  
14578      _proto.disable = function disable() {
14579        if (!this.enabled()) {
14580          return;
14581        }
14582  
14583        var doc = this.bar.el_.ownerDocument;
14584        this.off('mousedown', this.handleMouseDown);
14585        this.off('touchstart', this.handleMouseDown);
14586        this.off('keydown', this.handleKeyDown);
14587        this.off('click', this.handleClick);
14588        this.off(this.player_, 'controlsvisible', this.update);
14589        this.off(doc, 'mousemove', this.handleMouseMove);
14590        this.off(doc, 'mouseup', this.handleMouseUp);
14591        this.off(doc, 'touchmove', this.handleMouseMove);
14592        this.off(doc, 'touchend', this.handleMouseUp);
14593        this.removeAttribute('tabindex');
14594        this.addClass('disabled');
14595  
14596        if (this.playerEvent) {
14597          this.off(this.player_, this.playerEvent, this.update);
14598        }
14599  
14600        this.enabled_ = false;
14601      }
14602      /**
14603       * Create the `Slider`s DOM element.
14604       *
14605       * @param {string} type
14606       *        Type of element to create.
14607       *
14608       * @param {Object} [props={}]
14609       *        List of properties in Object form.
14610       *
14611       * @param {Object} [attributes={}]
14612       *        list of attributes in Object form.
14613       *
14614       * @return {Element}
14615       *         The element that gets created.
14616       */
14617      ;
14618  
14619      _proto.createEl = function createEl(type, props, attributes) {
14620        if (props === void 0) {
14621          props = {};
14622        }
14623  
14624        if (attributes === void 0) {
14625          attributes = {};
14626        }
14627  
14628        // Add the slider element class to all sub classes
14629        props.className = props.className + ' vjs-slider';
14630        props = assign({
14631          tabIndex: 0
14632        }, props);
14633        attributes = assign({
14634          'role': 'slider',
14635          'aria-valuenow': 0,
14636          'aria-valuemin': 0,
14637          'aria-valuemax': 100,
14638          'tabIndex': 0
14639        }, attributes);
14640        return _Component.prototype.createEl.call(this, type, props, attributes);
14641      }
14642      /**
14643       * Handle `mousedown` or `touchstart` events on the `Slider`.
14644       *
14645       * @param {EventTarget~Event} event
14646       *        `mousedown` or `touchstart` event that triggered this function
14647       *
14648       * @listens mousedown
14649       * @listens touchstart
14650       * @fires Slider#slideractive
14651       */
14652      ;
14653  
14654      _proto.handleMouseDown = function handleMouseDown(event) {
14655        var doc = this.bar.el_.ownerDocument;
14656  
14657        if (event.type === 'mousedown') {
14658          event.preventDefault();
14659        } // Do not call preventDefault() on touchstart in Chrome
14660        // to avoid console warnings. Use a 'touch-action: none' style
14661        // instead to prevent unintented scrolling.
14662        // https://developers.google.com/web/updates/2017/01/scrolling-intervention
14663  
14664  
14665        if (event.type === 'touchstart' && !IS_CHROME) {
14666          event.preventDefault();
14667        }
14668  
14669        blockTextSelection();
14670        this.addClass('vjs-sliding');
14671        /**
14672         * Triggered when the slider is in an active state
14673         *
14674         * @event Slider#slideractive
14675         * @type {EventTarget~Event}
14676         */
14677  
14678        this.trigger('slideractive');
14679        this.on(doc, 'mousemove', this.handleMouseMove);
14680        this.on(doc, 'mouseup', this.handleMouseUp);
14681        this.on(doc, 'touchmove', this.handleMouseMove);
14682        this.on(doc, 'touchend', this.handleMouseUp);
14683        this.handleMouseMove(event);
14684      }
14685      /**
14686       * Handle the `mousemove`, `touchmove`, and `mousedown` events on this `Slider`.
14687       * The `mousemove` and `touchmove` events will only only trigger this function during
14688       * `mousedown` and `touchstart`. This is due to {@link Slider#handleMouseDown} and
14689       * {@link Slider#handleMouseUp}.
14690       *
14691       * @param {EventTarget~Event} event
14692       *        `mousedown`, `mousemove`, `touchstart`, or `touchmove` event that triggered
14693       *        this function
14694       *
14695       * @listens mousemove
14696       * @listens touchmove
14697       */
14698      ;
14699  
14700      _proto.handleMouseMove = function handleMouseMove(event) {}
14701      /**
14702       * Handle `mouseup` or `touchend` events on the `Slider`.
14703       *
14704       * @param {EventTarget~Event} event
14705       *        `mouseup` or `touchend` event that triggered this function.
14706       *
14707       * @listens touchend
14708       * @listens mouseup
14709       * @fires Slider#sliderinactive
14710       */
14711      ;
14712  
14713      _proto.handleMouseUp = function handleMouseUp() {
14714        var doc = this.bar.el_.ownerDocument;
14715        unblockTextSelection();
14716        this.removeClass('vjs-sliding');
14717        /**
14718         * Triggered when the slider is no longer in an active state.
14719         *
14720         * @event Slider#sliderinactive
14721         * @type {EventTarget~Event}
14722         */
14723  
14724        this.trigger('sliderinactive');
14725        this.off(doc, 'mousemove', this.handleMouseMove);
14726        this.off(doc, 'mouseup', this.handleMouseUp);
14727        this.off(doc, 'touchmove', this.handleMouseMove);
14728        this.off(doc, 'touchend', this.handleMouseUp);
14729        this.update();
14730      }
14731      /**
14732       * Update the progress bar of the `Slider`.
14733       *
14734       * @return {number}
14735       *          The percentage of progress the progress bar represents as a
14736       *          number from 0 to 1.
14737       */
14738      ;
14739  
14740      _proto.update = function update() {
14741        var _this2 = this;
14742  
14743        // In VolumeBar init we have a setTimeout for update that pops and update
14744        // to the end of the execution stack. The player is destroyed before then
14745        // update will cause an error
14746        // If there's no bar...
14747        if (!this.el_ || !this.bar) {
14748          return;
14749        } // clamp progress between 0 and 1
14750        // and only round to four decimal places, as we round to two below
14751  
14752  
14753        var progress = this.getProgress();
14754  
14755        if (progress === this.progress_) {
14756          return progress;
14757        }
14758  
14759        this.progress_ = progress;
14760        this.requestAnimationFrame(function () {
14761          // Set the new bar width or height
14762          var sizeKey = _this2.vertical() ? 'height' : 'width'; // Convert to a percentage for css value
14763  
14764          _this2.bar.el().style[sizeKey] = (progress * 100).toFixed(2) + '%';
14765        });
14766        return progress;
14767      }
14768      /**
14769       * Get the percentage of the bar that should be filled
14770       * but clamped and rounded.
14771       *
14772       * @return {number}
14773       *         percentage filled that the slider is
14774       */
14775      ;
14776  
14777      _proto.getProgress = function getProgress() {
14778        return Number(clamp(this.getPercent(), 0, 1).toFixed(4));
14779      }
14780      /**
14781       * Calculate distance for slider
14782       *
14783       * @param {EventTarget~Event} event
14784       *        The event that caused this function to run.
14785       *
14786       * @return {number}
14787       *         The current position of the Slider.
14788       *         - position.x for vertical `Slider`s
14789       *         - position.y for horizontal `Slider`s
14790       */
14791      ;
14792  
14793      _proto.calculateDistance = function calculateDistance(event) {
14794        var position = getPointerPosition(this.el_, event);
14795  
14796        if (this.vertical()) {
14797          return position.y;
14798        }
14799  
14800        return position.x;
14801      }
14802      /**
14803       * Handle a `keydown` event on the `Slider`. Watches for left, rigth, up, and down
14804       * arrow keys. This function will only be called when the slider has focus. See
14805       * {@link Slider#handleFocus} and {@link Slider#handleBlur}.
14806       *
14807       * @param {EventTarget~Event} event
14808       *        the `keydown` event that caused this function to run.
14809       *
14810       * @listens keydown
14811       */
14812      ;
14813  
14814      _proto.handleKeyDown = function handleKeyDown(event) {
14815        // Left and Down Arrows
14816        if (keycode.isEventKey(event, 'Left') || keycode.isEventKey(event, 'Down')) {
14817          event.preventDefault();
14818          event.stopPropagation();
14819          this.stepBack(); // Up and Right Arrows
14820        } else if (keycode.isEventKey(event, 'Right') || keycode.isEventKey(event, 'Up')) {
14821          event.preventDefault();
14822          event.stopPropagation();
14823          this.stepForward();
14824        } else {
14825          // Pass keydown handling up for unsupported keys
14826          _Component.prototype.handleKeyDown.call(this, event);
14827        }
14828      }
14829      /**
14830       * Listener for click events on slider, used to prevent clicks
14831       *   from bubbling up to parent elements like button menus.
14832       *
14833       * @param {Object} event
14834       *        Event that caused this object to run
14835       */
14836      ;
14837  
14838      _proto.handleClick = function handleClick(event) {
14839        event.stopPropagation();
14840        event.preventDefault();
14841      }
14842      /**
14843       * Get/set if slider is horizontal for vertical
14844       *
14845       * @param {boolean} [bool]
14846       *        - true if slider is vertical,
14847       *        - false is horizontal
14848       *
14849       * @return {boolean}
14850       *         - true if slider is vertical, and getting
14851       *         - false if the slider is horizontal, and getting
14852       */
14853      ;
14854  
14855      _proto.vertical = function vertical(bool) {
14856        if (bool === undefined) {
14857          return this.vertical_ || false;
14858        }
14859  
14860        this.vertical_ = !!bool;
14861  
14862        if (this.vertical_) {
14863          this.addClass('vjs-slider-vertical');
14864        } else {
14865          this.addClass('vjs-slider-horizontal');
14866        }
14867      };
14868  
14869      return Slider;
14870    }(Component);
14871  
14872    Component.registerComponent('Slider', Slider);
14873  
14874    var percentify = function percentify(time, end) {
14875      return clamp(time / end * 100, 0, 100).toFixed(2) + '%';
14876    };
14877    /**
14878     * Shows loading progress
14879     *
14880     * @extends Component
14881     */
14882  
14883  
14884    var LoadProgressBar = /*#__PURE__*/function (_Component) {
14885      inheritsLoose(LoadProgressBar, _Component);
14886  
14887      /**
14888       * Creates an instance of this class.
14889       *
14890       * @param {Player} player
14891       *        The `Player` that this class should be attached to.
14892       *
14893       * @param {Object} [options]
14894       *        The key/value store of player options.
14895       */
14896      function LoadProgressBar(player, options) {
14897        var _this;
14898  
14899        _this = _Component.call(this, player, options) || this;
14900        _this.partEls_ = [];
14901  
14902        _this.on(player, 'progress', _this.update);
14903  
14904        return _this;
14905      }
14906      /**
14907       * Create the `Component`'s DOM element
14908       *
14909       * @return {Element}
14910       *         The element that was created.
14911       */
14912  
14913  
14914      var _proto = LoadProgressBar.prototype;
14915  
14916      _proto.createEl = function createEl$1() {
14917        var el = _Component.prototype.createEl.call(this, 'div', {
14918          className: 'vjs-load-progress'
14919        });
14920  
14921        var wrapper = createEl('span', {
14922          className: 'vjs-control-text'
14923        });
14924        var loadedText = createEl('span', {
14925          textContent: this.localize('Loaded')
14926        });
14927        var separator = document.createTextNode(': ');
14928        this.percentageEl_ = createEl('span', {
14929          className: 'vjs-control-text-loaded-percentage',
14930          textContent: '0%'
14931        });
14932        el.appendChild(wrapper);
14933        wrapper.appendChild(loadedText);
14934        wrapper.appendChild(separator);
14935        wrapper.appendChild(this.percentageEl_);
14936        return el;
14937      };
14938  
14939      _proto.dispose = function dispose() {
14940        this.partEls_ = null;
14941        this.percentageEl_ = null;
14942  
14943        _Component.prototype.dispose.call(this);
14944      }
14945      /**
14946       * Update progress bar
14947       *
14948       * @param {EventTarget~Event} [event]
14949       *        The `progress` event that caused this function to run.
14950       *
14951       * @listens Player#progress
14952       */
14953      ;
14954  
14955      _proto.update = function update(event) {
14956        var _this2 = this;
14957  
14958        this.requestAnimationFrame(function () {
14959          var liveTracker = _this2.player_.liveTracker;
14960  
14961          var buffered = _this2.player_.buffered();
14962  
14963          var duration = liveTracker && liveTracker.isLive() ? liveTracker.seekableEnd() : _this2.player_.duration();
14964  
14965          var bufferedEnd = _this2.player_.bufferedEnd();
14966  
14967          var children = _this2.partEls_;
14968          var percent = percentify(bufferedEnd, duration);
14969  
14970          if (_this2.percent_ !== percent) {
14971            // update the width of the progress bar
14972            _this2.el_.style.width = percent; // update the control-text
14973  
14974            textContent(_this2.percentageEl_, percent);
14975            _this2.percent_ = percent;
14976          } // add child elements to represent the individual buffered time ranges
14977  
14978  
14979          for (var i = 0; i < buffered.length; i++) {
14980            var start = buffered.start(i);
14981            var end = buffered.end(i);
14982            var part = children[i];
14983  
14984            if (!part) {
14985              part = _this2.el_.appendChild(createEl());
14986              children[i] = part;
14987            } //  only update if changed
14988  
14989  
14990            if (part.dataset.start === start && part.dataset.end === end) {
14991              continue;
14992            }
14993  
14994            part.dataset.start = start;
14995            part.dataset.end = end; // set the percent based on the width of the progress bar (bufferedEnd)
14996  
14997            part.style.left = percentify(start, bufferedEnd);
14998            part.style.width = percentify(end - start, bufferedEnd);
14999          } // remove unused buffered range elements
15000  
15001  
15002          for (var _i = children.length; _i > buffered.length; _i--) {
15003            _this2.el_.removeChild(children[_i - 1]);
15004          }
15005  
15006          children.length = buffered.length;
15007        });
15008      };
15009  
15010      return LoadProgressBar;
15011    }(Component);
15012  
15013    Component.registerComponent('LoadProgressBar', LoadProgressBar);
15014  
15015    /**
15016     * Time tooltips display a time above the progress bar.
15017     *
15018     * @extends Component
15019     */
15020  
15021    var TimeTooltip = /*#__PURE__*/function (_Component) {
15022      inheritsLoose(TimeTooltip, _Component);
15023  
15024      /**
15025       * Creates an instance of this class.
15026       *
15027       * @param {Player} player
15028       *        The {@link Player} that this class should be attached to.
15029       *
15030       * @param {Object} [options]
15031       *        The key/value store of player options.
15032       */
15033      function TimeTooltip(player, options) {
15034        var _this;
15035  
15036        _this = _Component.call(this, player, options) || this;
15037        _this.update = throttle(bind(assertThisInitialized(_this), _this.update), UPDATE_REFRESH_INTERVAL);
15038        return _this;
15039      }
15040      /**
15041       * Create the time tooltip DOM element
15042       *
15043       * @return {Element}
15044       *         The element that was created.
15045       */
15046  
15047  
15048      var _proto = TimeTooltip.prototype;
15049  
15050      _proto.createEl = function createEl() {
15051        return _Component.prototype.createEl.call(this, 'div', {
15052          className: 'vjs-time-tooltip'
15053        }, {
15054          'aria-hidden': 'true'
15055        });
15056      }
15057      /**
15058       * Updates the position of the time tooltip relative to the `SeekBar`.
15059       *
15060       * @param {Object} seekBarRect
15061       *        The `ClientRect` for the {@link SeekBar} element.
15062       *
15063       * @param {number} seekBarPoint
15064       *        A number from 0 to 1, representing a horizontal reference point
15065       *        from the left edge of the {@link SeekBar}
15066       */
15067      ;
15068  
15069      _proto.update = function update(seekBarRect, seekBarPoint, content) {
15070        var tooltipRect = getBoundingClientRect(this.el_);
15071        var playerRect = getBoundingClientRect(this.player_.el());
15072        var seekBarPointPx = seekBarRect.width * seekBarPoint; // do nothing if either rect isn't available
15073        // for example, if the player isn't in the DOM for testing
15074  
15075        if (!playerRect || !tooltipRect) {
15076          return;
15077        } // This is the space left of the `seekBarPoint` available within the bounds
15078        // of the player. We calculate any gap between the left edge of the player
15079        // and the left edge of the `SeekBar` and add the number of pixels in the
15080        // `SeekBar` before hitting the `seekBarPoint`
15081  
15082  
15083        var spaceLeftOfPoint = seekBarRect.left - playerRect.left + seekBarPointPx; // This is the space right of the `seekBarPoint` available within the bounds
15084        // of the player. We calculate the number of pixels from the `seekBarPoint`
15085        // to the right edge of the `SeekBar` and add to that any gap between the
15086        // right edge of the `SeekBar` and the player.
15087  
15088        var spaceRightOfPoint = seekBarRect.width - seekBarPointPx + (playerRect.right - seekBarRect.right); // This is the number of pixels by which the tooltip will need to be pulled
15089        // further to the right to center it over the `seekBarPoint`.
15090  
15091        var pullTooltipBy = tooltipRect.width / 2; // Adjust the `pullTooltipBy` distance to the left or right depending on
15092        // the results of the space calculations above.
15093  
15094        if (spaceLeftOfPoint < pullTooltipBy) {
15095          pullTooltipBy += pullTooltipBy - spaceLeftOfPoint;
15096        }
15096 else if (spaceRightOfPoint < pullTooltipBy) {
15097          pullTooltipBy = spaceRightOfPoint;
15098        } // Due to the imprecision of decimal/ratio based calculations and varying
15099        // rounding behaviors, there are cases where the spacing adjustment is off
15100        // by a pixel or two. This adds insurance to these calculations.
15101  
15102  
15103        if (pullTooltipBy < 0) {
15104          pullTooltipBy = 0;
15105        } else if (pullTooltipBy > tooltipRect.width) {
15106          pullTooltipBy = tooltipRect.width;
15107        }
15108  
15109        this.el_.style.right = "-" + pullTooltipBy + "px";
15110        this.write(content);
15111      }
15112      /**
15113       * Write the time to the tooltip DOM element.
15114       *
15115       * @param {string} content
15116       *        The formatted time for the tooltip.
15117       */
15118      ;
15119  
15120      _proto.write = function write(content) {
15121        textContent(this.el_, content);
15122      }
15123      /**
15124       * Updates the position of the time tooltip relative to the `SeekBar`.
15125       *
15126       * @param {Object} seekBarRect
15127       *        The `ClientRect` for the {@link SeekBar} element.
15128       *
15129       * @param {number} seekBarPoint
15130       *        A number from 0 to 1, representing a horizontal reference point
15131       *        from the left edge of the {@link SeekBar}
15132       *
15133       * @param {number} time
15134       *        The time to update the tooltip to, not used during live playback
15135       *
15136       * @param {Function} cb
15137       *        A function that will be called during the request animation frame
15138       *        for tooltips that need to do additional animations from the default
15139       */
15140      ;
15141  
15142      _proto.updateTime = function updateTime(seekBarRect, seekBarPoint, time, cb) {
15143        var _this2 = this;
15144  
15145        // If there is an existing rAF ID, cancel it so we don't over-queue.
15146        if (this.rafId_) {
15147          this.cancelAnimationFrame(this.rafId_);
15148        }
15149  
15150        this.rafId_ = this.requestAnimationFrame(function () {
15151          var content;
15152  
15153          var duration = _this2.player_.duration();
15154  
15155          if (_this2.player_.liveTracker && _this2.player_.liveTracker.isLive()) {
15156            var liveWindow = _this2.player_.liveTracker.liveWindow();
15157  
15158            var secondsBehind = liveWindow - seekBarPoint * liveWindow;
15159            content = (secondsBehind < 1 ? '' : '-') + formatTime(secondsBehind, liveWindow);
15160          } else {
15161            content = formatTime(time, duration);
15162          }
15163  
15164          _this2.update(seekBarRect, seekBarPoint, content);
15165  
15166          if (cb) {
15167            cb();
15168          }
15169        });
15170      };
15171  
15172      return TimeTooltip;
15173    }(Component);
15174  
15175    Component.registerComponent('TimeTooltip', TimeTooltip);
15176  
15177    /**
15178     * Used by {@link SeekBar} to display media playback progress as part of the
15179     * {@link ProgressControl}.
15180     *
15181     * @extends Component
15182     */
15183  
15184    var PlayProgressBar = /*#__PURE__*/function (_Component) {
15185      inheritsLoose(PlayProgressBar, _Component);
15186  
15187      /**
15188       * Creates an instance of this class.
15189       *
15190       * @param {Player} player
15191       *        The {@link Player} that this class should be attached to.
15192       *
15193       * @param {Object} [options]
15194       *        The key/value store of player options.
15195       */
15196      function PlayProgressBar(player, options) {
15197        var _this;
15198  
15199        _this = _Component.call(this, player, options) || this;
15200        _this.update = throttle(bind(assertThisInitialized(_this), _this.update), UPDATE_REFRESH_INTERVAL);
15201        return _this;
15202      }
15203      /**
15204       * Create the the DOM element for this class.
15205       *
15206       * @return {Element}
15207       *         The element that was created.
15208       */
15209  
15210  
15211      var _proto = PlayProgressBar.prototype;
15212  
15213      _proto.createEl = function createEl() {
15214        return _Component.prototype.createEl.call(this, 'div', {
15215          className: 'vjs-play-progress vjs-slider-bar'
15216        }, {
15217          'aria-hidden': 'true'
15218        });
15219      }
15220      /**
15221       * Enqueues updates to its own DOM as well as the DOM of its
15222       * {@link TimeTooltip} child.
15223       *
15224       * @param {Object} seekBarRect
15225       *        The `ClientRect` for the {@link SeekBar} element.
15226       *
15227       * @param {number} seekBarPoint
15228       *        A number from 0 to 1, representing a horizontal reference point
15229       *        from the left edge of the {@link SeekBar}
15230       */
15231      ;
15232  
15233      _proto.update = function update(seekBarRect, seekBarPoint) {
15234        var timeTooltip = this.getChild('timeTooltip');
15235  
15236        if (!timeTooltip) {
15237          return;
15238        }
15239  
15240        var time = this.player_.scrubbing() ? this.player_.getCache().currentTime : this.player_.currentTime();
15241        timeTooltip.updateTime(seekBarRect, seekBarPoint, time);
15242      };
15243  
15244      return PlayProgressBar;
15245    }(Component);
15246    /**
15247     * Default options for {@link PlayProgressBar}.
15248     *
15249     * @type {Object}
15250     * @private
15251     */
15252  
15253  
15254    PlayProgressBar.prototype.options_ = {
15255      children: []
15256    }; // Time tooltips should not be added to a player on mobile devices
15257  
15258    if (!IS_IOS && !IS_ANDROID) {
15259      PlayProgressBar.prototype.options_.children.push('timeTooltip');
15260    }
15261  
15262    Component.registerComponent('PlayProgressBar', PlayProgressBar);
15263  
15264    /**
15265     * The {@link MouseTimeDisplay} component tracks mouse movement over the
15266     * {@link ProgressControl}. It displays an indicator and a {@link TimeTooltip}
15267     * indicating the time which is represented by a given point in the
15268     * {@link ProgressControl}.
15269     *
15270     * @extends Component
15271     */
15272  
15273    var MouseTimeDisplay = /*#__PURE__*/function (_Component) {
15274      inheritsLoose(MouseTimeDisplay, _Component);
15275  
15276      /**
15277       * Creates an instance of this class.
15278       *
15279       * @param {Player} player
15280       *        The {@link Player} that this class should be attached to.
15281       *
15282       * @param {Object} [options]
15283       *        The key/value store of player options.
15284       */
15285      function MouseTimeDisplay(player, options) {
15286        var _this;
15287  
15288        _this = _Component.call(this, player, options) || this;
15289        _this.update = throttle(bind(assertThisInitialized(_this), _this.update), UPDATE_REFRESH_INTERVAL);
15290        return _this;
15291      }
15292      /**
15293       * Create the DOM element for this class.
15294       *
15295       * @return {Element}
15296       *         The element that was created.
15297       */
15298  
15299  
15300      var _proto = MouseTimeDisplay.prototype;
15301  
15302      _proto.createEl = function createEl() {
15303        return _Component.prototype.createEl.call(this, 'div', {
15304          className: 'vjs-mouse-display'
15305        });
15306      }
15307      /**
15308       * Enqueues updates to its own DOM as well as the DOM of its
15309       * {@link TimeTooltip} child.
15310       *
15311       * @param {Object} seekBarRect
15312       *        The `ClientRect` for the {@link SeekBar} element.
15313       *
15314       * @param {number} seekBarPoint
15315       *        A number from 0 to 1, representing a horizontal reference point
15316       *        from the left edge of the {@link SeekBar}
15317       */
15318      ;
15319  
15320      _proto.update = function update(seekBarRect, seekBarPoint) {
15321        var _this2 = this;
15322  
15323        var time = seekBarPoint * this.player_.duration();
15324        this.getChild('timeTooltip').updateTime(seekBarRect, seekBarPoint, time, function () {
15325          _this2.el_.style.left = seekBarRect.width * seekBarPoint + "px";
15326        });
15327      };
15328  
15329      return MouseTimeDisplay;
15330    }(Component);
15331    /**
15332     * Default options for `MouseTimeDisplay`
15333     *
15334     * @type {Object}
15335     * @private
15336     */
15337  
15338  
15339    MouseTimeDisplay.prototype.options_ = {
15340      children: ['timeTooltip']
15341    };
15342    Component.registerComponent('MouseTimeDisplay', MouseTimeDisplay);
15343  
15344    var STEP_SECONDS = 5; // The multiplier of STEP_SECONDS that PgUp/PgDown move the timeline.
15345  
15346    var PAGE_KEY_MULTIPLIER = 12;
15347    /**
15348     * Seek bar and container for the progress bars. Uses {@link PlayProgressBar}
15349     * as its `bar`.
15350     *
15351     * @extends Slider
15352     */
15353  
15354    var SeekBar = /*#__PURE__*/function (_Slider) {
15355      inheritsLoose(SeekBar, _Slider);
15356  
15357      /**
15358       * Creates an instance of this class.
15359       *
15360       * @param {Player} player
15361       *        The `Player` that this class should be attached to.
15362       *
15363       * @param {Object} [options]
15364       *        The key/value store of player options.
15365       */
15366      function SeekBar(player, options) {
15367        var _this;
15368  
15369        _this = _Slider.call(this, player, options) || this;
15370  
15371        _this.setEventHandlers_();
15372  
15373        return _this;
15374      }
15375      /**
15376       * Sets the event handlers
15377       *
15378       * @private
15379       */
15380  
15381  
15382      var _proto = SeekBar.prototype;
15383  
15384      _proto.setEventHandlers_ = function setEventHandlers_() {
15385        this.update_ = bind(this, this.update);
15386        this.update = throttle(this.update_, UPDATE_REFRESH_INTERVAL);
15387        this.on(this.player_, ['ended', 'durationchange', 'timeupdate'], this.update);
15388  
15389        if (this.player_.liveTracker) {
15390          this.on(this.player_.liveTracker, 'liveedgechange', this.update);
15391        } // when playing, let's ensure we smoothly update the play progress bar
15392        // via an interval
15393  
15394  
15395        this.updateInterval = null;
15396        this.on(this.player_, ['playing'], this.enableInterval_);
15397        this.on(this.player_, ['ended', 'pause', 'waiting'], this.disableInterval_); // we don't need to update the play progress if the document is hidden,
15398        // also, this causes the CPU to spike and eventually crash the page on IE11.
15399  
15400        if ('hidden' in document && 'visibilityState' in document) {
15401          this.on(document, 'visibilitychange', this.toggleVisibility_);
15402        }
15403      };
15404  
15405      _proto.toggleVisibility_ = function toggleVisibility_(e) {
15406        if (document.hidden) {
15407          this.disableInterval_(e);
15408        } else {
15409          this.enableInterval_(); // we just switched back to the page and someone may be looking, so, update ASAP
15410  
15411          this.update();
15412        }
15413      };
15414  
15415      _proto.enableInterval_ = function enableInterval_() {
15416        if (this.updateInterval) {
15417          return;
15418        }
15419  
15420        this.updateInterval = this.setInterval(this.update, UPDATE_REFRESH_INTERVAL);
15421      };
15422  
15423      _proto.disableInterval_ = function disableInterval_(e) {
15424        if (this.player_.liveTracker && this.player_.liveTracker.isLive() && e && e.type !== 'ended') {
15425          return;
15426        }
15427  
15428        if (!this.updateInterval) {
15429          return;
15430        }
15431  
15432        this.clearInterval(this.updateInterval);
15433        this.updateInterval = null;
15434      }
15435      /**
15436       * Create the `Component`'s DOM element
15437       *
15438       * @return {Element}
15439       *         The element that was created.
15440       */
15441      ;
15442  
15443      _proto.createEl = function createEl() {
15444        return _Slider.prototype.createEl.call(this, 'div', {
15445          className: 'vjs-progress-holder'
15446        }, {
15447          'aria-label': this.localize('Progress Bar')
15448        });
15449      }
15450      /**
15451       * This function updates the play progress bar and accessibility
15452       * attributes to whatever is passed in.
15453       *
15454       * @param {EventTarget~Event} [event]
15455       *        The `timeupdate` or `ended` event that caused this to run.
15456       *
15457       * @listens Player#timeupdate
15458       *
15459       * @return {number}
15460       *          The current percent at a number from 0-1
15461       */
15462      ;
15463  
15464      _proto.update = function update(event) {
15465        var _this2 = this;
15466  
15467        var percent = _Slider.prototype.update.call(this);
15468  
15469        this.requestAnimationFrame(function () {
15470          var currentTime = _this2.player_.ended() ? _this2.player_.duration() : _this2.getCurrentTime_();
15471          var liveTracker = _this2.player_.liveTracker;
15472  
15473          var duration = _this2.player_.duration();
15474  
15475          if (liveTracker && liveTracker.isLive()) {
15476            duration = _this2.player_.liveTracker.liveCurrentTime();
15477          }
15478  
15479          if (_this2.percent_ !== percent) {
15480            // machine readable value of progress bar (percentage complete)
15481            _this2.el_.setAttribute('aria-valuenow', (percent * 100).toFixed(2));
15482  
15483            _this2.percent_ = percent;
15484          }
15485  
15486          if (_this2.currentTime_ !== currentTime || _this2.duration_ !== duration) {
15487            // human readable value of progress bar (time complete)
15488            _this2.el_.setAttribute('aria-valuetext', _this2.localize('progress bar timing: currentTime={1} duration={2}
15488', [formatTime(currentTime, duration), formatTime(duration, duration)], '{1} of {2}'));
15489  
15490            _this2.currentTime_ = currentTime;
15491            _this2.duration_ = duration;
15492          } // update the progress bar time tooltip with the current time
15493  
15494  
15495          if (_this2.bar) {
15496            _this2.bar.update(getBoundingClientRect(_this2.el()), _this2.getProgress());
15497          }
15498        });
15499        return percent;
15500      }
15501      /**
15502       * Get the value of current time but allows for smooth scrubbing,
15503       * when player can't keep up.
15504       *
15505       * @return {number}
15506       *         The current time value to display
15507       *
15508       * @private
15509       */
15510      ;
15511  
15512      _proto.getCurrentTime_ = function getCurrentTime_() {
15513        return this.player_.scrubbing() ? this.player_.getCache().currentTime : this.player_.currentTime();
15514      }
15515      /**
15516       * Get the percentage of media played so far.
15517       *
15518       * @return {number}
15519       *         The percentage of media played so far (0 to 1).
15520       */
15521      ;
15522  
15523      _proto.getPercent = function getPercent() {
15524        var currentTime = this.getCurrentTime_();
15525        var percent;
15526        var liveTracker = this.player_.liveTracker;
15527  
15528        if (liveTracker && liveTracker.isLive()) {
15529          percent = (currentTime - liveTracker.seekableStart()) / liveTracker.liveWindow(); // prevent the percent from changing at the live edge
15530  
15531          if (liveTracker.atLiveEdge()) {
15532            percent = 1;
15533          }
15534        } else {
15535          percent = currentTime / this.player_.duration();
15536        }
15537  
15538        return percent;
15539      }
15540      /**
15541       * Handle mouse down on seek bar
15542       *
15543       * @param {EventTarget~Event} event
15544       *        The `mousedown` event that caused this to run.
15545       *
15546       * @listens mousedown
15547       */
15548      ;
15549  
15550      _proto.handleMouseDown = function handleMouseDown(event) {
15551        if (!isSingleLeftClick(event)) {
15552          return;
15553        } // Stop event propagation to prevent double fire in progress-control.js
15554  
15555  
15556        event.stopPropagation();
15557        this.player_.scrubbing(true);
15558        this.videoWasPlaying = !this.player_.paused();
15559        this.player_.pause();
15560  
15561        _Slider.prototype.handleMouseDown.call(this, event);
15562      }
15563      /**
15564       * Handle mouse move on seek bar
15565       *
15566       * @param {EventTarget~Event} event
15567       *        The `mousemove` event that caused this to run.
15568       *
15569       * @listens mousemove
15570       */
15571      ;
15572  
15573      _proto.handleMouseMove = function handleMouseMove(event) {
15574        if (!isSingleLeftClick(event)) {
15575          return;
15576        }
15577  
15578        var newTime;
15579        var distance = this.calculateDistance(event);
15580        var liveTracker = this.player_.liveTracker;
15581  
15582        if (!liveTracker || !liveTracker.isLive()) {
15583          newTime = distance * this.player_.duration(); // Don't let video end while scrubbing.
15584  
15585          if (newTime === this.player_.duration()) {
15586            newTime = newTime - 0.1;
15587          }
15588        } else {
15589          if (distance >= 0.99) {
15590            liveTracker.seekToLiveEdge();
15591            return;
15592          }
15593  
15594          var seekableStart = liveTracker.seekableStart();
15595          var seekableEnd = liveTracker.liveCurrentTime();
15596          newTime = seekableStart + distance * liveTracker.liveWindow(); // Don't let video end while scrubbing.
15597  
15598          if (newTime >= seekableEnd) {
15599            newTime = seekableEnd;
15600          } // Compensate for precision differences so that currentTime is not less
15601          // than seekable start
15602  
15603  
15604          if (newTime <= seekableStart) {
15605            newTime = seekableStart + 0.1;
15606          } // On android seekableEnd can be Infinity sometimes,
15607          // this will cause newTime to be Infinity, which is
15608          // not a valid currentTime.
15609  
15610  
15611          if (newTime === Infinity) {
15612            return;
15613          }
15614        } // Set new time (tell player to seek to new time)
15615  
15616  
15617        this.player_.currentTime(newTime);
15618      };
15619  
15620      _proto.enable = function enable() {
15621        _Slider.prototype.enable.call(this);
15622  
15623        var mouseTimeDisplay = this.getChild('mouseTimeDisplay');
15624  
15625        if (!mouseTimeDisplay) {
15626          return;
15627        }
15628  
15629        mouseTimeDisplay.show();
15630      };
15631  
15632      _proto.disable = function disable() {
15633        _Slider.prototype.disable.call(this);
15634  
15635        var mouseTimeDisplay = this.getChild('mouseTimeDisplay');
15636  
15637        if (!mouseTimeDisplay) {
15638          return;
15639        }
15640  
15641        mouseTimeDisplay.hide();
15642      }
15643      /**
15644       * Handle mouse up on seek bar
15645       *
15646       * @param {EventTarget~Event} event
15647       *        The `mouseup` event that caused this to run.
15648       *
15649       * @listens mouseup
15650       */
15651      ;
15652  
15653      _proto.handleMouseUp = function handleMouseUp(event) {
15654        _Slider.prototype.handleMouseUp.call(this, event); // Stop event propagation to prevent double fire in progress-control.js
15655  
15656  
15657        if (event) {
15658          event.stopPropagation();
15659        }
15660  
15661        this.player_.scrubbing(false);
15662        /**
15663         * Trigger timeupdate because we're done seeking and the time has changed.
15664         * This is particularly useful for if the player is paused to time the time displays.
15665         *
15666         * @event Tech#timeupdate
15667         * @type {EventTarget~Event}
15668         */
15669  
15670        this.player_.trigger({
15671          type: 'timeupdate',
15672          target: this,
15673          manuallyTriggered: true
15674        });
15675  
15676        if (this.videoWasPlaying) {
15677          silencePromise(this.player_.play());
15678        } else {
15679          // We're done seeking and the time has changed.
15680          // If the player is paused, make sure we display the correct time on the seek bar.
15681          this.update_();
15682        }
15683      }
15684      /**
15685       * Move more quickly fast forward for keyboard-only users
15686       */
15687      ;
15688  
15689      _proto.stepForward = function stepForward() {
15690        this.player_.currentTime(this.player_.currentTime() + STEP_SECONDS);
15691      }
15692      /**
15693       * Move more quickly rewind for keyboard-only users
15694       */
15695      ;
15696  
15697      _proto.stepBack = function stepBack() {
15698        this.player_.currentTime(this.player_.currentTime() - STEP_SECONDS);
15699      }
15700      /**
15701       * Toggles the playback state of the player
15702       * This gets called when enter or space is used on the seekbar
15703       *
15704       * @param {EventTarget~Event} event
15705       *        The `keydown` event that caused this function to be called
15706       *
15707       */
15708      ;
15709  
15710      _proto.handleAction = function handleAction(event) {
15711        if (this.player_.paused()) {
15712          this.player_.play();
15713        } else {
15714          this.player_.pause();
15715        }
15716      }
15717      /**
15718       * Called when this SeekBar has focus and a key gets pressed down.
15719       * Supports the following keys:
15720       *
15721       *   Space or Enter key fire a click event
15722       *   Home key moves to start of the timeline
15723       *   End key moves to end of the timeline
15724       *   Digit "0" through "9" keys move to 0%, 10% ... 80%, 90% of the timeline
15725       *   PageDown key moves back a larger step than ArrowDown
15726       *   PageUp key moves forward a large step
15727       *
15728       * @param {EventTarget~Event} event
15729       *        The `keydown` event that caused this function to be called.
15730       *
15731       * @listens keydown
15732       */
15733      ;
15734  
15735      _proto.handleKeyDown = function handleKeyDown(event) {
15736        if (keycode.isEventKey(event, 'Space') || keycode.isEventKey(event, 'Enter')) {
15737          event.preventDefault();
15738          event.stopPropagation();
15739          this.handleAction(event);
15740        } else if (keycode.isEventKey(event, 'Home')) {
15741          event.preventDefault();
15742          event.stopPropagation();
15743          this.player_.currentTime(0);
15744        } else if (keycode.isEventKey(event, 'End')) {
15745          event.preventDefault();
15746          event.stopPropagation();
15747          this.player_.currentTime(this.player_.duration());
15748        } else if (/^[0-9]$/.test(keycode(event))) {
15749          event.preventDefault();
15750          event.stopPropagation();
15751          var gotoFraction = (keycode.codes[keycode(event)] - keycode.codes['0']) * 10.0 / 100.0;
15752          this.player_.currentTime(this.player_.duration() * gotoFraction);
15753        } else if (keycode.isEventKey(event, 'PgDn')) {
15754          event.preventDefault();
15755          event.stopPropagation();
15756          this.player_.currentTime(this.player_.currentTime() - STEP_SECONDS * PAGE_KEY_MULTIPLIER);
15757        } else if (keycode.isEventKey(event, 'PgUp')) {
15758          event.preventDefault();
15759          event.stopPropagation();
15760          this.player_.currentTime(this.player_.currentTime() + STEP_SECONDS * PAGE_KEY_MULTIPLIER);
15761        } else {
15762          // Pass keydown handling up for unsupported keys
15763          _Slider.prototype.handleKeyDown.call(this, event);
15764        }
15765      };
15766  
15767      _proto.dispose = function dispose() {
15768        this.disableInterval_();
15769        this.off(this.player_, ['ended', 'durationchange', 'timeupdate'], this.update);
15770  
15771        if (this.player_.liveTracker) {
15772          this.on(this.player_.liveTracker, 'liveedgechange', this.update);
15773        }
15774  
15775        this.off(this.player_, ['playing'], this.enableInterval_);
15776        this.off(this.player_, ['ended', 'pause', 'waiting'], this.disableInterval_); // we don't need to update the play progress if the document is hidden,
15777        // also, this causes the CPU to spike and eventually crash the page on IE11.
15778  
15779        if ('hidden' in document && 'visibilityState' in document) {
15780          this.off(document, 'visibilitychange', this.toggleVisibility_);
15781        }
15782  
15783        _Slider.prototype.dispose.call(this);
15784      };
15785  
15786      return SeekBar;
15787    }(Slider);
15788    /**
15789     * Default options for the `SeekBar`
15790     *
15791     * @type {Object}
15792     * @private
15793     */
15794  
15795  
15796    SeekBar.prototype.options_ = {
15797      children: ['loadProgressBar', 'playProgressBar'],
15798      barName: 'playProgressBar'
15799    }; // MouseTimeDisplay tooltips should not be added to a player on mobile devices
15800  
15801    if (!IS_IOS && !IS_ANDROID) {
15802      SeekBar.prototype.options_.children.splice(1, 0, 'mouseTimeDisplay');
15803    }
15804  
15805    Component.registerComponent('SeekBar', SeekBar);
15806  
15807    /**
15808     * The Progress Control component contains the seek bar, load progress,
15809     * and play progress.
15810     *
15811     * @extends Component
15812     */
15813  
15814    var ProgressControl = /*#__PURE__*/function (_Component) {
15815      inheritsLoose(ProgressControl, _Component);
15816  
15817      /**
15818       * Creates an instance of this class.
15819       *
15820       * @param {Player} player
15821       *        The `Player` that this class should be attached to.
15822       *
15823       * @param {Object} [options]
15824       *        The key/value store of player options.
15825       */
15826      function ProgressControl(player, options) {
15827        var _this;
15828  
15829        _this = _Component.call(this, player, options) || this;
15830        _this.handleMouseMove = throttle(bind(assertThisInitialized(_this), _this.handleMouseMove), UPDATE_REFRESH_INTERVAL);
15831        _this.throttledHandleMouseSeek = throttle(bind(assertThisInitialized(_this), _this.handleMouseSeek), UPDATE_REFRESH_INTERVAL);
15832  
15833        _this.enable();
15834  
15835        return _this;
15836      }
15837      /**
15838       * Create the `Component`'s DOM element
15839       *
15840       * @return {Element}
15841       *         The element that was created.
15842       */
15843  
15844  
15845      var _proto = ProgressControl.prototype;
15846  
15847      _proto.createEl = function createEl() {
15848        return _Component.prototype.createEl.call(this, 'div', {
15849          className: 'vjs-progress-control vjs-control'
15850        });
15851      }
15852      /**
15853       * When the mouse moves over the `ProgressControl`, the pointer position
15854       * gets passed down to the `MouseTimeDisplay` component.
15855       *
15856       * @param {EventTarget~Event} event
15857       *        The `mousemove` event that caused this function to run.
15858       *
15859       * @listen mousemove
15860       */
15861      ;
15862  
15863      _proto.handleMouseMove = function handleMouseMove(event) {
15864        var seekBar = this.getChild('seekBar');
15865  
15866        if (!seekBar) {
15867          return;
15868        }
15869  
15870        var playProgressBar = seekBar.getChild('playProgressBar');
15871        var mouseTimeDisplay = seekBar.getChild('mouseTimeDisplay');
15872  
15873        if (!playProgressBar && !mouseTimeDisplay) {
15874          return;
15875        }
15876  
15877        var seekBarEl = seekBar.el();
15878        var seekBarRect = getBoundingClientRect(seekBarEl);
15879        var seekBarPoint = getPointerPosition(seekBarEl, event).x; // The default skin has a gap on either side of the `SeekBar`. This means
15880        // that it's possible to trigger this behavior outside the boundaries of
15881        // the `SeekBar`. This ensures we stay within it at all times.
15882  
15883        seekBarPoint = clamp(0, 1, seekBarPoint);
15884  
15885        if (mouseTimeDisplay) {
15886          mouseTimeDisplay.update(seekBarRect, seekBarPoint);
15887        }
15888  
15889        if (playProgressBar) {
15890          playProgressBar.update(seekBarRect, seekBar.getProgress());
15891        }
15892      }
15893      /**
15894       * A throttled version of the {@link ProgressControl#handleMouseSeek} listener.
15895       *
15896       * @method ProgressControl#throttledHandleMouseSeek
15897       * @param {EventTarget~Event} event
15898       *        The `mousemove` event that caused this function to run.
15899       *
15900       * @listen mousemove
15901       * @listen touchmove
15902       */
15903  
15904      /**
15905       * Handle `mousemove` or `touchmove` events on the `ProgressControl`.
15906       *
15907       * @param {EventTarget~Event} event
15908       *        `mousedown` or `touchstart` event that triggered this function
15909       *
15910       * @listens mousemove
15911       * @listens touchmove
15912       */
15913      ;
15914  
15915      _proto.handleMouseSeek = function handleMouseSeek(event) {
15916        var seekBar = this.getChild('seekBar');
15917  
15918        if (seekBar) {
15919          seekBar.handleMouseMove(event);
15920        }
15921      }
15922      /**
15923       * Are controls are currently enabled for this progress control.
15924       *
15925       * @return {boolean}
15926       *         true if controls are enabled, false otherwise
15927       */
15928      ;
15929  
15930      _proto.enabled = function enabled() {
15931        return this.enabled_;
15932      }
15933      /**
15934       * Disable all controls on the progress control and its children
15935       */
15936      ;
15937  
15938      _proto.disable = function disable() {
15939        this.children().forEach(function (child) {
15940          return child.disable && child.disable();
15941        });
15942  
15943        if (!this.enabled()) {
15944          return;
15945        }
15946  
15947        this.off(['mousedown', 'touchstart'], this.handleMouseDown);
15948        this.off(this.el_, 'mousemove', this.handleMouseMove);
15949        this.handleMouseUp();
15950        this.addClass('disabled');
15951        this.enabled_ = false;
15952      }
15953      /**
15954       * Enable all controls on the progress control and its children
15955       */
15956      ;
15957  
15958      _proto.enable = function enable() {
15959        this.children().forEach(function (child) {
15960          return child.enable && child.enable();
15961        });
15962  
15963        if (this.enabled()) {
15964          return;
15965        }
15966  
15967        this.on(['mousedown', 'touchstart'], this.handleMouseDown);
15968        this.on(this.el_, 'mousemove', this.handleMouseMove);
15969        this.removeClass('disabled');
15970        this.enabled_ = true;
15971      }
15972      /**
15973       * Handle `mousedown` or `touchstart` events on the `ProgressControl`.
15974       *
15975       * @param {EventTarget~Event} event
15976       *        `mousedown` or `touchstart` event that triggered this function
15977       *
15978       * @listens mousedown
15979       * @listens touchstart
15980       */
15981      ;
15982  
15983      _proto.handleMouseDown = function handleMouseDown(event) {
15984        var doc = this.el_.ownerDocument;
15985        var seekBar = this.getChild('seekBar');
15986  
15987        if (seekBar) {
15988          seekBar.handleMouseDown(event);
15989        }
15990  
15991        this.on(doc, 'mousemove', this.throttledHandleMouseSeek);
15992        this.on(doc, 'touchmove', this.throttledHandleMouseSeek);
15993        this.on(doc, 'mouseup', this.handleMouseUp);
15994        this.on(doc, 'touchend', this.handleMouseUp);
15995      }
15996      /**
15997       * Handle `mouseup` or `touchend` events on the `ProgressControl`.
15998       *
15999       * @param {EventTarget~Event} event
16000       *        `mouseup` or `touchend` event that triggered this function.
16001       *
16002       * @listens touchend
16003       * @listens mouseup
16004       */
16005      ;
16006  
16007      _proto.handleMouseUp = function handleMouseUp(event) {
16008        var doc = this.el_.ownerDocument;
16009        var seekBar = this.getChild('seekBar');
16010  
16011        if (seekBar) {
16012          seekBar.handleMouseUp(event);
16013        }
16014  
16015        this.off(doc, 'mousemove', this.throttledHandleMouseSeek);
16016        this.off(doc, 'touchmove', this.throttledHandleMouseSeek);
16017        this.off(doc, 'mouseup', this.handleMouseUp);
16018        this.off(doc, 'touchend', this.handleMouseUp);
16019      };
16020  
16021      return ProgressControl;
16022    }(Component);
16023    /**
16024     * Default options for `ProgressControl`
16025     *
16026     * @type {Object}
16027     * @private
16028     */
16029  
16030  
16031    ProgressControl.prototype.options_ = {
16032      children: ['seekBar']
16033    };
16034    Component.registerComponent('ProgressControl', ProgressControl);
16035  
16036    /**
16037     * Toggle Picture-in-Picture mode
16038     *
16039     * @extends Button
16040     */
16041  
16042    var PictureInPictureToggle = /*#__PURE__*/function (_Button) {
16043      inheritsLoose(PictureInPictureToggle, _Button);
16044  
16045      /**
16046       * Creates an instance of this class.
16047       *
16048       * @param {Player} player
16049       *        The `Player` that this class should be attached to.
16050       *
16051       * @param {Object} [options]
16052       *        The key/value store of player options.
16053       *
16054       * @listens Player#enterpictureinpicture
16055       * @listens Player#leavepictureinpicture
16056       */
16057      function PictureInPictureToggle(player, options) {
16058        var _this;
16059  
16060        _this = _Button.call(this, player, options) || this;
16061  
16062        _this.on(player, ['enterpictureinpicture', 'leavepictureinpicture'], _this.handlePictureInPictureChange); // TODO: Activate button on player loadedmetadata event.
16063        // TODO: Deactivate button on player emptied event.
16064        // TODO: Deactivate button if disablepictureinpicture attribute is present.
16065  
16066  
16067        if (!document.pictureInPictureEnabled) {
16068          _this.disable();
16069        }
16070  
16071        return _this;
16072      }
16073      /**
16074       * Builds the default DOM `className`.
16075       *
16076       * @return {string}
16077       *         The DOM `className` for this object.
16078       */
16079  
16080  
16081      var _proto = PictureInPictureToggle.prototype;
16082  
16083      _proto.buildCSSClass = function buildCSSClass() {
16084        return "vjs-picture-in-picture-control " + _Button.prototype.buildCSSClass.call(this);
16085      }
16086      /**
16087       * Handles enterpictureinpicture and leavepictureinpicture on the player and change control text accordingly.
16088       *
16089       * @param {EventTarget~Event} [event]
16090       *        The {@link Player#enterpictureinpicture} or {@link Player#leavepictureinpicture} event that caused this function to be
16091       *        called.
16092       *
16093       * @listens Player#enterpictureinpicture
16094       * @listens Player#leavepictureinpicture
16095       */
16096      ;
16097  
16098      _proto.handlePictureInPictureChange = function handlePictureInPictureChange(event) {
16099        if (this.player_.isInPictureInPicture()) {
16100          this.controlText('Exit Picture-in-Picture');
16101        } else {
16102          this.controlText('Picture-in-Picture');
16103        }
16104      }
16105      /**
16106       * This gets called when an `PictureInPictureToggle` is "clicked". See
16107       * {@link ClickableComponent} for more detailed information on what a click can be.
16108       *
16109       * @param {EventTarget~Event} [event]
16110       *        The `keydown`, `tap`, or `click` event that caused this function to be
16111       *        called.
16112       *
16113       * @listens tap
16114       * @listens click
16115       */
16116      ;
16117  
16118      _proto.handleClick = function handleClick(event) {
16119        if (!this.player_.isInPictureInPicture()) {
16120          this.player_.requestPictureInPicture();
16121        } else {
16122          this.player_.exitPictureInPicture();
16123        }
16124      };
16125  
16126      return PictureInPictureToggle;
16127    }(Button);
16128    /**
16129     * The text that should display over the `PictureInPictureToggle`s controls. Added for localization.
16130     *
16131     * @type {string}
16132     * @private
16133     */
16134  
16135  
16136    PictureInPictureToggle.prototype.controlText_ = 'Picture-in-Picture';
16137    Component.registerComponent('PictureInPictureToggle', PictureInPictureToggle);
16138  
16139    /**
16140     * Toggle fullscreen video
16141     *
16142     * @extends Button
16143     */
16144  
16145    var FullscreenToggle = /*#__PURE__*/function (_Button) {
16146      inheritsLoose(FullscreenToggle, _Button);
16147  
16148      /**
16149       * Creates an instance of this class.
16150       *
16151       * @param {Player} player
16152       *        The `Player` that this class should be attached to.
16153       *
16154       * @param {Object} [options]
16155       *        The key/value store of player options.
16156       */
16157      function FullscreenToggle(player, options) {
16158        var _this;
16159  
16160        _this = _Button.call(this, player, options) || this;
16161  
16162        _this.on(player, 'fullscreenchange', _this.handleFullscreenChange);
16163  
16164        if (document[player.fsApi_.fullscreenEnabled] === false) {
16165          _this.disable();
16166        }
16167  
16168        return _this;
16169      }
16170      /**
16171       * Builds the default DOM `className`.
16172       *
16173       * @return {string}
16174       *         The DOM `className` for this object.
16175       */
16176  
16177  
16178      var _proto = FullscreenToggle.prototype;
16179  
16180      _proto.buildCSSClass = function buildCSSClass() {
16181        return "vjs-fullscreen-control " + _Button.prototype.buildCSSClass.call(this);
16182      }
16183      /**
16184       * Handles fullscreenchange on the player and change control text accordingly.
16185       *
16186       * @param {EventTarget~Event} [event]
16187       *        The {@link Player#fullscreenchange} event that caused this function to be
16188       *        called.
16189       *
16190       * @listens Player#fullscreenchange
16191       */
16192      ;
16193  
16194      _proto.handleFullscreenChange = function handleFullscreenChange(event) {
16195        if (this.player_.isFullscreen()) {
16196          this.controlText('Non-Fullscreen');
16197        } else {
16198          this.controlText('Fullscreen');
16199        }
16200      }
16201      /**
16202       * This gets called when an `FullscreenToggle` is "clicked". See
16203       * {@link ClickableComponent} for more detailed information on what a click can be.
16204       *
16205       * @param {EventTarget~Event} [event]
16206       *        The `keydown`, `tap`, or `click` event that caused this function to be
16207       *        called.
16208       *
16209       * @listens tap
16210       * @listens click
16211       */
16212      ;
16213  
16214      _proto.handleClick = function handleClick(event) {
16215        if (!this.player_.isFullscreen()) {
16216          this.player_.requestFullscreen();
16217        } else {
16218          this.player_.exitFullscreen();
16219        }
16220      };
16221  
16222      return FullscreenToggle;
16223    }(Button);
16224    /**
16225     * The text that should display over the `FullscreenToggle`s controls. Added for localization.
16226     *
16227     * @type {string}
16228     * @private
16229     */
16230  
16231  
16232    FullscreenToggle.prototype.controlText_ = 'Fullscreen';
16233    Component.registerComponent('FullscreenToggle', FullscreenToggle);
16234  
16235    /**
16236     * Check if volume control is supported and if it isn't hide the
16237     * `Component` that was passed  using the `vjs-hidden` class.
16238     *
16239     * @param {Component} self
16240     *        The component that should be hidden if volume is unsupported
16241     *
16242     * @param {Player} player
16243     *        A reference to the player
16244     *
16245     * @private
16246     */
16247    var checkVolumeSupport = function checkVolumeSupport(self, player) {
16248      // hide volume controls when they're not supported by the current tech
16249      if (player.tech_ && !player.tech_.featuresVolumeControl) {
16250        self.addClass('vjs-hidden');
16251      }
16252  
16253      self.on(player, 'loadstart', function () {
16254        if (!player.tech_.featuresVolumeControl) {
16255          self.addClass('vjs-hidden');
16256        } else {
16257          self.removeClass('vjs-hidden');
16258        }
16259      });
16260    };
16261  
16262    /**
16263     * Shows volume level
16264     *
16265     * @extends Component
16266     */
16267  
16268    var VolumeLevel = /*#__PURE__*/function (_Component) {
16269      inheritsLoose(VolumeLevel, _Component);
16270  
16271      function VolumeLevel() {
16272        return _Component.apply(this, arguments) || this;
16273      }
16274  
16275      var _proto = VolumeLevel.prototype;
16276  
16277      /**
16278       * Create the `Component`'s DOM element
16279       *
16280       * @return {Element}
16281       *         The element that was created.
16282       */
16283      _proto.createEl = function createEl() {
16284        return _Component.prototype.createEl.call(this, 'div', {
16285          className: 'vjs-volume-level',
16286          innerHTML: '<span class="vjs-control-text"></span>'
16287        });
16288      };
16289  
16290      return VolumeLevel;
16291    }(Component);
16292  
16293    Component.registerComponent('VolumeLevel', VolumeLevel);
16294  
16295    /**
16296     * The bar that contains the volume level and can be clicked on to adjust the level
16297     *
16298     * @extends Slider
16299     */
16300  
vendor: 11,935 bytes, lines 16301-16714
16301    var VolumeBar = /*#__PURE__*/function (_Slider) {
16302      inheritsLoose(VolumeBar, _Slider);
16303  
16304      /**
16305       * Creates an instance of this class.
16306       *
16307       * @param {Player} player
16308       *        The `Player` that this class should be attached to.
16309       *
16310       * @param {Object} [options]
16311       *        The key/value store of player options.
16312       */
16313      function VolumeBar(player, options) {
16314        var _this;
16315  
16316        _this = _Slider.call(this, player, options) || this;
16317  
16318        _this.on('slideractive', _this.updateLastVolume_);
16319  
16320        _this.on(player, 'volumechange', _this.updateARIAAttributes);
16321  
16322        player.ready(function () {
16323          return _this.updateARIAAttributes();
16324        });
16325        return _this;
16326      }
16327      /**
16328       * Create the `Component`'s DOM element
16329       *
16330       * @return {Element}
16331       *         The element that was created.
16332       */
16333  
16334  
16335      var _proto = VolumeBar.prototype;
16336  
16337      _proto.createEl = function createEl() {
16338        return _Slider.prototype.createEl.call(this, 'div', {
16339          className: 'vjs-volume-bar vjs-slider-bar'
16340        }, {
16341          'aria-label': this.localize('Volume Level'),
16342          'aria-live': 'polite'
16343        });
16344      }
16345      /**
16346       * Handle mouse down on volume bar
16347       *
16348       * @param {EventTarget~Event} event
16349       *        The `mousedown` event that caused this to run.
16350       *
16351       * @listens mousedown
16352       */
16353      ;
16354  
16355      _proto.handleMouseDown = function handleMouseDown(event) {
16356        if (!isSingleLeftClick(event)) {
16357          return;
16358        }
16359  
16360        _Slider.prototype.handleMouseDown.call(this, event);
16361      }
16362      /**
16363       * Handle movement events on the {@link VolumeMenuButton}.
16364       *
16365       * @param {EventTarget~Event} event
16366       *        The event that caused this function to run.
16367       *
16368       * @listens mousemove
16369       */
16370      ;
16371  
16372      _proto.handleMouseMove = function handleMouseMove(event) {
16373        if (!isSingleLeftClick(event)) {
16374          return;
16375        }
16376  
16377        this.checkMuted();
16378        this.player_.volume(this.calculateDistance(event));
16379      }
16380      /**
16381       * If the player is muted unmute it.
16382       */
16383      ;
16384  
16385      _proto.checkMuted = function checkMuted() {
16386        if (this.player_.muted()) {
16387          this.player_.muted(false);
16388        }
16389      }
16390      /**
16391       * Get percent of volume level
16392       *
16393       * @return {number}
16394       *         Volume level percent as a decimal number.
16395       */
16396      ;
16397  
16398      _proto.getPercent = function getPercent() {
16399        if (this.player_.muted()) {
16400          return 0;
16401        }
16402  
16403        return this.player_.volume();
16404      }
16405      /**
16406       * Increase volume level for keyboard users
16407       */
16408      ;
16409  
16410      _proto.stepForward = function stepForward() {
16411        this.checkMuted();
16412        this.player_.volume(this.player_.volume() + 0.1);
16413      }
16414      /**
16415       * Decrease volume level for keyboard users
16416       */
16417      ;
16418  
16419      _proto.stepBack = function stepBack() {
16420        this.checkMuted();
16421        this.player_.volume(this.player_.volume() - 0.1);
16422      }
16423      /**
16424       * Update ARIA accessibility attributes
16425       *
16426       * @param {EventTarget~Event} [event]
16427       *        The `volumechange` event that caused this function to run.
16428       *
16429       * @listens Player#volumechange
16430       */
16431      ;
16432  
16433      _proto.updateARIAAttributes = function updateARIAAttributes(event) {
16434        var ariaValue = this.player_.muted() ? 0 : this.volumeAsPercentage_();
16435        this.el_.setAttribute('aria-valuenow', ariaValue);
16436        this.el_.setAttribute('aria-valuetext', ariaValue + '%');
16437      }
16438      /**
16439       * Returns the current value of the player volume as a percentage
16440       *
16441       * @private
16442       */
16443      ;
16444  
16445      _proto.volumeAsPercentage_ = function volumeAsPercentage_() {
16446        return Math.round(this.player_.volume() * 100);
16447      }
16448      /**
16449       * When user starts dragging the VolumeBar, store the volume and listen for
16450       * the end of the drag. When the drag ends, if the volume was set to zero,
16451       * set lastVolume to the stored volume.
16452       *
16453       * @listens slideractive
16454       * @private
16455       */
16456      ;
16457  
16458      _proto.updateLastVolume_ = function updateLastVolume_() {
16459        var _this2 = this;
16460  
16461        var volumeBeforeDrag = this.player_.volume();
16462        this.one('sliderinactive', function () {
16463          if (_this2.player_.volume() === 0) {
16464            _this2.player_.lastVolume_(volumeBeforeDrag);
16465          }
16466        });
16467      };
16468  
16469      return VolumeBar;
16470    }(Slider);
16471    /**
16472     * Default options for the `VolumeBar`
16473     *
16474     * @type {Object}
16475     * @private
16476     */
16477  
16478  
16479    VolumeBar.prototype.options_ = {
16480      children: ['volumeLevel'],
16481      barName: 'volumeLevel'
16482    };
16483    /**
16484     * Call the update event for this Slider when this event happens on the player.
16485     *
16486     * @type {string}
16487     */
16488  
16489    VolumeBar.prototype.playerEvent = 'volumechange';
16490    Component.registerComponent('VolumeBar', VolumeBar);
16491  
16492    /**
16493     * The component for controlling the volume level
16494     *
16495     * @extends Component
16496     */
16497  
16498    var VolumeControl = /*#__PURE__*/function (_Component) {
16499      inheritsLoose(VolumeControl, _Component);
16500  
16501      /**
16502       * Creates an instance of this class.
16503       *
16504       * @param {Player} player
16505       *        The `Player` that this class should be attached to.
16506       *
16507       * @param {Object} [options={}]
16508       *        The key/value store of player options.
16509       */
16510      function VolumeControl(player, options) {
16511        var _this;
16512  
16513        if (options === void 0) {
16514          options = {};
16515        }
16516  
16517        options.vertical = options.vertical || false; // Pass the vertical option down to the VolumeBar if
16518        // the VolumeBar is turned on.
16519  
16520        if (typeof options.volumeBar === 'undefined' || isPlain(options.volumeBar)) {
16521          options.volumeBar = options.volumeBar || {};
16522          options.volumeBar.vertical = options.vertical;
16523        }
16524  
16525        _this = _Component.call(this, player, options) || this; // hide this control if volume support is missing
16526  
16527        checkVolumeSupport(assertThisInitialized(_this), player);
16528        _this.throttledHandleMouseMove = throttle(bind(assertThisInitialized(_this), _this.handleMouseMove), UPDATE_REFRESH_INTERVAL);
16529  
16530        _this.on('mousedown', _this.handleMouseDown);
16531  
16532        _this.on('touchstart', _this.handleMouseDown); // while the slider is active (the mouse has been pressed down and
16533        // is dragging) or in focus we do not want to hide the VolumeBar
16534  
16535  
16536        _this.on(_this.volumeBar, ['focus', 'slideractive'], function () {
16537          _this.volumeBar.addClass('vjs-slider-active');
16538  
16539          _this.addClass('vjs-slider-active');
16540  
16541          _this.trigger('slideractive');
16542        });
16543  
16544        _this.on(_this.volumeBar, ['blur', 'sliderinactive'], function () {
16545          _this.volumeBar.removeClass('vjs-slider-active');
16546  
16547          _this.removeClass('vjs-slider-active');
16548  
16549          _this.trigger('sliderinactive');
16550        });
16551  
16552        return _this;
16553      }
16554      /**
16555       * Create the `Component`'s DOM element
16556       *
16557       * @return {Element}
16558       *         The element that was created.
16559       */
16560  
16561  
16562      var _proto = VolumeControl.prototype;
16563  
16564      _proto.createEl = function createEl() {
16565        var orientationClass = 'vjs-volume-horizontal';
16566  
16567        if (this.options_.vertical) {
16568          orientationClass = 'vjs-volume-vertical';
16569        }
16570  
16571        return _Component.prototype.createEl.call(this, 'div', {
16572          className: "vjs-volume-control vjs-control " + orientationClass
16573        });
16574      }
16575      /**
16576       * Handle `mousedown` or `touchstart` events on the `VolumeControl`.
16577       *
16578       * @param {EventTarget~Event} event
16579       *        `mousedown` or `touchstart` event that triggered this function
16580       *
16581       * @listens mousedown
16582       * @listens touchstart
16583       */
16584      ;
16585  
16586      _proto.handleMouseDown = function handleMouseDown(event) {
16587        var doc = this.el_.ownerDocument;
16588        this.on(doc, 'mousemove', this.throttledHandleMouseMove);
16589        this.on(doc, 'touchmove', this.throttledHandleMouseMove);
16590        this.on(doc, 'mouseup', this.handleMouseUp);
16591        this.on(doc, 'touchend', this.handleMouseUp);
16592      }
16593      /**
16594       * Handle `mouseup` or `touchend` events on the `VolumeControl`.
16595       *
16596       * @param {EventTarget~Event} event
16597       *        `mouseup` or `touchend` event that triggered this function.
16598       *
16599       * @listens touchend
16600       * @listens mouseup
16601       */
16602      ;
16603  
16604      _proto.handleMouseUp = function handleMouseUp(event) {
16605        var doc = this.el_.ownerDocument;
16606        this.off(doc, 'mousemove', this.throttledHandleMouseMove);
16607        this.off(doc, 'touchmove', this.throttledHandleMouseMove);
16608        this.off(doc, 'mouseup', this.handleMouseUp);
16609        this.off(doc, 'touchend', this.handleMouseUp);
16610      }
16611      /**
16612       * Handle `mousedown` or `touchstart` events on the `VolumeControl`.
16613       *
16614       * @param {EventTarget~Event} event
16615       *        `mousedown` or `touchstart` event that triggered this function
16616       *
16617       * @listens mousedown
16618       * @listens touchstart
16619       */
16620      ;
16621  
16622      _proto.handleMouseMove = function handleMouseMove(event) {
16623        this.volumeBar.handleMouseMove(event);
16624      };
16625  
16626      return VolumeControl;
16627    }(Component);
16628    /**
16629     * Default options for the `VolumeControl`
16630     *
16631     * @type {Object}
16632     * @private
16633     */
16634  
16635  
16636    VolumeControl.prototype.options_ = {
16637      children: ['volumeBar']
16638    };
16639    Component.registerComponent('VolumeControl', VolumeControl);
16640  
16641    /**
16642     * Check if muting volume is supported and if it isn't hide the mute toggle
16643     * button.
16644     *
16645     * @param {Component} self
16646     *        A reference to the mute toggle button
16647     *
16648     * @param {Player} player
16649     *        A reference to the player
16650     *
16651     * @private
16652     */
16653    var checkMuteSupport = function checkMuteSupport(self, player) {
16654      // hide mute toggle button if it's not supported by the current tech
16655      if (player.tech_ && !player.tech_.featuresMuteControl) {
16656        self.addClass('vjs-hidden');
16657      }
16658  
16659      self.on(player, 'loadstart', function () {
16660        if (!player.tech_.featuresMuteControl) {
16661          self.addClass('vjs-hidden');
16662        } else {
16663          self.removeClass('vjs-hidden');
16664        }
16665      });
16666    };
16667  
16668    /**
16669     * A button component for muting the audio.
16670     *
16671     * @extends Button
16672     */
16673  
16674    var MuteToggle = /*#__PURE__*/function (_Button) {
16675      inheritsLoose(MuteToggle, _Button);
16676  
16677      /**
16678       * Creates an instance of this class.
16679       *
16680       * @param {Player} player
16681       *        The `Player` that this class should be attached to.
16682       *
16683       * @param {Object} [options]
16684       *        The key/value store of player options.
16685       */
16686      function MuteToggle(player, options) {
16687        var _this;
16688  
16689        _this = _Button.call(this, player, options) || this; // hide this control if volume support is missing
16690  
16691        checkMuteSupport(assertThisInitialized(_this), player);
16692  
16693        _this.on(player, ['loadstart', 'volumechange'], _this.update);
16694  
16695        return _this;
16696      }
16697      /**
16698       * Builds the default DOM `className`.
16699       *
16700       * @return {string}
16701       *         The DOM `className` for this object.
16702       */
16703  
16704  
16705      var _proto = MuteToggle.prototype;
16706  
16707      _proto.buildCSSClass = function buildCSSClass() {
16708        return "vjs-mute-control " + _Button.prototype.buildCSSClass.call(this);
16709      }
16710      /**
16711       * This gets called when an `MuteToggle` is "clicked". See
16712       * {@link ClickableComponent} for more detailed information on what a click can be.
16713       *
16714       * @param {EventTarget~Event} [event]
16715       *        The `keydown`, `tap`, or `click` event that caused this function to be
16716       *        called.
16717       *
16718       * @listens tap
16719       * @listens click
16720       */
16721      ;
16722  
16723      _proto.handleClick = function handleClick(event) {
16724        var vol = this.player_.volume();
16725        var lastVolume = this.player_.lastVolume_();
16726  
16727        if (vol === 0) {
16728          var volumeToSet = lastVolume < 0.1 ? 0.1 : lastVolume;
16729          this.player_.volume(volumeToSet);
16730          this.player_.muted(false);
16731        } else {
16732          this.player_.muted(this.player_.muted() ? false : true);
16733        }
16734      }
16735      /**
16736       * Update the `MuteToggle` button based on the state of `volume` and `muted`
16737       * on the player.
16738       *
16739       * @param {EventTarget~Event} [event]
16740       *        The {@link Player#loadstart} event if this function was called
16741       *        through an event.
16742       *
16743       * @listens Player#loadstart
16744       * @listens Player#volumechange
16745       */
16746      ;
16747  
16748      _proto.update = function update(event) {
16749        this.updateIcon_();
16750        this.updateControlText_();
16751      }
16752      /**
16753       * Update the appearance of the `MuteToggle` icon.
16754       *
16755       * Possible states (given `level` variable below):
16756       * - 0: crossed out
16757       * - 1: zero bars of volume
16758       * - 2: one bar of volume
16759       * - 3: two bars of volume
16760       *
16761       * @private
16762       */
16763      ;
16764  
16765      _proto.updateIcon_ = function updateIcon_() {
16766        var vol = this.player_.volume();
16767        var level = 3; // in iOS when a player is loaded with muted attribute
16768        // and volume is changed with a native mute button
16769        // we want to make sure muted state is updated
16770  
16771        if (IS_IOS && this.player_.tech_ && this.player_.tech_.el_) {
16772          this.player_.muted(this.player_.tech_.el_.muted);
16773        }
16774  
16775        if (vol === 0 || this.player_.muted()) {
16776          level = 0;
16777        } else if (vol < 0.33) {
16778          level = 1;
16779        } else if (vol < 0.67) {
16780          level = 2;
16781        } // TODO improve muted icon classes
16782  
16783  
16784        for (var i = 0; i < 4; i++) {
16785          removeClass(this.el_, "vjs-vol-" + i);
16786        }
16787  
16788        addClass(this.el_, "vjs-vol-" + level);
16789      }
16790      /**
16791       * If `muted` has changed on the player, update the control text
16792       * (`title` attribute on `vjs-mute-control` element and content of
16793       * `vjs-control-text` element).
16794       *
16795       * @private
16796       */
16797      ;
16798  
16799      _proto.updateControlText_ = function updateControlText_() {
16800        var soundOff = this.player_.muted() || this.player_.volume() === 0;
16801        var text = soundOff ? 'Unmute' : 'Mute';
16802  
16803        if (this.controlText() !== text) {
16804          this.controlText(text);
16805        }
16806      };
16807  
16808      return MuteToggle;
16809    }(Button);
16810    /**
16811     * The text that should display over the `MuteToggle`s controls. Added for localization.
16812     *
16813     * @type {string}
16814     * @private
16815     */
16816  
16817  
16818    MuteToggle.prototype.controlText_ = 'Mute';
16819    Component.registerComponent('MuteToggle', MuteToggle);
16820  
16821    /**
16822     * A Component to contain the MuteToggle and VolumeControl so that
16823     * they can work together.
16824     *
16825     * @extends Component
16826     */
16827  
16828    var VolumePanel = /*#__PURE__*/function (_Component) {
16829      inheritsLoose(VolumePanel, _Component);
16830  
16831      /**
16832       * Creates an instance of this class.
16833       *
16834       * @param {Player} player
16835       *        The `Player` that this class should be attached to.
16836       *
16837       * @param {Object} [options={}]
16838       *        The key/value store of player options.
16839       */
16840      function VolumePanel(player, options) {
16841        var _this;
16842  
16843        if (options === void 0) {
16844          options = {};
16845        }
16846  
16847        if (typeof options.inline !== 'undefined') {
16848          options.inline = options.inline;
16849        } else {
16850          options.inline = true;
16851        } // pass the inline option down to the VolumeControl as vertical if
16852        // the VolumeControl is on.
16853  
16854  
16855        if (typeof options.volumeControl === 'undefined' || isPlain(options.volumeControl)) {
16856          options.volumeControl = options.volumeControl || {};
16857          options.volumeControl.vertical = !options.inline;
16858        }
16859  
16860        _this = _Component.call(this, player, options) || this;
16861  
16862        _this.on(player, ['loadstart'], _this.volumePanelState_);
16863  
16864        _this.on(_this.muteToggle, 'keyup', _this.handleKeyPress);
16865  
16866        _this.on(_this.volumeControl, 'keyup', _this.handleVolumeControlKeyUp);
16867  
16868        _this.on('keydown', _this.handleKeyPress);
16869  
16870        _this.on('mouseover', _this.handleMouseOver);
16871  
16872        _this.on('mouseout', _this.handleMouseOut); // while the slider is active (the mouse has been pressed down and
16873        // is dragging) we do not want to hide the VolumeBar
16874  
16875  
16876        _this.on(_this.volumeControl, ['slideractive'], _this.sliderActive_);
16877  
16878        _this.on(_this.volumeControl, ['sliderinactive'], _this.sliderInactive_);
16879  
16880        return _this;
16881      }
16882      /**
16883       * Add vjs-slider-active class to the VolumePanel
16884       *
16885       * @listens VolumeControl#slideractive
16886       * @private
16887       */
16888  
16889  
16890      var _proto = VolumePanel.prototype;
16891  
16892      _proto.sliderActive_ = function sliderActive_() {
16893        this.addClass('vjs-slider-active');
16894      }
16895      /**
16896       * Removes vjs-slider-active class to the VolumePanel
16897       *
16898       * @listens VolumeControl#sliderinactive
16899       * @private
16900       */
16901      ;
16902  
16903      _proto.sliderInactive_ = function sliderInactive_() {
16904        this.removeClass('vjs-slider-active');
16905      }
16906      /**
16907       * Adds vjs-hidden or vjs-mute-toggle-only to the VolumePanel
16908       * depending on MuteToggle and VolumeControl state
16909       *
16910       * @listens Player#loadstart
16911       * @private
16912       */
16913      ;
16914  
16915      _proto.volumePanelState_ = function volumePanelState_() {
16916        // hide volume panel if neither volume control or mute toggle
16917        // are displayed
16918        if (this.volumeControl.hasClass('vjs-hidden') && this.muteToggle.hasClass('vjs-hidden')) {
16919          this.addClass('vjs-hidden');
16920        } // if only mute toggle is visible we don't want
16921        // volume panel expanding when hovered or active
16922  
16923  
16924        if (this.volumeControl.hasClass('vjs-hidden') && !this.muteToggle.hasClass('vjs-hidden')) {
16925          this.addClass('vjs-mute-toggle-only');
16926        }
16927      }
16928      /**
16929       * Create the `Component`'s DOM element
16930       *
16931       * @return {Element}
16932       *         The element that was created.
16933       */
16934      ;
16935  
16936      _proto.createEl = function createEl() {
16937        var orientationClass = 'vjs-volume-panel-horizontal';
16938  
16939        if (!this.options_.inline) {
16940          orientationClass = 'vjs-volume-panel-vertical';
16941        }
16942  
16943        return _Component.prototype.createEl.call(this, 'div', {
16944          className: "vjs-volume-panel vjs-control " + orientationClass
16945        });
16946      }
16947      /**
16948       * Dispose of the `volume-panel` and all child components.
16949       */
16950      ;
16951  
16952      _proto.dispose = function dispose() {
16953        this.handleMouseOut();
16954  
16955        _Component.prototype.dispose.call(this);
16956      }
16957      /**
16958       * Handles `keyup` events on the `VolumeControl`, looking for ESC, which closes
16959       * the volume panel and sets focus on `MuteToggle`.
16960       *
16961       * @param {EventTarget~Event} event
16962       *        The `keyup` event that caused this function to be called.
16963       *
16964       * @listens keyup
16965       */
16966      ;
16967  
16968      _proto.handleVolumeControlKeyUp = function handleVolumeControlKeyUp(event) {
16969        if (keycode.isEventKey(event, 'Esc')) {
16970          this.muteToggle.focus();
16971        }
16972      }
16973      /**
16974       * This gets called when a `VolumePanel` gains hover via a `mouseover` event.
16975       * Turns on listening for `mouseover` event. When they happen it
16976       * calls `this.handleMouseOver`.
16977       *
16978       * @param {EventTarget~Event} event
16979       *        The `mouseover` event that caused this function to be called.
16980       *
16981       * @listens mouseover
16982       */
16983      ;
16984  
16985      _proto.handleMouseOver = function handleMouseOver(event) {
16986        this.addClass('vjs-hover');
16987        on(document, 'keyup', bind(this, this.handleKeyPress));
16988      }
16989      /**
16990       * This gets called when a `VolumePanel` gains hover via a `mouseout` event.
16991       * Turns on listening for `mouseout` event. When they happen it
16992       * calls `this.handleMouseOut`.
16993       *
16994       * @param {EventTarget~Event} event
16995       *        The `mouseout` event that caused this function to be called.
16996       *
16997       * @listens mouseout
16998       */
16999      ;
17000  
17001      _proto.handleMouseOut = function handleMouseOut(event) {
17002        this.removeClass('vjs-hover');
17003        off(document, 'keyup', bind(this, this.handleKeyPress));
17004      }
17005      /**
17006       * Handles `keyup` event on the document or `keydown` event on the `VolumePanel`,
17007       * looking for ESC, which hides the `VolumeControl`.
17008       *
17009       * @param {EventTarget~Event} event
17010       *        The keypress that triggered this event.
17011       *
17012       * @listens keydown | keyup
17013       */
17014      ;
17015  
17016      _proto.handleKeyPress = function handleKeyPress(event) {
17017        if (keycode.isEventKey(event, 'Esc')) {
17018          this.handleMouseOut();
17019        }
17020      };
17021  
17022      return VolumePanel;
17023    }(Component);
17024    /**
17025     * Default options for the `VolumeControl`
17026     *
17027     * @type {Object}
17028     * @private
17029     */
17030  
17031  
17032    VolumePanel.prototype.options_ = {
17033      children: ['muteToggle', 'volumeControl']
17034    };
17035    Component.registerComponent('VolumePanel', VolumePanel);
17036  
17037    /**
17038     * The Menu component is used to build popup menus, including subtitle and
17039     * captions selection menus.
17040     *
17041     * @extends Component
17042     */
17043  
17044    var Menu = /*#__PURE__*/function (_Component) {
17045      inheritsLoose(Menu, _Component);
17046  
17047      /**
17048       * Create an instance of this class.
17049       *
17050       * @param {Player} player
17051       *        the player that this component should attach to
17052       *
17053       * @param {Object} [options]
17054       *        Object of option names and values
17055       *
17056       */
17057      function Menu(player, options) {
17058        var _this;
17059  
17060        _this = _Component.call(this, player, options) || this;
17061  
17062        if (options) {
17063          _this.menuButton_ = options.menuButton;
17064        }
17065  
17066        _this.focusedChild_ = -1;
17067  
17068        _this.on('keydown', _this.handleKeyDown); // All the menu item instances share the same blur handler provided by the menu container.
17069  
17070  
17071        _this.boundHandleBlur_ = bind(assertThisInitialized(_this), _this.handleBlur);
17072        _this.boundHandleTapClick_ = bind(assertThisInitialized(_this), _this.handleTapClick);
17073        return _this;
17074      }
17075      /**
17076       * Add event listeners to the {@link MenuItem}.
17077       *
17078       * @param {Object} component
17079       *        The instance of the `MenuItem` to add listeners to.
17080       *
17081       */
17082  
17083  
17084      var _proto = Menu.prototype;
17085  
17086      _proto.addEventListenerForItem = function addEventListenerForItem(component) {
17087        if (!(component instanceof Component)) {
17088          return;
17089        }
17090  
17091        this.on(component, 'blur', this.boundHandleBlur_);
17092        this.on(component, ['tap', 'click'], this.boundHandleTapClick_);
17093      }
17094      /**
17095       * Remove event listeners from the {@link MenuItem}.
17096       *
17097       * @param {Object} component
17098       *        The instance of the `MenuItem` to remove listeners.
17099       *
17100       */
17101      ;
17102  
17103      _proto.removeEventListenerForItem = function removeEventListenerForItem(component) {
17104        if (!(component instanceof Component)) {
17105          return;
17106        }
17107  
17108        this.off(component, 'blur', this.boundHandleBlur_);
17109        this.off(component, ['tap', 'click'], this.boundHandleTapClick_);
17110      }
17111      /**
17112       * This method will be called indirectly when the component has been added
17113       * before the component adds to the new menu instance by `addItem`.
17114       * In this case, the original menu instance will remove the component
17115       * by calling `removeChild`.
17116       *
17117       * @param {Object} component
17118       *        The instance of the `MenuItem`
17119       */
17120      ;
17121  
17122      _proto.removeChild = function removeChild(component) {
17123        if (typeof component === 'string') {
17124          component = this.getChild(component);
17125        }
17126  
17127        this.removeEventListenerForItem(component);
17128  
17129        _Component.prototype.removeChild.call(this, component);
17130      }
17131      /**
17132       * Add a {@link MenuItem} to the menu.
17133       *
17134       * @param {Object|string} component
17135       *        The name or instance of the `MenuItem` to add.
17136       *
17137       */
17138      ;
17139  
17140      _proto.addItem = function addItem(component) {
17141        var childComponent = this.addChild(component);
17142  
17143        if (childComponent) {
17144          this.addEventListenerForItem(childComponent);
17145        }
17146      }
17147      /**
17148       * Create the `Menu`s DOM element.
17149       *
17150       * @return {Element}
17151       *         the element that was created
17152       */
17153      ;
17154  
17155      _proto.createEl = function createEl$1() {
17156        var contentElType = this.options_.contentElType || 'ul';
17157        this.contentEl_ = createEl(contentElType, {
17158          className: 'vjs-menu-content'
17159        });
17160        this.contentEl_.setAttribute('role', 'menu');
17161  
17162        var el = _Component.prototype.createEl.call(this, 'div', {
17163          append: this.contentEl_,
17164          className: 'vjs-menu'
17165        });
17166  
17167        el.appendChild(this.contentEl_); // Prevent clicks from bubbling up. Needed for Menu Buttons,
17168        // where a click on the parent is significant
17169  
17170        on(el, 'click', function (event) {
17171          event.preventDefault();
17172          event.stopImmediatePropagation();
17173        });
17174        return el;
17175      };
17176  
17177      _proto.dispose = function dispose() {
17178        this.contentEl_ = null;
17179        this.boundHandleBlur_ = null;
17180        this.boundHandleTapClick_ = null;
17181  
17182        _Component.prototype.dispose.call(this);
17183      }
17184      /**
17185       * Called when a `MenuItem` loses focus.
17186       *
17187       * @param {EventTarget~Event} event
17188       *        The `blur` event that caused this function to be called.
17189       *
17190       * @listens blur
17191       */
17192      ;
17193  
17194      _proto.handleBlur = function handleBlur(event) {
17195        var relatedTarget = event.relatedTarget || document.activeElement; // Close menu popup when a user clicks outside the menu
17196  
17197        if (!this.children().some(function (element) {
17198          return element.el() === relatedTarget;
17199        })) {
17200          var btn = this.menuButton_;
17201  
17202          if (btn && btn.buttonPressed_ && relatedTarget !== btn.el().firstChild) {
17203            btn.unpressButton();
17204          }
17205        }
17206      }
17207      /**
17208       * Called when a `MenuItem` gets clicked or tapped.
17209       *
17210       * @param {EventTarget~Event} event
17211       *        The `click` or `tap` event that caused this function to be called.
17212       *
17213       * @listens click,tap
17214       */
17215      ;
17216  
17217      _proto.handleTapClick = function handleTapClick(event) {
17218        // Unpress the associated MenuButton, and move focus back to it
17219        if (this.menuButton_) {
17220          this.menuButton_.unpressButton();
17221          var childComponents = this.children();
17222  
17223          if (!Array.isArray(childComponents)) {
17224            return;
17225          }
17226  
17227          var foundComponent = childComponents.filter(function (component) {
17228            return component.el() === event.target;
17229          })[0];
17230  
17231          if (!foundComponent) {
17232            return;
17233          } // don't focus menu button if item is a caption settings item
17234          // because focus will move elsewhere
17235  
17236  
17237          if (foundComponent.name() !== 'CaptionSettingsMenuItem') {
17238            this.menuButton_.focus();
17239          }
17240        }
17241      }
17242      /**
17243       * Handle a `keydown` event on this menu. This listener is added in the constructor.
17244       *
17245       * @param {EventTarget~Event} event
17246       *        A `keydown` event that happened on the menu.
17247       *
17248       * @listens keydown
17249       */
17250      ;
17251  
17252      _proto.handleKeyDown = function handleKeyDown(event) {
17253        // Left and Down Arrows
17254        if (keycode.isEventKey(event, 'Left') || keycode.isEventKey(event, 'Down')) {
17255          event.preventDefault();
17256          event.stopPropagation();
17257          this.stepForward(); // Up and Right Arrows
17258        } else if (keycode.isEventKey(event, 'Right') || keycode.isEventKey(event, 'Up')) {
17259          event.preventDefault();
17260          event.stopPropagation();
17261          this.stepBack();
17262        }
17263      }
17264      /**
17265       * Move to next (lower) menu item for keyboard users.
17266       */
17267      ;
17268  
17269      _proto.stepForward = function stepForward() {
vendor: 8,057 bytes, lines 17270-17557
17270        var stepChild = 0;
17271  
17272        if (this.focusedChild_ !== undefined) {
17273          stepChild = this.focusedChild_ + 1;
17274        }
17275  
17276        this.focus(stepChild);
17277      }
17278      /**
17279       * Move to previous (higher) menu item for keyboard users.
17280       */
17281      ;
17282  
17283      _proto.stepBack = function stepBack() {
17284        var stepChild = 0;
17285  
17286        if (this.focusedChild_ !== undefined) {
17287          stepChild = this.focusedChild_ - 1;
17288        }
17289  
17290        this.focus(stepChild);
17291      }
17292      /**
17293       * Set focus on a {@link MenuItem} in the `Menu`.
17294       *
17295       * @param {Object|string} [item=0]
17296       *        Index of child item set focus on.
17297       */
17298      ;
17299  
17300      _proto.focus = function focus(item) {
17301        if (item === void 0) {
17302          item = 0;
17303        }
17304  
17305        var children = this.children().slice();
17306        var haveTitle = children.length && children[0].className && /vjs-menu-title/.test(children[0].className);
17307  
17308        if (haveTitle) {
17309          children.shift();
17310        }
17311  
17312        if (children.length > 0) {
17313          if (item < 0) {
17314            item = 0;
17315          } else if (item >= children.length) {
17316            item = children.length - 1;
17317          }
17318  
17319          this.focusedChild_ = item;
17320          children[item].el_.focus();
17321        }
17322      };
17323  
17324      return Menu;
17325    }(Component);
17326  
17327    Component.registerComponent('Menu', Menu);
17328  
17329    /**
17330     * A `MenuButton` class for any popup {@link Menu}.
17331     *
17332     * @extends Component
17333     */
17334  
17335    var MenuButton = /*#__PURE__*/function (_Component) {
17336      inheritsLoose(MenuButton, _Component);
17337  
17338      /**
17339       * Creates an instance of this class.
17340       *
17341       * @param {Player} player
17342       *        The `Player` that this class should be attached to.
17343       *
17344       * @param {Object} [options={}]
17345       *        The key/value store of player options.
17346       */
17347      function MenuButton(player, options) {
17348        var _this;
17349  
17350        if (options === void 0) {
17351          options = {};
17352        }
17353  
17354        _this = _Component.call(this, player, options) || this;
17355        _this.menuButton_ = new Button(player, options);
17356  
17357        _this.menuButton_.controlText(_this.controlText_);
17358  
17359        _this.menuButton_.el_.setAttribute('aria-haspopup', 'true'); // Add buildCSSClass values to the button, not the wrapper
17360  
17361  
17362        var buttonClass = Button.prototype.buildCSSClass();
17363        _this.menuButton_.el_.className = _this.buildCSSClass() + ' ' + buttonClass;
17364  
17365        _this.menuButton_.removeClass('vjs-control');
17366  
17367        _this.addChild(_this.menuButton_);
17368  
17369        _this.update();
17370  
17371        _this.enabled_ = true;
17372  
17373        _this.on(_this.menuButton_, 'tap', _this.handleClick);
17374  
17375        _this.on(_this.menuButton_, 'click', _this.handleClick);
17376  
17377        _this.on(_this.menuButton_, 'keydown', _this.handleKeyDown);
17378  
17379        _this.on(_this.menuButton_, 'mouseenter', function () {
17380          _this.addClass('vjs-hover');
17381  
17382          _this.menu.show();
17383  
17384          on(document, 'keyup', bind(assertThisInitialized(_this), _this.handleMenuKeyUp));
17385        });
17386  
17387        _this.on('mouseleave', _this.handleMouseLeave);
17388  
17389        _this.on('keydown', _this.handleSubmenuKeyDown);
17390  
17391        return _this;
17392      }
17393      /**
17394       * Update the menu based on the current state of its items.
17395       */
17396  
17397  
17398      var _proto = MenuButton.prototype;
17399  
17400      _proto.update = function update() {
17401        var menu = this.createMenu();
17402  
17403        if (this.menu) {
17404          this.menu.dispose();
17405          this.removeChild(this.menu);
17406        }
17407  
17408        this.menu = menu;
17409        this.addChild(menu);
17410        /**
17411         * Track the state of the menu button
17412         *
17413         * @type {Boolean}
17414         * @private
17415         */
17416  
17417        this.buttonPressed_ = false;
17418        this.menuButton_.el_.setAttribute('aria-expanded', 'false');
17419  
17420        if (this.items && this.items.length <= this.hideThreshold_) {
17421          this.hide();
17422        } else {
17423          this.show();
17424        }
17425      }
17426      /**
17427       * Create the menu and add all items to it.
17428       *
17429       * @return {Menu}
17430       *         The constructed menu
17431       */
17432      ;
17433  
17434      _proto.createMenu = function createMenu() {
17435        var menu = new Menu(this.player_, {
17436          menuButton: this
17437        });
17438        /**
17439         * Hide the menu if the number of items is less than or equal to this threshold. This defaults
17440         * to 0 and whenever we add items which can be hidden to the menu we'll increment it. We list
17441         * it here because every time we run `createMenu` we need to reset the value.
17442         *
17443         * @protected
17444         * @type {Number}
17445         */
17446  
17447        this.hideThreshold_ = 0; // Add a title list item to the top
17448  
17449        if (this.options_.title) {
17450          var titleEl = createEl('li', {
17451            className: 'vjs-menu-title',
17452            innerHTML: toTitleCase(this.options_.title),
17453            tabIndex: -1
17454          });
17455          this.hideThreshold_ += 1;
17456          var titleComponent = new Component(this.player_, {
17457            el: titleEl
17458          });
17459          menu.addItem(titleComponent);
17460        }
17461  
17462        this.items = this.createItems();
17463  
17464        if (this.items) {
17465          // Add menu items to the menu
17466          for (var i = 0; i < this.items.length; i++) {
17467            menu.addItem(this.items[i]);
17468          }
17469        }
17470  
17471        return menu;
17472      }
17473      /**
17474       * Create the list of menu items. Specific to each subclass.
17475       *
17476       * @abstract
17477       */
17478      ;
17479  
17480      _proto.createItems = function createItems() {}
17481      /**
17482       * Create the `MenuButtons`s DOM element.
17483       *
17484       * @return {Element}
17485       *         The element that gets created.
17486       */
17487      ;
17488  
17489      _proto.createEl = function createEl() {
17490        return _Component.prototype.createEl.call(this, 'div', {
17491          className: this.buildWrapperCSSClass()
17492        }, {});
17493      }
17494      /**
17495       * Allow sub components to stack CSS class names for the wrapper element
17496       *
17497       * @return {string}
17498       *         The constructed wrapper DOM `className`
17499       */
17500      ;
17501  
17502      _proto.buildWrapperCSSClass = function buildWrapperCSSClass() {
17503        var menuButtonClass = 'vjs-menu-button'; // If the inline option is passed, we want to use different styles altogether.
17504  
17505        if (this.options_.inline === true) {
17506          menuButtonClass += '-inline';
17507        } else {
17508          menuButtonClass += '-popup';
17509        } // TODO: Fix the CSS so that this isn't necessary
17510  
17511  
17512        var buttonClass = Button.prototype.buildCSSClass();
17513        return "vjs-menu-button " + menuButtonClass + " " + buttonClass + " " + _Component.prototype.buildCSSClass.call(this);
17514      }
17515      /**
17516       * Builds the default DOM `className`.
17517       *
17518       * @return {string}
17519       *         The DOM `className` for this object.
17520       */
17521      ;
17522  
17523      _proto.buildCSSClass = function buildCSSClass() {
17524        var menuButtonClass = 'vjs-menu-button'; // If the inline option is passed, we want to use different styles altogether.
17525  
17526        if (this.options_.inline === true) {
17527          menuButtonClass += '-inline';
17528        } else {
17529          menuButtonClass += '-popup';
17530        }
17531  
17532        return "vjs-menu-button " + menuButtonClass + " " + _Component.prototype.buildCSSClass.call(this);
17533      }
17534      /**
17535       * Get or set the localized control text that will be used for accessibility.
17536       *
17537       * > NOTE: This will come from the internal `menuButton_` element.
17538       *
17539       * @param {string} [text]
17540       *        Control text for element.
17541       *
17542       * @param {Element} [el=this.menuButton_.el()]
17543       *        Element to set the title on.
17544       *
17545       * @return {string}
17546       *         - The control text when getting
17547       */
17548      ;
17549  
17550      _proto.controlText = function controlText(text, el) {
17551        if (el === void 0) {
17552          el = this.menuButton_.el();
17553        }
17554  
17555        return this.menuButton_.controlText(text, el);
17556      }
17557      /**
17558       * Dispose of the `menu-button` and all child components.
17559       */
17560      ;
17561  
17562      _proto.dispose = function dispose() {
17563        this.handleMouseLeave();
17564  
17565        _Component.prototype.dispose.call(this);
17566      }
17567      /**
17568       * Handle a click on a `MenuButton`.
17569       * See {@link ClickableComponent#handleClick} for instances where this is called.
17570       *
17571       * @param {EventTarget~Event} event
17572       *        The `keydown`, `tap`, or `click` event that caused this function to be
17573       *        called.
17574       *
17575       * @listens tap
17576       * @listens click
17577       */
17578      ;
17579  
17580      _proto.handleClick = function handleClick(event) {
17581        if (this.buttonPressed_) {
17582          this.unpressButton();
17583        } else {
17584          this.pressButton();
17585        }
17586      }
17587      /**
17588       * Handle `mouseleave` for `MenuButton`.
17589       *
17590       * @param {EventTarget~Event} event
17591       *        The `mouseleave` event that caused this function to be called.
17592       *
17593       * @listens mouseleave
17594       */
17595      ;
17596  
17597      _proto.handleMouseLeave = function handleMouseLeave(event) {
17598        this.removeClass('vjs-hover');
17599        off(document, 'keyup', bind(this, this.handleMenuKeyUp));
17600      }
17601      /**
17602       * Set the focus to the actual button, not to this element
17603       */
17604      ;
17605  
17606      _proto.focus = function focus() {
17607        this.menuButton_.focus();
17608      }
17609      /**
17610       * Remove the focus from the actual button, not this element
17611       */
17612      ;
17613  
17614      _proto.blur = function blur() {
17615        this.menuButton_.blur();
17616      }
17617      /**
17618       * Handle tab, escape, down arrow, and up arrow keys for `MenuButton`. See
17619       * {@link ClickableComponent#handleKeyDown} for instances where this is called.
17620       *
17621       * @param {EventTarget~Event} event
17622       *        The `keydown` event that caused this function to be called.
17623       *
17624       * @listens keydown
17625       */
17626      ;
17627  
17628      _proto.handleKeyDown = function handleKeyDown(event) {
17629        // Escape or Tab unpress the 'button'
17630        if (keycode.isEventKey(event, 'Esc') || keycode.isEventKey(event, 'Tab')) {
17631          if (this.buttonPressed_) {
17632            this.unpressButton();
17633          } // Don't preventDefault for Tab key - we still want to lose focus
17634  
17635  
17636          if (!keycode.isEventKey(event, 'Tab')) {
17637            event.preventDefault(); // Set focus back to the menu button's button
17638  
17639            this.menuButton_.focus();
17640          } // Up Arrow or Down Arrow also 'press' the button to open the menu
17641  
17642        } else if (keycode.isEventKey(event, 'Up') || keycode.isEventKey(event, 'Down')) {
17643          if (!this.buttonPressed_) {
17644            event.preventDefault();
17645            this.pressButton();
17646          }
17647        }
17648      }
17649      /**
17650       * Handle a `keyup` event on a `MenuButton`. The listener for this is added in
17651       * the constructor.
17652       *
17653       * @param {EventTarget~Event} event
17654       *        Key press event
17655       *
17656       * @listens keyup
17657       */
17658      ;
17659  
17660      _proto.handleMenuKeyUp = function handleMenuKeyUp(event) {
17661        // Escape hides popup menu
17662        if (keycode.isEventKey(event, 'Esc') || keycode.isEventKey(event, 'Tab')) {
17663          this.removeClass('vjs-hover');
17664        }
17665      }
17666      /**
17667       * This method name now delegates to `handleSubmenuKeyDown`. This means
17668       * anyone calling `handleSubmenuKeyPress` will not see their method calls
17669       * stop working.
17670       *
17671       * @param {EventTarget~Event} event
17672       *        The event that caused this function to be called.
17673       */
17674      ;
17675  
17676      _proto.handleSubmenuKeyPress = function handleSubmenuKeyPress(event) {
17677        this.handleSubmenuKeyDown(event);
17678      }
17679      /**
17680       * Handle a `keydown` event on a sub-menu. The listener for this is added in
17681       * the constructor.
17682       *
17683       * @param {EventTarget~Event} event
17684       *        Key press event
17685       *
17686       * @listens keydown
17687       */
17688      ;
17689  
17690      _proto.handleSubmenuKeyDown = function handleSubmenuKeyDown(event) {
17691        // Escape or Tab unpress the 'button'
17692        if (keycode.isEventKey(event, 'Esc') || keycode.isEventKey(event, 'Tab')) {
17693          if (this.buttonPressed_) {
17694            this.unpressButton();
17695          } // Don't preventDefault for Tab key - we still want to lose focus
17696  
17697  
17698          if (!keycode.isEventKey(event, 'Tab')) {
17699            event.preventDefault(); // Set focus back to the menu button's button
17700  
17701            this.menuButton_.focus();
17702          }
17703        }
17704      }
17705      /**
17706       * Put the current `MenuButton` into a pressed state.
17707       */
17708      ;
17709  
17710      _proto.pressButton = function pressButton() {
17711        if (this.enabled_) {
17712          this.buttonPressed_ = true;
17713          this.menu.show();
17714          this.menu.lockShowing();
17715          this.menuButton_.el_.setAttribute('aria-expanded', 'true'); // set the focus into the submenu, except on iOS where it is resulting in
17716          // undesired scrolling behavior when the player is in an iframe
17717  
17718          if (IS_IOS && isInFrame()) {
17719            // Return early so that the menu isn't focused
17720            return;
17721          }
17722  
17723          this.menu.focus();
17724        }
17725      }
17726      /**
17727       * Take the current `MenuButton` out of a pressed state.
17728       */
17729      ;
17730  
17731      _proto.unpressButton = function unpressButton() {
17732        if (this.enabled_) {
17733          this.buttonPressed_ = false;
17734          this.menu.unlockShowing();
17735          this.menu.hide();
17736          this.menuButton_.el_.setAttribute('aria-expanded', 'false');
17737        }
17738      }
17739      /**
17740       * Disable the `MenuButton`. Don't allow it to be clicked.
17741       */
17742      ;
17743  
17744      _proto.disable = function disable() {
17745        this.unpressButton();
17746        this.enabled_ = false;
17747        this.addClass('vjs-disabled');
17748        this.menuButton_.disable();
17749      }
17750      /**
17751       * Enable the `MenuButton`. Allow it to be clicked.
17752       */
17753      ;
17754  
17755      _proto.enable = function enable() {
17756        this.enabled_ = true;
17757        this.removeClass('vjs-disabled');
17758        this.menuButton_.enable();
17759      };
17760  
17761      return MenuButton;
17762    }(Component);
17763  
17764    Component.registerComponent('MenuButton', MenuButton);
17765  
17766    /**
17767     * The base class for buttons that toggle specific  track types (e.g. subtitles).
17768     *
17769     * @extends MenuButton
17770     */
17771  
17772    var TrackButton = /*#__PURE__*/function (_MenuButton) {
17773      inheritsLoose(TrackButton, _MenuButton);
17774  
17775      /**
17776       * Creates an instance of this class.
17777       *
17778       * @param {Player} player
17779       *        The `Player` that this class should be attached to.
17780       *
17781       * @param {Object} [options]
17782       *        The key/value store of player options.
17783       */
17784      function TrackButton(player, options) {
17785        var _this;
17786  
17787        var tracks = options.tracks;
17788        _this = _MenuButton.call(this, player, options) || this;
17789  
17790        if (_this.items.length <= 1) {
17791          _this.hide();
17792        }
17793  
17794        if (!tracks) {
17795          return assertThisInitialized(_this);
17796        }
17797  
17798        var updateHandler = bind(assertThisInitialized(_this), _this.update);
17799        tracks.addEventListener('removetrack', updateHandler);
17800        tracks.addEventListener('addtrack', updateHandler);
17801  
17802        _this.player_.on('ready', updateHandler);
17803  
17804        _this.player_.on('dispose', function () {
17805          tracks.removeEventListener('removetrack', updateHandler);
17806          tracks.removeEventListener('addtrack', updateHandler);
17807        });
17808  
17809        return _this;
17810      }
17811  
17812      return TrackButton;
17813    }(MenuButton);
17814  
17815    Component.registerComponent('TrackButton', TrackButton);
17816  
17817    /**
17818     * @file menu-keys.js
17819     */
17820  
17821    /**
17822      * All keys used for operation of a menu (`MenuButton`, `Menu`, and `MenuItem`)
17823      * Note that 'Enter' and 'Space' are not included here (otherwise they would
17824      * prevent the `MenuButton` and `MenuItem` from being keyboard-clickable)
17825      * @typedef MenuKeys
17826      * @array
17827      */
17828    var MenuKeys = ['Tab', 'Esc', 'Up', 'Down', 'Right', 'Left'];
17829  
17830    /**
17831     * The component for a menu item. `<li>`
17832     *
17833     * @extends ClickableComponent
17834     */
17835  
17836    var MenuItem = /*#__PURE__*/function (_ClickableComponent) {
17837      inheritsLoose(MenuItem, _ClickableComponent);
17838  
17839      /**
17840       * Creates an instance of the this class.
17841       *
17842       * @param {Player} player
17843       *        The `Player` that this class should be attached to.
17844       *
17845       * @param {Object} [options={}]
17846       *        The key/value store of player options.
17847       *
17848       */
17849      function MenuItem(player, options) {
17850        var _this;
17851  
17852        _this = _ClickableComponent.call(this, player, options) || this;
17853        _this.selectable = options.selectable;
17854        _this.isSelected_ = options.selected || false;
17855        _this.multiSelectable = options.multiSelectable;
17856  
17857        _this.selected(_this.isSelected_);
17858  
17859        if (_this.selectable) {
17860          if (_this.multiSelectable) {
17861            _this.el_.setAttribute('role', 'menuitemcheckbox');
17862          } else {
17863            _this.el_.setAttribute('role', 'menuitemradio');
17864          }
17865        } else {
17866          _this.el_.setAttribute('role', 'menuitem');
17867        }
17868  
17869        return _this;
17870      }
17871      /**
17872       * Create the `MenuItem's DOM element
17873       *
17874       * @param {string} [type=li]
17875       *        Element's node type, not actually used, always set to `li`.
17876       *
17877       * @param {Object} [props={}]
17878       *        An object of properties that should be set on the element
17879       *
17880       * @param {Object} [attrs={}]
17881       *        An object of attributes that should be set on the element
17882       *
17883       * @return {Element}
17884       *         The element that gets created.
17885       */
17886  
17887  
17888      var _proto = MenuItem.prototype;
17889  
17890      _proto.createEl = function createEl(type, props, attrs) {
17891        // The control is textual, not just an icon
17892        this.nonIconControl = true;
17893        return _ClickableComponent.prototype.createEl.call(this, 'li', assign({
17894          className: 'vjs-menu-item',
17895          innerHTML: "<span class=\"vjs-menu-item-text\">" + this.localize(this.options_.label) + "</span>",
17896          tabIndex: -1
17897        }, props), attrs);
17898      }
17899      /**
17900       * Ignore keys which are used by the menu, but pass any other ones up. See
17901       * {@link ClickableComponent#handleKeyDown} for instances where this is called.
17902       *
17903       * @param {EventTarget~Event} event
17904       *        The `keydown` event that caused this function to be called.
17905       *
17906       * @listens keydown
17907       */
17908      ;
17909  
17910      _proto.handleKeyDown = function handleKeyDown(event) {
17911        if (!MenuKeys.some(function (key) {
17912          return keycode.isEventKey(event, key);
17913        })) {
17914          // Pass keydown handling up for unused keys
17915          _ClickableComponent.prototype.handleKeyDown.call(this, event);
17916        }
17917      }
17918      /**
17919       * Any click on a `MenuItem` puts it into the selected state.
17920       * See {@link ClickableComponent#handleClick} for instances where this is called.
17921       *
17922       * @param {EventTarget~Event} event
17923       *        The `keydown`, `tap`, or `click` event that caused this function to be
17924       *        called.
17925       *
17926       * @listens tap
17927       * @listens click
17928       */
17929      ;
17930  
17931      _proto.handleClick = function handleClick(event) {
17932        this.selected(true);
17933      }
17934      /**
17935       * Set the state for this menu item as selected or not.
17936       *
17937       * @param {boolean} selected
17938       *        if the menu item is selected or not
17939       */
17940      ;
17941  
17942      _proto.selected = function selected(_selected) {
17943        if (this.selectable) {
17944          if (_selected) {
17945            this.addClass('vjs-selected');
17946            this.el_.setAttribute('aria-checked', 'true'); // aria-checked isn't fully supported by browsers/screen readers,
17947            // so indicate selected state to screen reader in the control text.
17948  
17949            this.controlText(', selected');
17950            this.isSelected_ = true;
17951          } else {
17952            this.removeClass('vjs-selected');
17953            this.el_.setAttribute('aria-checked', 'false'); // Indicate un-selected state to screen reader
17954  
17955            this.controlText('');
17956            this.isSelected_ = false;
17957          }
17958        }
17959      };
17960  
17961      return MenuItem;
17962    }(ClickableComponent);
17963  
17964    Component.registerComponent('MenuItem', MenuItem);
17965  
17966    /**
17967     * The specific menu item type for selecting a language within a text track kind
17968     *
17969     * @extends MenuItem
17970     */
17971  
17972    var TextTrackMenuItem = /*#__PURE__*/function (_MenuItem) {
17973      inheritsLoose(TextTrackMenuItem, _MenuItem);
17974  
17975      /**
17976       * Creates an instance of this class.
17977       *
17978       * @param {Player} player
17979       *        The `Player` that this class should be attached to.
17980       *
17981       * @param {Object} [options]
17982       *        The key/value store of player options.
17983       */
17984      function TextTrackMenuItem(player, options) {
17985        var _this;
17986  
17987        var track = options.track;
17988        var tracks = player.textTracks(); // Modify options for parent MenuItem class's init.
17989  
17990        options.label = track.label || track.language || 'Unknown';
17991        options.selected = track.mode === 'showing';
17992        _this = _MenuItem.call(this, player, options) || this;
17993        _this.track = track; // Determine the relevant kind(s) of tracks for this component and filter
17994        // out empty kinds.
17995  
17996        _this.kinds = (options.kinds || [options.kind || _this.track.kind]).filter(Boolean);
17997  
17998        var changeHandler = function changeHandler() {
17999          for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) {
18000            args[_key] = arguments[_key];
18001          }
18002  
18003          _this.handleTracksChange.apply(assertThisInitialized(_this), args);
18004        };
18005  
18006        var selectedLanguageChangeHandler = function selectedLanguageChangeHandler() {
18007          for (var _len2 = arguments.length, args = new Array(_len2), _key2 = 0; _key2 < _len2; _key2++) {
18008            args[_key2] = arguments[_key2];
18009          }
18010  
18011          _this.handleSelectedLanguageChange.apply(assertThisInitialized(_this), args);
18012        };
18013  
18014        player.on(['loadstart', 'texttrackchange'], changeHandler);
18015        tracks.addEventListener('change', changeHandler);
18016        tracks.addEventListener('selectedlanguagechange', selectedLanguageChangeHandler);
18017  
18018        _this.on('dispose', function () {
18019          player.off(['loadstart', 'texttrackchange'], changeHandler);
18020          tracks.removeEventListener('change', changeHandler);
18021          tracks.removeEventListener('selectedlanguagechange', selectedLanguageChangeHandler);
18022        }); // iOS7 doesn't dispatch change events to TextTrackLists when an
18023        // associated track's mode changes. Without something like
18024        // Object.observe() (also not present on iOS7), it's not
18025        // possible to detect changes to the mode attribute and polyfill
18026        // the change event. As a poor substitute, we manually dispatch
18027        // change events whenever the controls modify the mode.
18028  
18029  
18030        if (tracks.onchange === undefined) {
18031          var event;
18032  
18033          _this.on(['tap', 'click'], function () {
18034            if (typeof window$3.Event !== 'object') {
18035              // Android 2.3 throws an Illegal Constructor error for window.Event
18036              try {
18037                event = new window$3.Event('change');
18038              } catch (err) {// continue regardless of error
18039              }
18040            }
18041  
18042            if (!event) {
18043              event = document.createEvent('Event');
18044              event.initEvent('change', true, true);
18045            }
18046  
18047            tracks.dispatchEvent(event);
18048          });
18049        } // set the default state based on current tracks
18050  
18051  
vendor: 9,010 bytes, lines 18052-18333
18052        _this.handleTracksChange();
18053  
18054        return _this;
18055      }
18056      /**
18057       * This gets called when an `TextTrackMenuItem` is "clicked". See
18058       * {@link ClickableComponent} for more detailed information on what a click can be.
18059       *
18060       * @param {EventTarget~Event} event
18061       *        The `keydown`, `tap`, or `click` event that caused this function to be
18062       *        called.
18063       *
18064       * @listens tap
18065       * @listens click
18066       */
18067  
18068  
18069      var _proto = TextTrackMenuItem.prototype;
18070  
18071      _proto.handleClick = function handleClick(event) {
18072        var referenceTrack = this.track;
18073        var tracks = this.player_.textTracks();
18074  
18075        _MenuItem.prototype.handleClick.call(this, event);
18076  
18077        if (!tracks) {
18078          return;
18079        }
18080  
18081        for (var i = 0; i < tracks.length; i++) {
18082          var track = tracks[i]; // If the track from the text tracks list is not of the right kind,
18083          // skip it. We do not want to affect tracks of incompatible kind(s).
18084  
18085          if (this.kinds.indexOf(track.kind) === -1) {
18086            continue;
18087          } // If this text track is the component's track and it is not showing,
18088          // set it to showing.
18089  
18090  
18091          if (track === referenceTrack) {
18092            if (track.mode !== 'showing') {
18093              track.mode = 'showing';
18094            } // If this text track is not the component's track and it is not
18095            // disabled, set it to disabled.
18096  
18097          } else if (track.mode !== 'disabled') {
18098            track.mode = 'disabled';
18099          }
18100        }
18101      }
18102      /**
18103       * Handle text track list change
18104       *
18105       * @param {EventTarget~Event} event
18106       *        The `change` event that caused this function to be called.
18107       *
18108       * @listens TextTrackList#change
18109       */
18110      ;
18111  
18112      _proto.handleTracksChange = function handleTracksChange(event) {
18113        var shouldBeSelected = this.track.mode === 'showing'; // Prevent redundant selected() calls because they may cause
18114        // screen readers to read the appended control text unnecessarily
18115  
18116        if (shouldBeSelected !== this.isSelected_) {
18117          this.selected(shouldBeSelected);
18118        }
18119      };
18120  
18121      _proto.handleSelectedLanguageChange = function handleSelectedLanguageChange(event) {
18122        if (this.track.mode === 'showing') {
18123          var selectedLanguage = this.player_.cache_.selectedLanguage; // Don't replace the kind of track across the same language
18124  
18125          if (selectedLanguage && selectedLanguage.enabled && selectedLanguage.language === this.track.language && selectedLanguage.kind !== this.track.kind) {
18126            return;
18127          }
18128  
18129          this.player_.cache_.selectedLanguage = {
18130            enabled: true,
18131            language: this.track.language,
18132            kind: this.track.kind
18133          };
18134        }
18135      };
18136  
18137      _proto.dispose = function dispose() {
18138        // remove reference to track object on dispose
18139        this.track = null;
18140  
18141        _MenuItem.prototype.dispose.call(this);
18142      };
18143  
18144      return TextTrackMenuItem;
18145    }(MenuItem);
18146  
18147    Component.registerComponent('TextTrackMenuItem', TextTrackMenuItem);
18148  
18149    /**
18150     * A special menu item for turning of a specific type of text track
18151     *
18152     * @extends TextTrackMenuItem
18153     */
18154  
18155    var OffTextTrackMenuItem = /*#__PURE__*/function (_TextTrackMenuItem) {
18156      inheritsLoose(OffTextTrackMenuItem, _TextTrackMenuItem);
18157  
18158      /**
18159       * Creates an instance of this class.
18160       *
18161       * @param {Player} player
18162       *        The `Player` that this class should be attached to.
18163       *
18164       * @param {Object} [options]
18165       *        The key/value store of player options.
18166       */
18167      function OffTextTrackMenuItem(player, options) {
18168        // Create pseudo track info
18169        // Requires options['kind']
18170        options.track = {
18171          player: player,
18172          // it is no longer necessary to store `kind` or `kinds` on the track itself
18173          // since they are now stored in the `kinds` property of all instances of
18174          // TextTrackMenuItem, but this will remain for backwards compatibility
18175          kind: options.kind,
18176          kinds: options.kinds,
18177          "default": false,
18178          mode: 'disabled'
18179        };
18180  
18181        if (!options.kinds) {
18182          options.kinds = [options.kind];
18183        }
18184  
18185        if (options.label) {
18186          options.track.label = options.label;
18187        } else {
18188          options.track.label = options.kinds.join(' and ') + ' off';
18189        } // MenuItem is selectable
18190  
18191  
18192        options.selectable = true; // MenuItem is NOT multiSelectable (i.e. only one can be marked "selected" at a time)
18193  
18194        options.multiSelectable = false;
18195        return _TextTrackMenuItem.call(this, player, options) || this;
18196      }
18197      /**
18198       * Handle text track change
18199       *
18200       * @param {EventTarget~Event} event
18201       *        The event that caused this function to run
18202       */
18203  
18204  
18205      var _proto = OffTextTrackMenuItem.prototype;
18206  
18207      _proto.handleTracksChange = function handleTracksChange(event) {
18208        var tracks = this.player().textTracks();
18209        var shouldBeSelected = true;
18210  
18211        for (var i = 0, l = tracks.length; i < l; i++) {
18212          var track = tracks[i];
18213  
18214          if (this.options_.kinds.indexOf(track.kind) > -1 && track.mode === 'showing') {
18215            shouldBeSelected = false;
18216            break;
18217          }
18218        } // Prevent redundant selected() calls because they may cause
18219        // screen readers to read the appended control text unnecessarily
18220  
18221  
18222        if (shouldBeSelected !== this.isSelected_) {
18223          this.selected(shouldBeSelected);
18224        }
18225      };
18226  
18227      _proto.handleSelectedLanguageChange = function handleSelectedLanguageChange(event) {
18228        var tracks = this.player().textTracks();
18229        var allHidden = true;
18230  
18231        for (var i = 0, l = tracks.length; i < l; i++) {
18232          var track = tracks[i];
18233  
18234          if (['captions', 'descriptions', 'subtitles'].indexOf(track.kind) > -1 && track.mode === 'showing') {
18235            allHidden = false;
18236            break;
18237          }
18238        }
18239  
18240        if (allHidden) {
18241          this.player_.cache_.selectedLanguage = {
18242            enabled: false
18243          };
18244        }
18245      };
18246  
18247      return OffTextTrackMenuItem;
18248    }(TextTrackMenuItem);
18249  
18250    Component.registerComponent('OffTextTrackMenuItem', OffTextTrackMenuItem);
18251  
18252    /**
18253     * The base class for buttons that toggle specific text track types (e.g. subtitles)
18254     *
18255     * @extends MenuButton
18256     */
18257  
18258    var TextTrackButton = /*#__PURE__*/function (_TrackButton) {
18259      inheritsLoose(TextTrackButton, _TrackButton);
18260  
18261      /**
18262       * Creates an instance of this class.
18263       *
18264       * @param {Player} player
18265       *        The `Player` that this class should be attached to.
18266       *
18267       * @param {Object} [options={}]
18268       *        The key/value store of player options.
18269       */
18270      function TextTrackButton(player, options) {
18271        if (options === void 0) {
18272          options = {};
18273        }
18274  
18275        options.tracks = player.textTracks();
18276        return _TrackButton.call(this, player, options) || this;
18277      }
18278      /**
18279       * Create a menu item for each text track
18280       *
18281       * @param {TextTrackMenuItem[]} [items=[]]
18282       *        Existing array of items to use during creation
18283       *
18284       * @return {TextTrackMenuItem[]}
18285       *         Array of menu items that were created
18286       */
18287  
18288  
18289      var _proto = TextTrackButton.prototype;
18290  
18291      _proto.createItems = function createItems(items, TrackMenuItem) {
18292        if (items === void 0) {
18293          items = [];
18294        }
18295  
18296        if (TrackMenuItem === void 0) {
18297          TrackMenuItem = TextTrackMenuItem;
18298        }
18299  
18300        // Label is an override for the [track] off label
18301        // USed to localise captions/subtitles
18302        var label;
18303  
18304        if (this.label_) {
18305          label = this.label_ + " off";
18306        } // Add an OFF menu item to turn all tracks off
18307  
18308  
18309        items.push(new OffTextTrackMenuItem(this.player_, {
18310          kinds: this.kinds_,
18311          kind: this.kind_,
18312          label: label
18313        }));
18314        this.hideThreshold_ += 1;
18315        var tracks = this.player_.textTracks();
18316  
18317        if (!Array.isArray(this.kinds_)) {
18318          this.kinds_ = [this.kind_];
18319        }
18320  
18321        for (var i = 0; i < tracks.length; i++) {
18322          var track = tracks[i]; // only add tracks that are of an appropriate kind and have a label
18323  
18324          if (this.kinds_.indexOf(track.kind) > -1) {
18325            var item = new TrackMenuItem(this.player_, {
18326              track: track,
18327              kinds: this.kinds_,
18328              kind: this.kind_,
18329              // MenuItem is selectable
18330              selectable: true,
18331              // MenuItem is NOT multiSelectable (i.e. only one can be marked "selected" at a time)
18332              multiSelectable: false
18333            });
18334            item.addClass("vjs-" + track.kind + "-menu-item");
18335            items.push(item);
18336          }
18337        }
18338  
18339        return items;
18340      };
18341  
18342      return TextTrackButton;
18343    }(TrackButton);
18344  
18345    Component.registerComponent('TextTrackButton', TextTrackButton);
18346  
18347    /**
18348     * The chapter track menu item
18349     *
18350     * @extends MenuItem
18351     */
18352  
18353    var ChaptersTrackMenuItem = /*#__PURE__*/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 = /*#__PURE__*/function (_TextTrackButton) {
18434      inheritsLoose(ChaptersButton, _TextTrackButton);
18435  
18436      /**
18437       * Creates an instance of this class.
18438       *
18439       * @param {Player} player
18440       *        The `Player` that this class should be attached to.
18441       *
18442       * @param {Object} [options]
18443       *        The key/value store of player options.
18444       *
18445       * @param {Component~ReadyCallback} [ready]
18446       *        The function to call when this function is ready.
18447       */
18448      function ChaptersButton(player, options, ready) {
18449        return _TextTrackButton.call(this, player, options, ready) || this;
18450      }
18451      /**
18452       * Builds the default DOM `className`.
18453       *
18454       * @return {string}
18455       *         The DOM `className` for this object.
18456       */
18457  
18458  
18459      var _proto = ChaptersButton.prototype;
18460  
18461      _proto.buildCSSClass = function buildCSSClass() {
18462        return "vjs-chapters-button " + _TextTrackButton.prototype.buildCSSClass.call(this);
18463      };
18464  
18465      _proto.buildWrapperCSSClass = function buildWrapperCSSClass() {
18466        return "vjs-chapters-button " + _TextTrackButton.prototype.buildWrapperCSSClass.call(this);
18467      }
18468      /**
18469       * Update the menu based on the current state of its items.
18470       *
18471       * @param {EventTarget~Event} [event]
18472       *        An event that triggered this function to run.
18473       *
18474       * @listens TextTrackList#addtrack
18475       * @listens TextTrackList#removetrack
18476       * @listens TextTrackList#change
18477       */
18478      ;
18479  
18480      _proto.update = function update(event) {
18481        if (!this.track_ || event && (event.type === 'addtrack' || event.type === 'removetrack')) {
vendor: 6,005 bytes, lines 18482-18690
18482          this.setTrack(this.findChaptersTrack());
18483        }
18484  
18485        _TextTrackButton.prototype.update.call(this);
18486      }
18487      /**
18488       * Set the currently selected track for the chapters button.
18489       *
18490       * @param {TextTrack} track
18491       *        The new track to select. Nothing will change if this is the currently selected
18492       *        track.
18493       */
18494      ;
18495  
18496      _proto.setTrack = function setTrack(track) {
18497        if (this.track_ === track) {
18498          return;
18499        }
18500  
18501        if (!this.updateHandler_) {
18502          this.updateHandler_ = this.update.bind(this);
18503        } // here this.track_ refers to the old track instance
18504  
18505  
18506        if (this.track_) {
18507          var remoteTextTrackEl = this.player_.remoteTextTrackEls().getTrackElementByTrack_(this.track_);
18508  
18509          if (remoteTextTrackEl) {
18510            remoteTextTrackEl.removeEventListener('load', this.updateHandler_);
18511          }
18512  
18513          this.track_ = null;
18514        }
18515  
18516        this.track_ = track; // here this.track_ refers to the new track instance
18517  
18518        if (this.track_) {
18519          this.track_.mode = 'hidden';
18520  
18521          var _remoteTextTrackEl = this.player_.remoteTextTrackEls().getTrackElementByTrack_(this.track_);
18522  
18523          if (_remoteTextTrackEl) {
18524            _remoteTextTrackEl.addEventListener('load', this.updateHandler_);
18525          }
18526        }
18527      }
18528      /**
18529       * Find the track object that is currently in use by this ChaptersButton
18530       *
18531       * @return {TextTrack|undefined}
18532       *         The current track or undefined if none was found.
18533       */
18534      ;
18535  
18536      _proto.findChaptersTrack = function findChaptersTrack() {
18537        var tracks = this.player_.textTracks() || [];
18538  
18539        for (var i = tracks.length - 1; i >= 0; i--) {
18540          // We will always choose the last track as our chaptersTrack
18541          var track = tracks[i];
18542  
18543          if (track.kind === this.kind_) {
18544            return track;
18545          }
18546        }
18547      }
18548      /**
18549       * Get the caption for the ChaptersButton based on the track label. This will also
18550       * use the current tracks localized kind as a fallback if a label does not exist.
18551       *
18552       * @return {string}
18553       *         The tracks current label or the localized track kind.
18554       */
18555      ;
18556  
18557      _proto.getMenuCaption = function getMenuCaption() {
18558        if (this.track_ && this.track_.label) {
18559          return this.track_.label;
18560        }
18561  
18562        return this.localize(toTitleCase(this.kind_));
18563      }
18564      /**
18565       * Create menu from chapter track
18566       *
18567       * @return {Menu}
18568       *         New menu for the chapter buttons
18569       */
18570      ;
18571  
18572      _proto.createMenu = function createMenu() {
18573        this.options_.title = this.getMenuCaption();
18574        return _TextTrackButton.prototype.createMenu.call(this);
18575      }
18576      /**
18577       * Create a menu item for each text track
18578       *
18579       * @return {TextTrackMenuItem[]}
18580       *         Array of menu items
18581       */
18582      ;
18583  
18584      _proto.createItems = function createItems() {
18585        var items = [];
18586  
18587        if (!this.track_) {
18588          return items;
18589        }
18590  
18591        var cues = this.track_.cues;
18592  
18593        if (!cues) {
18594          return items;
18595        }
18596  
18597        for (var i = 0, l = cues.length; i < l; i++) {
18598          var cue = cues[i];
18599          var mi = new ChaptersTrackMenuItem(this.player_, {
18600            track: this.track_,
18601            cue: cue
18602          });
18603          items.push(mi);
18604        }
18605  
18606        return items;
18607      };
18608  
18609      return ChaptersButton;
18610    }(TextTrackButton);
18611    /**
18612     * `kind` of TextTrack to look for to associate it with this menu.
18613     *
18614     * @type {string}
18615     * @private
18616     */
18617  
18618  
18619    ChaptersButton.prototype.kind_ = 'chapters';
18620    /**
18621     * The text that should display over the `ChaptersButton`s controls. Added for localization.
18622     *
18623     * @type {string}
18624     * @private
18625     */
18626  
18627    ChaptersButton.prototype.controlText_ = 'Chapters';
18628    Component.registerComponent('ChaptersButton', ChaptersButton);
18629  
18630    /**
18631     * The button component for toggling and selecting descriptions
18632     *
18633     * @extends TextTrackButton
18634     */
18635  
18636    var DescriptionsButton = /*#__PURE__*/function (_TextTrackButton) {
18637      inheritsLoose(DescriptionsButton, _TextTrackButton);
18638  
18639      /**
18640       * Creates an instance of this class.
18641       *
18642       * @param {Player} player
18643       *        The `Player` that this class should be attached to.
18644       *
18645       * @param {Object} [options]
18646       *        The key/value store of player options.
18647       *
18648       * @param {Component~ReadyCallback} [ready]
18649       *        The function to call when this component is ready.
18650       */
18651      function DescriptionsButton(player, options, ready) {
18652        var _this;
18653  
18654        _this = _TextTrackButton.call(this, player, options, ready) || this;
18655        var tracks = player.textTracks();
18656        var changeHandler = bind(assertThisInitialized(_this), _this.handleTracksChange);
18657        tracks.addEventListener('change', changeHandler);
18658  
18659        _this.on('dispose', function () {
18660          tracks.removeEventListener('change', changeHandler);
18661        });
18662  
18663        return _this;
18664      }
18665      /**
18666       * Handle text track change
18667       *
18668       * @param {EventTarget~Event} event
18669       *        The event that caused this function to run
18670       *
18671       * @listens TextTrackList#change
18672       */
18673  
18674  
18675      var _proto = DescriptionsButton.prototype;
18676  
18677      _proto.handleTracksChange = function handleTracksChange(event) {
18678        var tracks = this.player().textTracks();
18679        var disabled = false; // Check whether a track of a different kind is showing
18680  
18681        for (var i = 0, l = tracks.length; i < l; i++) {
18682          var track = tracks[i];
18683  
18684          if (track.kind !== this.kind_ && track.mode === 'showing') {
18685            disabled = true;
18686            break;
18687          }
18688        } // If another track is showing, disable this menu button
18689  
18690  
18691        if (disabled) {
18692          this.disable();
18693        } else {
18694          this.enable();
18695        }
18696      }
18697      /**
18698       * Builds the default DOM `className`.
18699       *
18700       * @return {string}
18701       *         The DOM `className` for this object.
18702       */
18703      ;
18704  
18705      _proto.buildCSSClass = function buildCSSClass() {
18706        return "vjs-descriptions-button " + _TextTrackButton.prototype.buildCSSClass.call(this);
18707      };
18708  
18709      _proto.buildWrapperCSSClass = function buildWrapperCSSClass() {
18710        return "vjs-descriptions-button " + _TextTrackButton.prototype.buildWrapperCSSClass.call(this);
18711      };
18712  
18713      return DescriptionsButton;
18714    }(TextTrackButton);
18715    /**
18716     * `kind` of TextTrack to look for to associate it with this menu.
18717     *
18718     * @type {string}
18719     * @private
18720     */
18721  
18722  
18723    DescriptionsButton.prototype.kind_ = 'descriptions';
18724    /**
18725     * The text that should display over the `DescriptionsButton`s controls. Added for localization.
18726     *
18727     * @type {string}
18728     * @private
18729     */
18730  
18731    DescriptionsButton.prototype.controlText_ = 'Descriptions';
18732    Component.registerComponent('DescriptionsButton', DescriptionsButton);
18733  
18734    /**
18735     * The button component for toggling and selecting subtitles
18736     *
18737     * @extends TextTrackButton
18738     */
18739  
18740    var SubtitlesButton = /*#__PURE__*/function (_TextTrackButton) {
18741      inheritsLoose(SubtitlesButton, _TextTrackButton);
18742  
18743      /**
18744       * Creates an instance of this class.
18745       *
18746       * @param {Player} player
18747       *        The `Player` that this class should be attached to.
18748       *
18749       * @param {Object} [options]
18750       *        The key/value store of player options.
18751       *
18752       * @param {Component~ReadyCallback} [ready]
18753       *        The function to call when this component is ready.
18754       */
18755      function SubtitlesButton(player, options, ready) {
18756        return _TextTrackButton.call(this, player, options, ready) || this;
18757      }
18758      /**
18759       * Builds the default DOM `className`.
18760       *
18761       * @return {string}
18762       *         The DOM `className` for this object.
18763       */
18764  
18765  
18766      var _proto = SubtitlesButton.prototype;
18767  
18768      _proto.buildCSSClass = function buildCSSClass() {
18769        return "vjs-subtitles-button " + _TextTrackButton.prototype.buildCSSClass.call(this);
18770      };
18771  
18772      _proto.buildWrapperCSSClass = function buildWrapperCSSClass() {
18773        return "vjs-subtitles-button " + _TextTrackButton.prototype.buildWrapperCSSClass.call(this);
18774      };
18775  
18776      return SubtitlesButton;
18777    }(TextTrackButton);
18778    /**
18779     * `kind` of TextTrack to look for to associate it with this menu.
18780     *
18781     * @type {string}
18782     * @private
18783     */
18784  
18785  
18786    SubtitlesButton.prototype.kind_ = 'subtitles';
18787    /**
18788     * The text that should display over the `SubtitlesButton`s controls. Added for localization.
18789     *
18790     * @type {string}
18791     * @private
18792     */
18793  
18794    SubtitlesButton.prototype.controlText_ = 'Subtitles';
18795    Component.registerComponent('SubtitlesButton', SubtitlesButton);
18796  
18797    /**
18798     * The menu item for caption track settings menu
18799     *
18800     * @extends TextTrackMenuItem
18801     */
18802  
18803    var CaptionSettingsMenuItem = /*#__PURE__*/function (_TextTrackMenuItem) {
18804      inheritsLoose(CaptionSettingsMenuItem, _TextTrackMenuItem);
18805  
18806      /**
18807       * Creates an instance of this class.
18808       *
18809       * @param {Player} player
18810       *        The `Player` that this class should be attached to.
18811       *
18812       * @param {Object} [options]
18813       *        The key/value store of player options.
18814       */
18815      function CaptionSettingsMenuItem(player, options) {
18816        var _this;
18817  
18818        options.track = {
18819          player: player,
18820          kind: options.kind,
18821          label: options.kind + ' settings',
18822          selectable: false,
18823          "default": false,
18824          mode: 'disabled'
18825        }; // CaptionSettingsMenuItem has no concept of 'selected'
18826  
18827        options.selectable = false;
18828        options.name = 'CaptionSettingsMenuItem';
18829        _this = _TextTrackMenuItem.call(this, player, options) || this;
18830  
18831        _this.addClass('vjs-texttrack-settings');
18832  
18833        _this.controlText(', opens ' + options.kind + ' settings dialog');
18834  
18835        return _this;
18836      }
18837      /**
18838       * This gets called when an `CaptionSettingsMenuItem` is "clicked". See
18839       * {@link ClickableComponent} for more detailed information on what a click can be.
18840       *
18841       * @param {EventTarget~Event} [event]
18842       *        The `keydown`, `tap`, or `click` event that caused this function to be
18843       *        called.
18844       *
18845       * @listens tap
18846       * @listens click
18847       */
18848  
18849  
18850      var _proto = CaptionSettingsMenuItem.prototype;
18851  
18852      _proto.handleClick = function handleClick(event) {
18853        this.player().getChild('textTrackSettings').open();
18854      };
18855  
18856      return CaptionSettingsMenuItem;
18857    }(TextTrackMenuItem);
18858  
18859    Component.registerComponent('CaptionSettingsMenuItem', CaptionSettingsMenuItem);
18860  
18861    /**
18862     * The button component for toggling and selecting captions
18863     *
18864     * @extends TextTrackButton
18865     */
18866  
18867    var CaptionsButton = /*#__PURE__*/function (_TextTrackButton) {
18868      inheritsLoose(CaptionsButton, _TextTrackButton);
18869  
18870      /**
18871       * Creates an instance of this class.
18872       *
18873       * @param {Player} player
18874       *        The `Player` that this class should be attached to.
18875       *
18876       * @param {Object} [options]
18877       *        The key/value store of player options.
18878       *
18879       * @param {Component~ReadyCallback} [ready]
18880       *        The function to call when this component is ready.
18881       */
18882      function CaptionsButton(player, options, ready) {
18883        return _TextTrackButton.call(this, player, options, ready) || this;
18884      }
18885      /**
18886       * Builds the default DOM `className`.
18887       *
18888       * @return {string}
18889       *         The DOM `className` for this object.
18890       */
18891  
18892  
18893      var _proto = CaptionsButton.prototype;
18894  
18895      _proto.buildCSSClass = function buildCSSClass() {
18896        return "vjs-captions-button " + _TextTrackButton.prototype.buildCSSClass.call(this);
18897      };
18898  
18899      _proto.buildWrapperCSSClass = function buildWrapperCSSClass() {
18900        return "vjs-captions-button " + _TextTrackButton.prototype.buildWrapperCSSClass.call(this);
18901      }
18902      /**
18903       * Create caption menu items
18904       *
18905       * @return {CaptionSettingsMenuItem[]}
18906       *         The array of current menu items.
18907       */
18908      ;
18909  
18910      _proto.createItems = function createItems() {
18911        var items = [];
18912  
18913        if (!(this.player().tech_ && this.player().tech_.featuresNativeTextTracks) && this.player().getChild('textTrackSettings')) {
18914          items.push(new CaptionSettingsMenuItem(this.player_, {
18915            kind: this.kind_
18916          }));
18917          this.hideThreshold_ += 1;
18918        }
18919  
18920        return _TextTrackButton.prototype.createItems.call(this, items);
18921      };
18922  
18923      return CaptionsButton;
18924    }(TextTrackButton);
18925    /**
18926     * `kind` of TextTrack to look for to associate it with this menu.
18927     *
18928     * @type {string}
18929     * @private
18930     */
18931  
18932  
18933    CaptionsButton.prototype.kind_ = 'captions';
18934    /**
18935     * The text that should display over the `CaptionsButton`s controls. Added for localization.
18936     *
18937     * @type {string}
18938     * @private
18939     */
18940  
18941    CaptionsButton.prototype.controlText_ = 'Captions';
18942    Component.registerComponent('CaptionsButton', CaptionsButton);
18943  
18944    /**
18945     * SubsCapsMenuItem has an [cc] icon to distinguish captions from subtitles
18946     * in the SubsCapsMenu.
18947     *
18948     * @extends TextTrackMenuItem
18949     */
18950  
18951    var SubsCapsMenuItem = /*#__PURE__*/function (_TextTrackMenuItem) {
18952      inheritsLoose(SubsCapsMenuItem, _TextTrackMenuItem);
18953  
18954      function SubsCapsMenuItem() {
18955        return _TextTrackMenuItem.apply(this, arguments) || this;
18956      }
18957  
18958      var _proto = SubsCapsMenuItem.prototype;
18959  
18960      _proto.createEl = function createEl(type, props, attrs) {
18961        var innerHTML = "<span class=\"vjs-menu-item-text\">" + this.localize(this.options_.label);
18962  
18963        if (this.options_.track.kind === 'captions') {
18964          innerHTML += "\n        <span aria-hidden=\"true\" class=\"vjs-icon-placeholder\"></span>\n        <span class=\"vjs-control-text\"> " + this.localize('Captions') + "</span>\n      ";
18965        }
18966  
18967        innerHTML += '</span>';
18968  
18969        var el = _TextTrackMenuItem.prototype.createEl.call(this, type, assign({
18970          innerHTML: innerHTML
18971        }, props), attrs);
18972  
18973        return el;
18974      };
18975  
18976      return SubsCapsMenuItem;
18977    }(TextTrackMenuItem);
18978  
18979    Component.registerComponent('SubsCapsMenuItem', SubsCapsMenuItem);
18980  
18981    /**
18982     * The button component for toggling and selecting captions and/or subtitles
18983     *
18984     * @extends TextTrackButton
18985     */
18986  
18987    var SubsCapsButton = /*#__PURE__*/function (_TextTrackButton) {
vendor: 18,171 bytes, lines 18988-19603
18988      inheritsLoose(SubsCapsButton, _TextTrackButton);
18989  
18990      function SubsCapsButton(player, options) {
18991        var _this;
18992  
18993        if (options === void 0) {
18994          options = {};
18995        }
18996  
18997        _this = _TextTrackButton.call(this, player, options) || this; // Although North America uses "captions" in most cases for
18998        // "captions and subtitles" other locales use "subtitles"
18999  
19000        _this.label_ = 'subtitles';
19001  
19002        if (['en', 'en-us', 'en-ca', 'fr-ca'].indexOf(_this.player_.language_) > -1) {
19003          _this.label_ = 'captions';
19004        }
19005  
19006        _this.menuButton_.controlText(toTitleCase(_this.label_));
19007  
19008        return _this;
19009      }
19010      /**
19011       * Builds the default DOM `className`.
19012       *
19013       * @return {string}
19014       *         The DOM `className` for this object.
19015       */
19016  
19017  
19018      var _proto = SubsCapsButton.prototype;
19019  
19020      _proto.buildCSSClass = function buildCSSClass() {
19021        return "vjs-subs-caps-button " + _TextTrackButton.prototype.buildCSSClass.call(this);
19022      };
19023  
19024      _proto.buildWrapperCSSClass = function buildWrapperCSSClass() {
19025        return "vjs-subs-caps-button " + _TextTrackButton.prototype.buildWrapperCSSClass.call(this);
19026      }
19027      /**
19028       * Create caption/subtitles menu items
19029       *
19030       * @return {CaptionSettingsMenuItem[]}
19031       *         The array of current menu items.
19032       */
19033      ;
19034  
19035      _proto.createItems = function createItems() {
19036        var items = [];
19037  
19038        if (!(this.player().tech_ && this.player().tech_.featuresNativeTextTracks) && this.player().getChild('textTrackSettings')) {
19039          items.push(new CaptionSettingsMenuItem(this.player_, {
19040            kind: this.label_
19041          }));
19042          this.hideThreshold_ += 1;
19043        }
19044  
19045        items = _TextTrackButton.prototype.createItems.call(this, items, SubsCapsMenuItem);
19046        return items;
19047      };
19048  
19049      return SubsCapsButton;
19050    }(TextTrackButton);
19051    /**
19052     * `kind`s of TextTrack to look for to associate it with this menu.
19053     *
19054     * @type {array}
19055     * @private
19056     */
19057  
19058  
19059    SubsCapsButton.prototype.kinds_ = ['captions', 'subtitles'];
19060    /**
19061     * The text that should display over the `SubsCapsButton`s controls.
19062     *
19063     *
19064     * @type {string}
19065     * @private
19066     */
19067  
19068    SubsCapsButton.prototype.controlText_ = 'Subtitles';
19069    Component.registerComponent('SubsCapsButton', SubsCapsButton);
19070  
19071    /**
19072     * An {@link AudioTrack} {@link MenuItem}
19073     *
19074     * @extends MenuItem
19075     */
19076  
19077    var AudioTrackMenuItem = /*#__PURE__*/function (_MenuItem) {
19078      inheritsLoose(AudioTrackMenuItem, _MenuItem);
19079  
19080      /**
19081       * Creates an instance of this class.
19082       *
19083       * @param {Player} player
19084       *        The `Player` that this class should be attached to.
19085       *
19086       * @param {Object} [options]
19087       *        The key/value store of player options.
19088       */
19089      function AudioTrackMenuItem(player, options) {
19090        var _this;
19091  
19092        var track = options.track;
19093        var tracks = player.audioTracks(); // Modify options for parent MenuItem class's init.
19094  
19095        options.label = track.label || track.language || 'Unknown';
19096        options.selected = track.enabled;
19097        _this = _MenuItem.call(this, player, options) || this;
19098        _this.track = track;
19099  
19100        _this.addClass("vjs-" + track.kind + "-menu-item");
19101  
19102        var changeHandler = function changeHandler() {
19103          for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) {
19104            args[_key] = arguments[_key];
19105          }
19106  
19107          _this.handleTracksChange.apply(assertThisInitialized(_this), args);
19108        };
19109  
19110        tracks.addEventListener('change', changeHandler);
19111  
19112        _this.on('dispose', function () {
19113          tracks.removeEventListener('change', changeHandler);
19114        });
19115  
19116        return _this;
19117      }
19118  
19119      var _proto = AudioTrackMenuItem.prototype;
19120  
19121      _proto.createEl = function createEl(type, props, attrs) {
19122        var innerHTML = "<span class=\"vjs-menu-item-text\">" + this.localize(this.options_.label);
19123  
19124        if (this.options_.track.kind === 'main-desc') {
19125          innerHTML += "\n        <span aria-hidden=\"true\" class=\"vjs-icon-placeholder\"></span>\n        <span class=\"vjs-control-text\"> " + this.localize('Descriptions') + "</span>\n      ";
19126        }
19127  
19128        innerHTML += '</span>';
19129  
19130        var el = _MenuItem.prototype.createEl.call(this, type, assign({
19131          innerHTML: innerHTML
19132        }, props), attrs);
19133  
19134        return el;
19135      }
19136      /**
19137       * This gets called when an `AudioTrackMenuItem is "clicked". See {@link ClickableComponent}
19138       * for more detailed information on what a click can be.
19139       *
19140       * @param {EventTarget~Event} [event]
19141       *        The `keydown`, `tap`, or `click` event that caused this function to be
19142       *        called.
19143       *
19144       * @listens tap
19145       * @listens click
19146       */
19147      ;
19148  
19149      _proto.handleClick = function handleClick(event) {
19150        var tracks = this.player_.audioTracks();
19151  
19152        _MenuItem.prototype.handleClick.call(this, event);
19153  
19154        for (var i = 0; i < tracks.length; i++) {
19155          var track = tracks[i];
19156          track.enabled = track === this.track;
19157        }
19158      }
19159      /**
19160       * Handle any {@link AudioTrack} change.
19161       *
19162       * @param {EventTarget~Event} [event]
19163       *        The {@link AudioTrackList#change} event that caused this to run.
19164       *
19165       * @listens AudioTrackList#change
19166       */
19167      ;
19168  
19169      _proto.handleTracksChange = function handleTracksChange(event) {
19170        this.selected(this.track.enabled);
19171      };
19172  
19173      return AudioTrackMenuItem;
19174    }(MenuItem);
19175  
19176    Component.registerComponent('AudioTrackMenuItem', AudioTrackMenuItem);
19177  
19178    /**
19179     * The base class for buttons that toggle specific {@link AudioTrack} types.
19180     *
19181     * @extends TrackButton
19182     */
19183  
19184    var AudioTrackButton = /*#__PURE__*/function (_TrackButton) {
19185      inheritsLoose(AudioTrackButton, _TrackButton);
19186  
19187      /**
19188       * Creates an instance of this class.
19189       *
19190       * @param {Player} player
19191       *        The `Player` that this class should be attached to.
19192       *
19193       * @param {Object} [options={}]
19194       *        The key/value store of player options.
19195       */
19196      function AudioTrackButton(player, options) {
19197        if (options === void 0) {
19198          options = {};
19199        }
19200  
19201        options.tracks = player.audioTracks();
19202        return _TrackButton.call(this, player, options) || this;
19203      }
19204      /**
19205       * Builds the default DOM `className`.
19206       *
19207       * @return {string}
19208       *         The DOM `className` for this object.
19209       */
19210  
19211  
19212      var _proto = AudioTrackButton.prototype;
19213  
19214      _proto.buildCSSClass = function buildCSSClass() {
19215        return "vjs-audio-button " + _TrackButton.prototype.buildCSSClass.call(this);
19216      };
19217  
19218      _proto.buildWrapperCSSClass = function buildWrapperCSSClass() {
19219        return "vjs-audio-button " + _TrackButton.prototype.buildWrapperCSSClass.call(this);
19220      }
19221      /**
19222       * Create a menu item for each audio track
19223       *
19224       * @param {AudioTrackMenuItem[]} [items=[]]
19225       *        An array of existing menu items to use.
19226       *
19227       * @return {AudioTrackMenuItem[]}
19228       *         An array of menu items
19229       */
19230      ;
19231  
19232      _proto.createItems = function createItems(items) {
19233        if (items === void 0) {
19234          items = [];
19235        }
19236  
19237        // if there's only one audio track, there no point in showing it
19238        this.hideThreshold_ = 1;
19239        var tracks = this.player_.audioTracks();
19240  
19241        for (var i = 0; i < tracks.length; i++) {
19242          var track = tracks[i];
19243          items.push(new AudioTrackMenuItem(this.player_, {
19244            track: track,
19245            // MenuItem is selectable
19246            selectable: true,
19247            // MenuItem is NOT multiSelectable (i.e. only one can be marked "selected" at a time)
19248            multiSelectable: false
19249          }));
19250        }
19251  
19252        return items;
19253      };
19254  
19255      return AudioTrackButton;
19256    }(TrackButton);
19257    /**
19258     * The text that should display over the `AudioTrackButton`s controls. Added for localization.
19259     *
19260     * @type {string}
19261     * @private
19262     */
19263  
19264  
19265    AudioTrackButton.prototype.controlText_ = 'Audio Track';
19266    Component.registerComponent('AudioTrackButton', AudioTrackButton);
19267  
19268    /**
19269     * The specific menu item type for selecting a playback rate.
19270     *
19271     * @extends MenuItem
19272     */
19273  
19274    var PlaybackRateMenuItem = /*#__PURE__*/function (_MenuItem) {
19275      inheritsLoose(PlaybackRateMenuItem, _MenuItem);
19276  
19277      /**
19278       * Creates an instance of this class.
19279       *
19280       * @param {Player} player
19281       *        The `Player` that this class should be attached to.
19282       *
19283       * @param {Object} [options]
19284       *        The key/value store of player options.
19285       */
19286      function PlaybackRateMenuItem(player, options) {
19287        var _this;
19288  
19289        var label = options.rate;
19290        var rate = parseFloat(label, 10); // Modify options for parent MenuItem class's init.
19291  
19292        options.label = label;
19293        options.selected = rate === 1;
19294        options.selectable = true;
19295        options.multiSelectable = false;
19296        _this = _MenuItem.call(this, player, options) || this;
19297        _this.label = label;
19298        _this.rate = rate;
19299  
19300        _this.on(player, 'ratechange', _this.update);
19301  
19302        return _this;
19303      }
19304      /**
19305       * This gets called when an `PlaybackRateMenuItem` is "clicked". See
19306       * {@link ClickableComponent} for more detailed information on what a click can be.
19307       *
19308       * @param {EventTarget~Event} [event]
19309       *        The `keydown`, `tap`, or `click` event that caused this function to be
19310       *        called.
19311       *
19312       * @listens tap
19313       * @listens click
19314       */
19315  
19316  
19317      var _proto = PlaybackRateMenuItem.prototype;
19318  
19319      _proto.handleClick = function handleClick(event) {
19320        _MenuItem.prototype.handleClick.call(this);
19321  
19322        this.player().playbackRate(this.rate);
19323      }
19324      /**
19325       * Update the PlaybackRateMenuItem when the playbackrate changes.
19326       *
19327       * @param {EventTarget~Event} [event]
19328       *        The `ratechange` event that caused this function to run.
19329       *
19330       * @listens Player#ratechange
19331       */
19332      ;
19333  
19334      _proto.update = function update(event) {
19335        this.selected(this.player().playbackRate() === this.rate);
19336      };
19337  
19338      return PlaybackRateMenuItem;
19339    }(MenuItem);
19340    /**
19341     * The text that should display over the `PlaybackRateMenuItem`s controls. Added for localization.
19342     *
19343     * @type {string}
19344     * @private
19345     */
19346  
19347  
19348    PlaybackRateMenuItem.prototype.contentElType = 'button';
19349    Component.registerComponent('PlaybackRateMenuItem', PlaybackRateMenuItem);
19350  
19351    /**
19352     * The component for controlling the playback rate.
19353     *
19354     * @extends MenuButton
19355     */
19356  
19357    var PlaybackRateMenuButton = /*#__PURE__*/function (_MenuButton) {
19358      inheritsLoose(PlaybackRateMenuButton, _MenuButton);
19359  
19360      /**
19361       * Creates an instance of this class.
19362       *
19363       * @param {Player} player
19364       *        The `Player` that this class should be attached to.
19365       *
19366       * @param {Object} [options]
19367       *        The key/value store of player options.
19368       */
19369      function PlaybackRateMenuButton(player, options) {
19370        var _this;
19371  
19372        _this = _MenuButton.call(this, player, options) || this;
19373  
19374        _this.updateVisibility();
19375  
19376        _this.updateLabel();
19377  
19378        _this.on(player, 'loadstart', _this.updateVisibility);
19379  
19380        _this.on(player, 'ratechange', _this.updateLabel);
19381  
19382        return _this;
19383      }
19384      /**
19385       * Create the `Component`'s DOM element
19386       *
19387       * @return {Element}
19388       *         The element that was created.
19389       */
19390  
19391  
19392      var _proto = PlaybackRateMenuButton.prototype;
19393  
19394      _proto.createEl = function createEl$1() {
19395        var el = _MenuButton.prototype.createEl.call(this);
19396  
19397        this.labelEl_ = createEl('div', {
19398          className: 'vjs-playback-rate-value',
19399          innerHTML: '1x'
19400        });
19401        el.appendChild(this.labelEl_);
19402        return el;
19403      };
19404  
19405      _proto.dispose = function dispose() {
19406        this.labelEl_ = null;
19407  
19408        _MenuButton.prototype.dispose.call(this);
19409      }
19410      /**
19411       * Builds the default DOM `className`.
19412       *
19413       * @return {string}
19414       *         The DOM `className` for this object.
19415       */
19416      ;
19417  
19418      _proto.buildCSSClass = function buildCSSClass() {
19419        return "vjs-playback-rate " + _MenuButton.prototype.buildCSSClass.call(this);
19420      };
19421  
19422      _proto.buildWrapperCSSClass = function buildWrapperCSSClass() {
19423        return "vjs-playback-rate " + _MenuButton.prototype.buildWrapperCSSClass.call(this);
19424      }
19425      /**
19426       * Create the playback rate menu
19427       *
19428       * @return {Menu}
19429       *         Menu object populated with {@link PlaybackRateMenuItem}s
19430       */
19431      ;
19432  
19433      _proto.createMenu = function createMenu() {
19434        var menu = new Menu(this.player());
19435        var rates = this.playbackRates();
19436  
19437        if (rates) {
19438          for (var i = rates.length - 1; i >= 0; i--) {
19439            menu.addChild(new PlaybackRateMenuItem(this.player(), {
19440              rate: rates[i] + 'x'
19441            }));
19442          }
19443        }
19444  
19445        return menu;
19446      }
19447      /**
19448       * Updates ARIA accessibility attributes
19449       */
19450      ;
19451  
19452      _proto.updateARIAAttributes = function updateARIAAttributes() {
19453        // Current playback rate
19454        this.el().setAttribute('aria-valuenow', this.player().playbackRate());
19455      }
19456      /**
19457       * This gets called when an `PlaybackRateMenuButton` is "clicked". See
19458       * {@link ClickableComponent} for more detailed information on what a click can be.
19459       *
19460       * @param {EventTarget~Event} [event]
19461       *        The `keydown`, `tap`, or `click` event that caused this function to be
19462       *        called.
19463       *
19464       * @listens tap
19465       * @listens click
19466       */
19467      ;
19468  
19469      _proto.handleClick = function handleClick(event) {
19470        // select next rate option
19471        var currentRate = this.player().playbackRate();
19472        var rates = this.playbackRates(); // this will select first one if the last one currently selected
19473  
19474        var newRate = rates[0];
19475  
19476        for (var i = 0; i < rates.length; i++) {
19477          if (rates[i] > currentRate) {
19478            newRate = rates[i];
19479            break;
19480          }
19481        }
19482  
19483        this.player().playbackRate(newRate);
19484      }
19485      /**
19486       * Get possible playback rates
19487       *
19488       * @return {Array}
19489       *         All possible playback rates
19490       */
19491      ;
19492  
19493      _proto.playbackRates = function playbackRates() {
19494        return this.options_.playbackRates || this.options_.playerOptions && this.options_.playerOptions.playbackRates;
19495      }
19496      /**
19497       * Get whether playback rates is supported by the tech
19498       * and an array of playback rates exists
19499       *
19500       * @return {boolean}
19501       *         Whether changing playback rate is supported
19502       */
19503      ;
19504  
19505      _proto.playbackRateSupported = function playbackRateSupported() {
19506        return this.player().tech_ && this.player().tech_.featuresPlaybackRate && this.playbackRates() && this.playbackRates().length > 0;
19507      }
19508      /**
19509       * Hide playback rate controls when they're no playback rate options to select
19510       *
19511       * @param {EventTarget~Event} [event]
19512       *        The event that caused this function to run.
19513       *
19514       * @listens Player#loadstart
19515       */
19516      ;
19517  
19518      _proto.updateVisibility = function updateVisibility(event) {
19519        if (this.playbackRateSupported()) {
19520          this.removeClass('vjs-hidden');
19521        } else {
19522          this.addClass('vjs-hidden');
19523        }
19524      }
19525      /**
19526       * Update button label when rate changed
19527       *
19528       * @param {EventTarget~Event} [event]
19529       *        The event that caused this function to run.
19530       *
19531       * @listens Player#ratechange
19532       */
19533      ;
19534  
19535      _proto.updateLabel = function updateLabel(event) {
19536        if (this.playbackRateSupported()) {
19537          this.labelEl_.innerHTML = this.player().playbackRate() + 'x';
19538        }
19539      };
19540  
19541      return PlaybackRateMenuButton;
19542    }(MenuButton);
19543    /**
19544     * The text that should display over the `FullscreenToggle`s controls. Added for localization.
19545     *
19546     * @type {string}
19547     * @private
19548     */
19549  
19550  
19551    PlaybackRateMenuButton.prototype.controlText_ = 'Playback Rate';
19552    Component.registerComponent('PlaybackRateMenuButton', PlaybackRateMenuButton);
19553  
19554    /**
19555     * Just an empty spacer element that can be used as an append point for plugins, etc.
19556     * Also can be used to create space between elements when necessary.
19557     *
19558     * @extends Component
19559     */
19560  
19561    var Spacer = /*#__PURE__*/function (_Component) {
19562      inheritsLoose(Spacer, _Component);
19563  
19564      function Spacer() {
19565        return _Component.apply(this, arguments) || this;
19566      }
19567  
19568      var _proto = Spacer.prototype;
19569  
19570      /**
19571      * Builds the default DOM `className`.
19572      *
19573      * @return {string}
19574      *         The DOM `className` for this object.
19575      */
19576      _proto.buildCSSClass = function buildCSSClass() {
19577        return "vjs-spacer " + _Component.prototype.buildCSSClass.call(this);
19578      }
19579      /**
19580       * Create the `Component`'s DOM element
19581       *
19582       * @return {Element}
19583       *         The element that was created.
19584       */
19585      ;
19586  
19587      _proto.createEl = function createEl() {
19588        return _Component.prototype.createEl.call(this, 'div', {
19589          className: this.buildCSSClass()
19590        });
19591      };
19592  
19593      return Spacer;
19594    }(Component);
19595  
19596    Component.registerComponent('Spacer', Spacer);
19597  
19598    /**
19599     * Spacer specifically meant to be used as an insertion point for new plugins, etc.
19600     *
19601     * @extends Spacer
19602     */
19603  
19604    var CustomControlSpacer = /*#__PURE__*/function (_Spacer) {
19605      inheritsLoose(CustomControlSpacer, _Spacer);
19606  
19607      function CustomControlSpacer() {
19608        return _Spacer.apply(this, arguments) || this;
19609      }
19610  
19611      var _proto = CustomControlSpacer.prototype;
19612  
19613      /**
19614       * Builds the default DOM `className`.
19615       *
19616       * @return {string}
19617       *         The DOM `className` for this object.
19618       */
19619      _proto.buildCSSClass = function buildCSSClass() {
19620        return "vjs-custom-control-spacer " + _Spacer.prototype.buildCSSClass.call(this);
19621      }
19622      /**
19623       * Create the `Component`'s DOM element
19624       *
19625       * @return {Element}
19626       *         The element that was created.
19627       */
19628      ;
19629  
19630      _proto.createEl = function createEl() {
19631        var el = _Spacer.prototype.createEl.call(this, {
19632          className: this.buildCSSClass()
19633        }); // No-flex/table-cell mode requires there be some content
19634        // in the cell to fill the remaining space of the table.
19635  
19636  
19637        el.innerHTML = "\xA0";
19638        return el;
19639      };
19640  
19641      return CustomControlSpacer;
19642    }(Spacer);
19643  
19644    Component.registerComponent('CustomControlSpacer', CustomControlSpacer);
19645  
19646    /**
19647     * Container of main controls.
19648     *
19649     * @extends Component
19650     */
19651  
19652    var ControlBar = /*#__PURE__*/function (_Component) {
19653      inheritsLoose(ControlBar, _Component);
19654  
19655      function ControlBar() {
19656        return _Component.apply(this, arguments) || this;
19657      }
19658  
19659      var _proto = ControlBar.prototype;
19660  
19661      /**
19662       * Create the `Component`'s DOM element
19663       *
19664       * @return {Element}
19665       *         The element that was created.
19666       */
19667      _proto.createEl = function createEl() {
19668        return _Component.prototype.createEl.call(this, 'div', {
19669          className: 'vjs-control-bar',
19670          dir: 'ltr'
19671        });
19672      };
19673  
19674      return ControlBar;
19675    }(Component);
19676    /**
19677     * Default options for `ControlBar`
19678     *
19679     * @type {Object}
19680     * @private
19681     */
19682  
19683  
19684    ControlBar.prototype.options_ = {
19685      children: ['playToggle', 'volumePanel', 'currentTimeDisplay', 'timeDivider', 'durationDisplay', 'progressControl', 'liveDisplay', 'seekToLive', 'remainingTimeDisplay', 'customControlSpacer', 'playbackRateMenuButton', 'chaptersButton', 'descriptionsButton', 'subsCapsButton', 'audioTrackButton', 'fullscreenToggle']
19686    };
19687  
19688    if ('exitPictureInPicture' in document) {
19689      ControlBar.prototype.options_.children.splice(ControlBar.prototype.options_.children.length - 1, 0, 'pictureInPictureToggle');
19690    }
19691  
19692    Component.registerComponent('ControlBar', ControlBar);
19693  
19694    /**
19695     * A display that indicates an error has occurred. This means that the video
19696     * is unplayable.
19697     *
19698     * @extends ModalDialog
19699     */
19700  
19701    var ErrorDisplay = /*#__PURE__*/function (_ModalDialog) {
19702      inheritsLoose(ErrorDisplay, _ModalDialog);
19703  
19704      /**
19705       * Creates an instance of this class.
19706       *
19707       * @param  {Player} player
19708       *         The `Player` that this class should be attached to.
19709       *
19710       * @param  {Object} [options]
19711       *         The key/value store of player options.
19712       */
19713      function ErrorDisplay(player, options) {
19714        var _this;
19715  
19716        _this = _ModalDialog.call(this, player, options) || this;
19717  
19718        _this.on(player, 'error', _this.open);
19719  
19720        return _this;
19721      }
19722      /**
19723       * Builds the default DOM `className`.
19724       *
19725       * @return {string}
19726       *         The DOM `className` for this object.
19727       *
19728       * @deprecated Since version 5.
19729       */
19730  
19731  
19732      var _proto = ErrorDisplay.prototype;
19733  
19734      _proto.buildCSSClass = function buildCSSClass() {
19735        return "vjs-error-display " + _ModalDialog.prototype.buildCSSClass.call(this);
19736      }
19737      /**
19738       * Gets the localized error message based on the `Player`s error.
19739       *
19740       * @return {string}
19741       *         The `Player`s error message localized or an empty string.
19742       */
19743      ;
19744  
19745      _proto.content = function content() {
19746        var error = this.player().error();
19747        return error ? this.localize(error.message) : '';
19748      };
19749  
19750      return ErrorDisplay;
19751    }(ModalDialog);
19752    /**
19753     * The default options for an `ErrorDisplay`.
19754     *
19755     * @private
19756     */
19757  
19758  
19759    ErrorDisplay.prototype.options_ = _extends_1({}, ModalDialog.prototype.options_, {
vendor: 16,614 bytes, lines 19760-20251
19760      pauseOnOpen: false,
19761      fillAlways: true,
19762      temporary: false,
19763      uncloseable: true
19764    });
19765    Component.registerComponent('ErrorDisplay', ErrorDisplay);
19766  
19767    var LOCAL_STORAGE_KEY = 'vjs-text-track-settings';
19768    var COLOR_BLACK = ['#000', 'Black'];
19769    var COLOR_BLUE = ['#00F', 'Blue'];
19770    var COLOR_CYAN = ['#0FF', 'Cyan'];
19771    var COLOR_GREEN = ['#0F0', 'Green'];
19772    var COLOR_MAGENTA = ['#F0F', 'Magenta'];
19773    var COLOR_RED = ['#F00', 'Red'];
19774    var COLOR_WHITE = ['#FFF', 'White'];
19775    var COLOR_YELLOW = ['#FF0', 'Yellow'];
19776    var OPACITY_OPAQUE = ['1', 'Opaque'];
19777    var OPACITY_SEMI = ['0.5', 'Semi-Transparent'];
19778    var OPACITY_TRANS = ['0', 'Transparent']; // Configuration for the various <select> elements in the DOM of this component.
19779    //
19780    // Possible keys include:
19781    //
19782    // `default`:
19783    //   The default option index. Only needs to be provided if not zero.
19784    // `parser`:
19785    //   A function which is used to parse the value from the selected option in
19786    //   a customized way.
19787    // `selector`:
19788    //   The selector used to find the associated <select> element.
19789  
19790    var selectConfigs = {
19791      backgroundColor: {
19792        selector: '.vjs-bg-color > select',
19793        id: 'captions-background-color-%s',
19794        label: 'Color',
19795        options: [COLOR_BLACK, COLOR_WHITE, COLOR_RED, COLOR_GREEN, COLOR_BLUE, COLOR_YELLOW, COLOR_MAGENTA, COLOR_CYAN]
19796      },
19797      backgroundOpacity: {
19798        selector: '.vjs-bg-opacity > select',
19799        id: 'captions-background-opacity-%s',
19800        label: 'Transparency',
19801        options: [OPACITY_OPAQUE, OPACITY_SEMI, OPACITY_TRANS]
19802      },
19803      color: {
19804        selector: '.vjs-fg-color > select',
19805        id: 'captions-foreground-color-%s',
19806        label: 'Color',
19807        options: [COLOR_WHITE, COLOR_BLACK, COLOR_RED, COLOR_GREEN, COLOR_BLUE, COLOR_YELLOW, COLOR_MAGENTA, COLOR_CYAN]
19808      },
19809      edgeStyle: {
19810        selector: '.vjs-edge-style > select',
19811        id: '%s',
19812        label: 'Text Edge Style',
19813        options: [['none', 'None'], ['raised', 'Raised'], ['depressed', 'Depressed'], ['uniform', 'Uniform'], ['dropshadow', 'Dropshadow']]
19814      },
19815      fontFamily: {
19816        selector: '.vjs-font-family > select',
19817        id: 'captions-font-family-%s',
19818        label: 'Font Family',
19819        options: [['proportionalSansSerif', 'Proportional Sans-Serif'], ['monospaceSansSerif', 'Monospace Sans-Serif'], ['proportionalSerif', 'Proportional Serif'], ['monospaceSerif', 'Monospace Serif'], ['casual', 'Casual'], ['script', 'Script'], ['small-caps', 'Small Caps']]
19820      },
19821      fontPercent: {
19822        selector: '.vjs-font-percent > select',
19823        id: 'captions-font-size-%s',
19824        label: 'Font Size',
19825        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%']],
19826        "default": 2,
19827        parser: function parser(v) {
19828          return v === '1.00' ? null : Number(v);
19829        }
19830      },
19831      textOpacity: {
19832        selector: '.vjs-text-opacity > select',
19833        id: 'captions-foreground-opacity-%s',
19834        label: 'Transparency',
19835        options: [OPACITY_OPAQUE, OPACITY_SEMI]
19836      },
19837      // Options for this object are defined below.
19838      windowColor: {
19839        selector: '.vjs-window-color > select',
19840        id: 'captions-window-color-%s',
19841        label: 'Color'
19842      },
19843      // Options for this object are defined below.
19844      windowOpacity: {
19845        selector: '.vjs-window-opacity > select',
19846        id: 'captions-window-opacity-%s',
19847        label: 'Transparency',
19848        options: [OPACITY_TRANS, OPACITY_SEMI, OPACITY_OPAQUE]
19849      }
19850    };
19851    selectConfigs.windowColor.options = selectConfigs.backgroundColor.options;
19852    /**
19853     * Get the actual value of an option.
19854     *
19855     * @param  {string} value
19856     *         The value to get
19857     *
19858     * @param  {Function} [parser]
19859     *         Optional function to adjust the value.
19860     *
19861     * @return {Mixed}
19862     *         - Will be `undefined` if no value exists
19863     *         - Will be `undefined` if the given value is "none".
19864     *         - Will be the actual value otherwise.
19865     *
19866     * @private
19867     */
19868  
19869    function parseOptionValue(value, parser) {
19870      if (parser) {
19871        value = parser(value);
19872      }
19873  
19874      if (value && value !== 'none') {
19875        return value;
19876      }
19877    }
19878    /**
19879     * Gets the value of the selected <option> element within a <select> element.
19880     *
19881     * @param  {Element} el
19882     *         the element to look in
19883     *
19884     * @param  {Function} [parser]
19885     *         Optional function to adjust the value.
19886     *
19887     * @return {Mixed}
19888     *         - Will be `undefined` if no value exists
19889     *         - Will be `undefined` if the given value is "none".
19890     *         - Will be the actual value otherwise.
19891     *
19892     * @private
19893     */
19894  
19895  
19896    function getSelectedOptionValue(el, parser) {
19897      var value = el.options[el.options.selectedIndex].value;
19898      return parseOptionValue(value, parser);
19899    }
19900    /**
19901     * Sets the selected <option> element within a <select> element based on a
19902     * given value.
19903     *
19904     * @param {Element} el
19905     *        The element to look in.
19906     *
19907     * @param {string} value
19908     *        the property to look on.
19909     *
19910     * @param {Function} [parser]
19911     *        Optional function to adjust the value before comparing.
19912     *
19913     * @private
19914     */
19915  
19916  
19917    function setSelectedOption(el, value, parser) {
19918      if (!value) {
19919        return;
19920      }
19921  
19922      for (var i = 0; i < el.options.length; i++) {
19923        if (parseOptionValue(el.options[i].value, parser) === value) {
19924          el.selectedIndex = i;
19925          break;
19926        }
19927      }
19928    }
19929    /**
19930     * Manipulate Text Tracks settings.
19931     *
19932     * @extends ModalDialog
19933     */
19934  
19935  
19936    var TextTrackSettings = /*#__PURE__*/function (_ModalDialog) {
19937      inheritsLoose(TextTrackSettings, _ModalDialog);
19938  
19939      /**
19940       * Creates an instance of this class.
19941       *
19942       * @param {Player} player
19943       *         The `Player` that this class should be attached to.
19944       *
19945       * @param {Object} [options]
19946       *         The key/value store of player options.
19947       */
19948      function TextTrackSettings(player, options) {
19949        var _this;
19950  
19951        options.temporary = false;
19952        _this = _ModalDialog.call(this, player, options) || this;
19953        _this.updateDisplay = bind(assertThisInitialized(_this), _this.updateDisplay); // fill the modal and pretend we have opened it
19954  
19955        _this.fill();
19956  
19957        _this.hasBeenOpened_ = _this.hasBeenFilled_ = true;
19958        _this.endDialog = createEl('p', {
19959          className: 'vjs-control-text',
19960          textContent: _this.localize('End of dialog window.')
19961        });
19962  
19963        _this.el().appendChild(_this.endDialog);
19964  
19965        _this.setDefaults(); // Grab `persistTextTrackSettings` from the player options if not passed in child options
19966  
19967  
19968        if (options.persistTextTrackSettings === undefined) {
19969          _this.options_.persistTextTrackSettings = _this.options_.playerOptions.persistTextTrackSettings;
19970        }
19971  
19972        _this.on(_this.$('.vjs-done-button'), 'click', function () {
19973          _this.saveSettings();
19974  
19975          _this.close();
19976        });
19977  
19978        _this.on(_this.$('.vjs-default-button'), 'click', function () {
19979          _this.setDefaults();
19980  
19981          _this.updateDisplay();
19982        });
19983  
19984        each(selectConfigs, function (config) {
19985          _this.on(_this.$(config.selector), 'change', _this.updateDisplay);
19986        });
19987  
19988        if (_this.options_.persistTextTrackSettings) {
19989          _this.restoreSettings();
19990        }
19991  
19992        return _this;
19993      }
19994  
19995      var _proto = TextTrackSettings.prototype;
19996  
19997      _proto.dispose = function dispose() {
19998        this.endDialog = null;
19999  
20000        _ModalDialog.prototype.dispose.call(this);
20001      }
20002      /**
20003       * Create a <select> element with configured options.
20004       *
20005       * @param {string} key
20006       *        Configuration key to use during creation.
20007       *
20008       * @return {string}
20009       *         An HTML string.
20010       *
20011       * @private
20012       */
20013      ;
20014  
20015      _proto.createElSelect_ = function createElSelect_(key, legendId, type) {
20016        var _this2 = this;
20017  
20018        if (legendId === void 0) {
20019          legendId = '';
20020        }
20021  
20022        if (type === void 0) {
20023          type = 'label';
20024        }
20025  
20026        var config = selectConfigs[key];
20027        var id = config.id.replace('%s', this.id_);
20028        var selectLabelledbyIds = [legendId, id].join(' ').trim();
20029        return ["<" + type + " id=\"" + id + "\" class=\"" + (type === 'label' ? 'vjs-label' : '') + "\">", this.localize(config.label), "</" + type + ">", "<select aria-labelledby=\"" + selectLabelledbyIds + "\">"].concat(config.options.map(function (o) {
20030          var optionId = id + '-' + o[1].replace(/\W+/g, '');
20031          return ["<option id=\"" + optionId + "\" value=\"" + o[0] + "\" ", "aria-labelledby=\"" + selectLabelledbyIds + " " + optionId + "\">", _this2.localize(o[1]), '</option>'].join('');
20032        })).concat('</select>').join('');
20033      }
20034      /**
20035       * Create foreground color element for the component
20036       *
20037       * @return {string}
20038       *         An HTML string.
20039       *
20040       * @private
20041       */
20042      ;
20043  
20044      _proto.createElFgColor_ = function createElFgColor_() {
20045        var legendId = "captions-text-legend-" + this.id_;
20046        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('');
20047      }
20048      /**
20049       * Create background color element for the component
20050       *
20051       * @return {string}
20052       *         An HTML string.
20053       *
20054       * @private
20055       */
20056      ;
20057  
20058      _proto.createElBgColor_ = function createElBgColor_() {
20059        var legendId = "captions-background-" + this.id_;
20060        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('');
20061      }
20062      /**
20063       * Create window color element for the component
20064       *
20065       * @return {string}
20066       *         An HTML string.
20067       *
20068       * @private
20069       */
20070      ;
20071  
20072      _proto.createElWinColor_ = function createElWinColor_() {
20073        var legendId = "captions-window-" + this.id_;
20074        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('');
20075      }
20076      /**
20077       * Create color elements for the component
20078       *
20079       * @return {Element}
20080       *         The element that was created
20081       *
20082       * @private
20083       */
20084      ;
20085  
20086      _proto.createElColors_ = function createElColors_() {
20087        return createEl('div', {
20088          className: 'vjs-track-settings-colors',
20089          innerHTML: [this.createElFgColor_(), this.createElBgColor_(), this.createElWinColor_()].join('')
20090        });
20091      }
20092      /**
20093       * Create font elements for the component
20094       *
20095       * @return {Element}
20096       *         The element that was created.
20097       *
20098       * @private
20099       */
20100      ;
20101  
20102      _proto.createElFont_ = function createElFont_() {
20103        return createEl('div', {
20104          className: 'vjs-track-settings-font',
20105          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('')
20106        });
20107      }
20108      /**
20109       * Create controls for the component
20110       *
20111       * @return {Element}
20112       *         The element that was created.
20113       *
20114       * @private
20115       */
20116      ;
20117  
20118      _proto.createElControls_ = function createElControls_() {
20119        var defaultsDescription = this.localize('restore all settings to the default values');
20120        return createEl('div', {
20121          className: 'vjs-track-settings-controls',
20122          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('')
20123        });
20124      };
20125  
20126      _proto.content = function content() {
20127        return [this.createElColors_(), this.createElFont_(), this.createElControls_()];
20128      };
20129  
20130      _proto.label = function label() {
20131        return this.localize('Caption Settings Dialog');
20132      };
20133  
20134      _proto.description = function description() {
20135        return this.localize('Beginning of dialog window. Escape will cancel and close the window.');
20136      };
20137  
20138      _proto.buildCSSClass = function buildCSSClass() {
20139        return _ModalDialog.prototype.buildCSSClass.call(this) + ' vjs-text-track-settings';
20140      }
20141      /**
20142       * Gets an object of text track settings (or null).
20143       *
20144       * @return {Object}
20145       *         An object with config values parsed from the DOM or localStorage.
20146       */
20147      ;
20148  
20149      _proto.getValues = function getValues() {
20150        var _this3 = this;
20151  
20152        return reduce(selectConfigs, function (accum, config, key) {
20153          var value = getSelectedOptionValue(_this3.$(config.selector), config.parser);
20154  
20155          if (value !== undefined) {
20156            accum[key] = value;
20157          }
20158  
20159          return accum;
20160        }, {});
20161      }
20162      /**
20163       * Sets text track settings from an object of values.
20164       *
20165       * @param {Object} values
20166       *        An object with config values parsed from the DOM or localStorage.
20167       */
20168      ;
20169  
20170      _proto.setValues = function setValues(values) {
20171        var _this4 = this;
20172  
20173        each(selectConfigs, function (config, key) {
20174          setSelectedOption(_this4.$(config.selector), values[key], config.parser);
20175        });
20176      }
20177      /**
20178       * Sets all `<select>` elements to their default values.
20179       */
20180      ;
20181  
20182      _proto.setDefaults = function setDefaults() {
20183        var _this5 = this;
20184  
20185        each(selectConfigs, function (config) {
20186          var index = config.hasOwnProperty('default') ? config["default"] : 0;
20187          _this5.$(config.selector).selectedIndex = index;
20188        });
20189      }
20190      /**
20191       * Restore texttrack settings from localStorage
20192       */
20193      ;
20194  
20195      _proto.restoreSettings = function restoreSettings() {
20196        var values;
20197  
20198        try {
20199          values = JSON.parse(window$3.localStorage.getItem(LOCAL_STORAGE_KEY));
20200        } catch (err) {
20201          log.warn(err);
20202        }
20203  
20204        if (values) {
20205          this.setValues(values);
20206        }
20207      }
20208      /**
20209       * Save text track settings to localStorage
20210       */
20211      ;
20212  
20213      _proto.saveSettings = function saveSettings() {
20214        if (!this.options_.persistTextTrackSettings) {
20215          return;
20216        }
20217  
20218        var values = this.getValues();
20219  
20220        try {
20221          if (Object.keys(values).length) {
20222            window$3.localStorage.setItem(LOCAL_STORAGE_KEY, JSON.stringify(values));
20223          } else {
20224            window$3.localStorage.removeItem(LOCAL_STORAGE_KEY);
20225          }
20226        } catch (err) {
20227          log.warn(err);
20228        }
20229      }
20230      /**
20231       * Update display of text track settings
20232       */
20233      ;
20234  
20235      _proto.updateDisplay = function updateDisplay() {
20236        var ttDisplay = this.player_.getChild('textTrackDisplay');
20237  
20238        if (ttDisplay) {
20239          ttDisplay.updateDisplay();
20240        }
20241      }
20242      /**
20243       * conditionally blur the element and refocus the captions button
20244       *
20245       * @private
20246       */
20247      ;
20248  
20249      _proto.conditionalBlur_ = function conditionalBlur_() {
20250        this.previouslyActiveEl_ = null;
20251        var cb = this.player_.controlBar;
20252        var subsCapsBtn = cb && cb.subsCapsButton;
20253        var ccBtn = cb && cb.captionsButton;
20254  
20255        if (subsCapsBtn) {
20256          subsCapsBtn.focus();
20257        } else if (ccBtn) {
20258          ccBtn.focus();
20259        }
20260      };
20261  
20262      return TextTrackSettings;
20263    }(ModalDialog);
20264  
20265    Component.registerComponent('TextTrackSettings', TextTrackSettings);
20266  
20267    /**
20268     * A Resize Manager. It is in charge of triggering `playerresize` on the player in the right conditions.
20269     *
20270     * 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}.
20271     *
20272     * If the ResizeObserver is available natively, it will be used. A polyfill can be passed in as an option.
20273     * If a `playerresize` event is not needed, the ResizeManager component can be removed from the player, see the example below.
20274     * @example <caption>How to disable the resize manager</caption>
20275     * const player = videojs('#vid', {
20276     *   resizeManager: false
20277     * });
20278     *
20279     * @see {@link https://wicg.github.io/ResizeObserver/|ResizeObserver specification}
20280     *
20281     * @extends Component
20282     */
20283  
20284    var ResizeManager = /*#__PURE__*/function (_Component) {
20285      inheritsLoose(ResizeManager, _Component);
20286  
20287      /**
20288       * Create the ResizeManager.
20289       *
20290       * @param {Object} player
20291       *        The `Player` that this class should be attached to.
20292       *
20293       * @param {Object} [options]
20294       *        The key/value store of ResizeManager options.
20295       *
20296       * @param {Object} [options.ResizeObserver]
20297       *        A polyfill for ResizeObserver can be passed in here.
20298       *        If this is set to null it will ignore the native ResizeObserver and fall back to the iframe fallback.
20299       */
20300      function ResizeManager(player, options) {
20301        var _this;
20302  
20303        var RESIZE_OBSERVER_AVAILABLE = options.ResizeObserver || window$3.ResizeObserver; // if `null` was passed, we want to disable the ResizeObserver
20304  
20305        if (options.ResizeObserver === null) {
20306          RESIZE_OBSERVER_AVAILABLE = false;
20307        } // Only create an element when ResizeObserver isn't available
20308  
20309  
20310        var options_ = mergeOptions({
20311          createEl: !RESIZE_OBSERVER_AVAILABLE,
20312          reportTouchActivity: false
20313        }, options);
20314        _this = _Component.call(this, player, options_) || this;
20315        _this.ResizeObserver = options.ResizeObserver || window$3.ResizeObserver;
20316        _this.loadListener_ = null;
20317        _this.resizeObserver_ = null;
20318        _this.debouncedHandler_ = debounce(function () {
20319          _this.resizeHandler();
20320        }, 100, false, assertThisInitialized(_this));
20321  
20322        if (RESIZE_OBSERVER_AVAILABLE) {
20323          _this.resizeObserver_ = new _this.ResizeObserver(_this.debouncedHandler_);
20324  
20325          _this.resizeObserver_.observe(player.el());
20326        } else {
20327          _this.loadListener_ = function () {
20328            if (!_this.el_ || !_this.el_.contentWindow) {
20329              return;
20330            }
20331  
20332            var debouncedHandler_ = _this.debouncedHandler_;
20333  
20334            var unloadListener_ = _this.unloadListener_ = function () {
20335              off(this, 'resize', debouncedHandler_);
20336              off(this, 'unload', unloadListener_);
20337              unloadListener_ = null;
20338            }; // safari and edge can unload the iframe before resizemanager dispose
20339            // we have to dispose of event handlers correctly before that happens
20340  
20341  
20342            on(_this.el_.contentWindow, 'unload', unloadListener_);
20343            on(_this.el_.contentWindow, 'resize', debouncedHandler_);
20344          };
20345  
20346          _this.one('load', _this.loadListener_);
20347        }
20348  
20349        return _this;
20350      }
20351  
20352      var _proto = ResizeManager.prototype;
20353  
20354      _proto.createEl = function createEl() {
20355        return _Component.prototype.createEl.call(this, 'iframe', {
20356          className: 'vjs-resize-manager',
20357          tabIndex: -1
20358        }, {
20359          'aria-hidden': 'true'
20360        });
20361      }
20362      /**
20363       * Called when a resize is triggered on the iframe or a resize is observed via the ResizeObserver
20364       *
20365       * @fires Player#playerresize
20366       */
20367      ;
20368  
20369      _proto.resizeHandler = function resizeHandler() {
20370        /**
20371         * Called when the player size has changed
20372         *
20373         * @event Player#playerresize
20374         * @type {EventTarget~Event}
20375         */
20376        // make sure player is still around to trigger
20377        // prevents this from causing an error after dispose
20378        if (!this.player_ || !this.player_.trigger) {
20379          return;
20380        }
20381  
20382        this.player_.trigger('playerresize');
20383      };
20384  
20385      _proto.dispose = function dispose() {
20386        if (this.debouncedHandler_) {
20387          this.debouncedHandler_.cancel();
20388        }
20389  
20390        if (this.resizeObserver_) {
20391          if (this.player_.el()) {
20392            this.resizeObserver_.unobserve(this.player_.el());
20393          }
20394  
20395          this.resizeObserver_.disconnect();
20396        }
20397  
20398        if (this.loadListener_) {
20399          this.off('load', this.loadListener_);
20400        }
20401  
20402        if (this.el_ && this.el_.contentWindow && this.unloadListener_) {
20403          this.unloadListener_.call(this.el_.contentWindow);
20404        }
20405  
20406        this.ResizeObserver = null;
20407        this.resizeObserver = null;
20408        this.debouncedHandler_ = null;
20409        this.loadListener_ = null;
20410  
20411        _Component.prototype.dispose.call(this);
20412      };
20413  
20414      return ResizeManager;
20415    }(Component);
20416  
20417    Component.registerComponent('ResizeManager', ResizeManager);
20418  
20419    var defaults = {
20420      trackingThreshold: 30,
20421      liveTolerance: 15
20422    };
20423    /*
20424      track when we are at the live edge, and other helpers for live playback */
20425  
20426    /**
20427     * A class for checking live current time and determining when the player
20428     * is at or behind the live edge.
20429     */
20430  
20431    var LiveTracker = /*#__PURE__*/function (_Component) {
20432      inheritsLoose(LiveTracker, _Component);
20433  
20434      /**
20435       * Creates an instance of this class.
20436       *
20437       * @param {Player} player
20438       *        The `Player` that this class should be attached to.
20439       *
20440       * @param {Object} [options]
20441       *        The key/value store of player options.
20442       *
20443       * @param {number} [options.trackingThreshold=30]
20444       *        Number of seconds of live window (seekableEnd - seekableStart) that
20445       *        media needs to have before the liveui will be shown.
20446       *
20447       * @param {number} [options.liveTolerance=15]
20448       *        Number of seconds behind live that we have to be
20449       *        before we will be considered non-live. Note that this will only
20450       *        be used when playing at the live edge. This allows large seekable end
20451       *        changes to not effect wether we are live or not.
20452       */
20453      function LiveTracker(player, options) {
20454        var _this;
20455  
20456        // LiveTracker does not need an element
20457        var options_ = mergeOptions(defaults, options, {
20458          createEl: false
20459        });
20460        _this = _Component.call(this, player, options_) || this;
20461  
20462        _this.reset_();
20463  
20464        _this.on(_this.player_, 'durationchange', _this.handleDurationchange); // we don't need to track live playback if the document is hidden,
20465        // also, tracking when the document is hidden can
20466        // cause the CPU to spike and eventually crash the page on IE11.
20467  
20468  
20469        if (IE_VERSION && 'hidden' in document && 'visibilityState' in document) {
20470          _this.on(document, 'visibilitychange', _this.handleVisibilityChange);
20471        }
20472  
20473        return _this;
20474      }
20475      /**
20476       * toggle tracking based on document visiblility
20477       */
20478  
20479  
20480      var _proto = LiveTracker.prototype;
20481  
20482      _proto.handleVisibilityChange = function handleVisibilityChange() {
20483        if (this.player_.duration() !== Infinity) {
20484          return;
20485        }
20486  
20487        if (document.hidden) {
20488          this.stopTracking();
20489        } else {
20490          this.startTracking();
20491        }
20492      }
20493      /**
20494       * all the functionality for tracking when seek end changes
20495       * and for tracking how far past seek end we should be
20496       */
20497      ;
20498  
20499      _proto.trackLive_ = function trackLive_() {
20500        var seekable = this.player_.seekable();
20500 // skip undefined seekable
20501  
20502        if (!seekable || !seekable.length) {
20503          return;
20504        }
20505  
20506        var newTime = Number(window$3.performance.now().toFixed(4));
20507        var deltaTime = this.lastTime_ === -1 ? 0 : (newTime - this.lastTime_) / 1000;
20508        this.lastTime_ = newTime;
20509        this.pastSeekEnd_ = this.pastSeekEnd() + deltaTime;
20510        var liveCurrentTime = this.liveCurrentTime();
20511        var currentTime = this.player_.currentTime(); // we are behind live if any are true
20512        // 1. the player is paused
20513        // 2. the user seeked to a location 2 seconds away from live
20514        // 3. the difference between live and current time is greater
20515        //    liveTolerance which defaults to 15s
20516  
20517        var isBehind = this.player_.paused() || this.seekedBehindLive_ || Math.abs(liveCurrentTime - currentTime) > this.options_.liveTolerance; // we cannot be behind if
20518        // 1. until we have not seen a timeupdate yet
20519        // 2. liveCurrentTime is Infinity, which happens on Android
20520  
20521        if (!this.timeupdateSeen_ || liveCurrentTime === Infinity) {
20522          isBehind = false;
20523        }
20524  
20525        if (isBehind !== this.behindLiveEdge_) {
20526          this.behindLiveEdge_ = isBehind;
20527          this.trigger('liveedgechange');
20528        }
20529      }
20530      /**
20531       * handle a durationchange event on the player
20532       * and start/stop tracking accordingly.
20533       */
20534      ;
20535  
20536      _proto.handleDurationchange = function handleDurationchange() {
20537        if (this.player_.duration() === Infinity && this.liveWindow() >= this.options_.trackingThreshold) {
20538          if (this.player_.options_.liveui) {
20539            this.player_.addClass('vjs-liveui');
20540          }
20541  
20542          this.startTracking();
20543        } else {
20544          this.player_.removeClass('vjs-liveui');
20545          this.stopTracking();
20546        }
20547      }
20548      /**
20549       * start tracking live playback
20550       */
20551      ;
20552  
20553      _proto.startTracking = function startTracking() {
20554        if (this.isTracking()) {
20555          return;
20556        } // If we haven't seen a timeupdate, we need to check whether playback
20557        // began before this component started tracking. This can happen commonly
20558        // when using autoplay.
20559  
20560  
20561        if (!this.timeupdateSeen_) {
20562          this.timeupdateSeen_ = this.player_.hasStarted();
20563        }
20564  
20565        this.trackingInterval_ = this.setInterval(this.trackLive_, UPDATE_REFRESH_INTERVAL);
20566        this.trackLive_();
20567        this.on(this.player_, ['play', 'pause'], this.trackLive_);
20568  
20569        if (!this.timeupdateSeen_) {
20570          this.one(this.player_, 'play', this.handlePlay);
20571          this.one(this.player_, 'timeupdate', this.handleFirstTimeupdate);
20572        } else {
20573          this.on(this.player_, 'seeked', this.handleSeeked);
20574        }
20575      }
20576      /**
20577       * handle the first timeupdate on the player if it wasn't already playing
20578       * when live tracker started tracking.
20579       */
20580      ;
20581  
20582      _proto.handleFirstTimeupdate = function handleFirstTimeupdate() {
20583        this.timeupdateSeen_ = true;
20584        this.on(this.player_, 'seeked', this.handleSeeked);
20585      }
20586      /**
20587       * Keep track of what time a seek starts, and listen for seeked
20588       * to find where a seek ends.
20589       */
20590      ;
20591  
20592      _proto.handleSeeked = function handleSeeked() {
20593        var timeDiff = Math.abs(this.liveCurrentTime() - this.player_.currentTime());
20594        this.seekedBehindLive_ = this.skipNextSeeked_ ? false : timeDiff > 2;
20595        this.skipNextSeeked_ = false;
20596        this.trackLive_();
20597      }
20598      /**
20599       * handle the first play on the player, and make sure that we seek
20600       * right to the live edge.
20601       */
20602      ;
20603  
20604      _proto.handlePlay = function handlePlay() {
20605        this.one(this.player_, 'timeupdate', this.seekToLiveEdge);
20606      }
20607      /**
20608       * Stop tracking, and set all internal variables to
20609       * their initial value.
20610       */
20611      ;
20612  
20613      _proto.reset_ = function reset_() {
20614        this.lastTime_ = -1;
20615        this.pastSeekEnd_ = 0;
20616        this.lastSeekEnd_ = -1;
20617        this.behindLiveEdge_ = true;
20618        this.timeupdateSeen_ = false;
20619        this.seekedBehindLive_ = false;
20620        this.skipNextSeeked_ = false;
20621        this.clearInterval(this.trackingInterval_);
20622        this.trackingInterval_ = null;
20623        this.off(this.player_, ['play', 'pause'], this.trackLive_);
20624        this.off(this.player_, 'seeked', this.handleSeeked);
20625        this.off(this.player_, 'play', this.handlePlay);
20626        this.off(this.player_, 'timeupdate', this.handleFirstTimeupdate);
20627        this.off(this.player_, 'timeupdate', this.seekToLiveEdge);
20628      }
20629      /**
20630       * stop tracking live playback
20631       */
20632      ;
20633  
20634      _proto.stopTracking = function stopTracking() {
20635        if (!this.isTracking()) {
20636          return;
20637        }
20638  
20639        this.reset_();
20640        this.trigger('liveedgechange');
20641      }
20642      /**
20643       * A helper to get the player seekable end
20644       * so that we don't have to null check everywhere
20645       *
20646       * @return {number}
20647       *         The furthest seekable end or Infinity.
20648       */
20649      ;
20650  
20651      _proto.seekableEnd = function seekableEnd() {
20652        var seekable = this.player_.seekable();
20653        var seekableEnds = [];
20654        var i = seekable ? seekable.length : 0;
20655  
20656        while (i--) {
20657          seekableEnds.push(seekable.end(i));
20658        } // grab the furthest seekable end after sorting, or if there are none
20659        // default to Infinity
20660  
20661  
20662        return seekableEnds.length ? seekableEnds.sort()[seekableEnds.length - 1] : Infinity;
20663      }
20664      /**
20665       * A helper to get the player seekable start
20666       * so that we don't have to null check everywhere
20667       *
20668       * @return {number}
20669       *         The earliest seekable start or 0.
20670       */
20671      ;
20672  
20673      _proto.seekableStart = function seekableStart() {
20674        var seekable = this.player_.seekable();
20675        var seekableStarts = [];
20676        var i = seekable ? seekable.length : 0;
20677  
20678        while (i--) {
20679          seekableStarts.push(seekable.start(i));
20680        } // grab the first seekable start after sorting, or if there are none
20681        // default to 0
20682  
20683  
20684        return seekableStarts.length ? seekableStarts.sort()[0] : 0;
20685      }
20686      /**
20687       * Get the live time window aka
20688       * the amount of time between seekable start and
20689       * live current time.
20690       *
20691       * @return {number}
20692       *         The amount of seconds that are seekable in
20693       *         the live video.
20694       */
20695      ;
20696  
20697      _proto.liveWindow = function liveWindow() {
20698        var liveCurrentTime = this.liveCurrentTime();
20699  
20700        if (liveCurrentTime === Infinity) {
20701          return Infinity;
20702        }
20703  
20704        return liveCurrentTime - this.seekableStart();
20705      }
20706      /**
20707       * Determines if the player is live, only checks if this component
20708       * is tracking live playback or not
20709       *
20710       * @return {boolean}
20711       *         Wether liveTracker is tracking
20712       */
20713      ;
20714  
20715      _proto.isLive = function isLive() {
20716        return this.isTracking();
20717      }
20718      /**
20719       * Determines if currentTime is at the live edge and won't fall behind
20720       * on each seekableendchange
20721       *
20722       * @return {boolean}
20723       *         Wether playback is at the live edge
20724       */
20725      ;
20726  
20727      _proto.atLiveEdge = function atLiveEdge() {
20728        return !this.behindLiveEdge();
20729      }
20730      /**
20731       * get what we expect the live current time to be
20732       *
20733       * @return {number}
20734       *         The expected live current time
20735       */
20736      ;
20737  
20738      _proto.liveCurrentTime = function liveCurrentTime() {
20739        return this.pastSeekEnd() + this.seekableEnd();
20740      }
20741      /**
20742       * The number of seconds that have occured after seekable end
20743       * changed. This will be reset to 0 once seekable end changes.
20744       *
20745       * @return {number}
20746       *         Seconds past the current seekable end
20747       */
20748      ;
20749  
20750      _proto.pastSeekEnd = function pastSeekEnd() {
20751        var seekableEnd = this.seekableEnd();
20752  
20753        if (this.lastSeekEnd_ !== -1 && seekableEnd !== this.lastSeekEnd_) {
20754          this.pastSeekEnd_ = 0;
20755        }
20756  
20757        this.lastSeekEnd_ = seekableEnd;
20758        return this.pastSeekEnd_;
20759      }
20760      /**
20761       * If we are currently behind the live edge, aka currentTime will be
20762       * behind on a seekableendchange
20763       *
20764       * @return {boolean}
20765       *         If we are behind the live edge
20766       */
20767      ;
20768  
20769      _proto.behindLiveEdge = function behindLiveEdge() {
20770        return this.behindLiveEdge_;
20771      }
20772      /**
20773       * Wether live tracker is currently tracking or not.
20774       */
20775      ;
20776  
20777      _proto.isTracking = function isTracking() {
20778        return typeof this.trackingInterval_ === 'number';
20779      }
20780      /**
20781       * Seek to the live edge if we are behind the live edge
20782       */
20783      ;
20784  
20785      _proto.seekToLiveEdge = function seekToLiveEdge() {
20786        this.seekedBehindLive_ = false;
vendor: 35,079 bytes, lines 20787-21791
20787  
20788        if (this.atLiveEdge()) {
20789          return;
20790        } // skipNextSeeked_
20791  
20792  
20793        this.skipNextSeeked_ = true;
20794        this.player_.currentTime(this.liveCurrentTime());
20795      }
20796      /**
20797       * Dispose of liveTracker
20798       */
20799      ;
20800  
20801      _proto.dispose = function dispose() {
20802        this.off(document, 'visibilitychange', this.handleVisibilityChange);
20803        this.stopTracking();
20804  
20805        _Component.prototype.dispose.call(this);
20806      };
20807  
20808      return LiveTracker;
20809    }(Component);
20810  
20811    Component.registerComponent('LiveTracker', LiveTracker);
20812  
20813    /**
20814     * This function is used to fire a sourceset when there is something
20815     * similar to `mediaEl.load()` being called. It will try to find the source via
20816     * the `src` attribute and then the `<source>` elements. It will then fire `sourceset`
20817     * with the source that was found or empty string if we cannot know. If it cannot
20818     * find a source then `sourceset` will not be fired.
20819     *
20820     * @param {Html5} tech
20821     *        The tech object that sourceset was setup on
20822     *
20823     * @return {boolean}
20824     *         returns false if the sourceset was not fired and true otherwise.
20825     */
20826  
20827    var sourcesetLoad = function sourcesetLoad(tech) {
20828      var el = tech.el(); // if `el.src` is set, that source will be loaded.
20829  
20830      if (el.hasAttribute('src')) {
20831        tech.triggerSourceset(el.src);
20832        return true;
20833      }
20834      /**
20835       * Since there isn't a src property on the media element, source elements will be used for
20836       * implementing the source selection algorithm. This happens asynchronously and
20837       * for most cases were there is more than one source we cannot tell what source will
20838       * be loaded, without re-implementing the source selection algorithm. At this time we are not
20839       * going to do that. There are three special cases that we do handle here though:
20840       *
20841       * 1. If there are no sources, do not fire `sourceset`.
20842       * 2. If there is only one `<source>` with a `src` property/attribute that is our `src`
20843       * 3. If there is more than one `<source>` but all of them have the same `src` url.
20844       *    That will be our src.
20845       */
20846  
20847  
20848      var sources = tech.$$('source');
20849      var srcUrls = [];
20850      var src = ''; // if there are no sources, do not fire sourceset
20851  
20852      if (!sources.length) {
20853        return false;
20854      } // only count valid/non-duplicate source elements
20855  
20856  
20857      for (var i = 0; i < sources.length; i++) {
20858        var url = sources[i].src;
20859  
20860        if (url && srcUrls.indexOf(url) === -1) {
20861          srcUrls.push(url);
20862        }
20863      } // there were no valid sources
20864  
20865  
20866      if (!srcUrls.length) {
20867        return false;
20868      } // there is only one valid source element url
20869      // use that
20870  
20871  
20872      if (srcUrls.length === 1) {
20873        src = srcUrls[0];
20874      }
20875  
20876      tech.triggerSourceset(src);
20877      return true;
20878    };
20879    /**
20880     * our implementation of an `innerHTML` descriptor for browsers
20881     * that do not have one.
20882     */
20883  
20884  
20885    var innerHTMLDescriptorPolyfill = Object.defineProperty({}, 'innerHTML', {
20886      get: function get() {
20887        return this.cloneNode(true).innerHTML;
20888      },
20889      set: function set(v) {
20890        // make a dummy node to use innerHTML on
20891        var dummy = document.createElement(this.nodeName.toLowerCase()); // set innerHTML to the value provided
20892  
20893        dummy.innerHTML = v; // make a document fragment to hold the nodes from dummy
20894  
20895        var docFrag = document.createDocumentFragment(); // copy all of the nodes created by the innerHTML on dummy
20896        // to the document fragment
20897  
20898        while (dummy.childNodes.length) {
20899          docFrag.appendChild(dummy.childNodes[0]);
20900        } // remove content
20901  
20902  
20903        this.innerText = ''; // now we add all of that html in one by appending the
20904        // document fragment. This is how innerHTML does it.
20905  
20906        window$3.Element.prototype.appendChild.call(this, docFrag); // then return the result that innerHTML's setter would
20907  
20908        return this.innerHTML;
20909      }
20910    });
20911    /**
20912     * Get a property descriptor given a list of priorities and the
20913     * property to get.
20914     */
20915  
20916    var getDescriptor = function getDescriptor(priority, prop) {
20917      var descriptor = {};
20918  
20919      for (var i = 0; i < priority.length; i++) {
20920        descriptor = Object.getOwnPropertyDescriptor(priority[i], prop);
20921  
20922        if (descriptor && descriptor.set && descriptor.get) {
20923          break;
20924        }
20925      }
20926  
20927      descriptor.enumerable = true;
20928      descriptor.configurable = true;
20929      return descriptor;
20930    };
20931  
20932    var getInnerHTMLDescriptor = function getInnerHTMLDescriptor(tech) {
20933      return getDescriptor([tech.el(), window$3.HTMLMediaElement.prototype, window$3.Element.prototype, innerHTMLDescriptorPolyfill], 'innerHTML');
20934    };
20935    /**
20936     * Patches browser internal functions so that we can tell synchronously
20937     * if a `<source>` was appended to the media element. For some reason this
20938     * causes a `sourceset` if the the media element is ready and has no source.
20939     * This happens when:
20940     * - The page has just loaded and the media element does not have a source.
20941     * - The media element was emptied of all sources, then `load()` was called.
20942     *
20943     * It does this by patching the following functions/properties when they are supported:
20944     *
20945     * - `append()` - can be used to add a `<source>` element to the media element
20946     * - `appendChild()` - can be used to add a `<source>` element to the media element
20947     * - `insertAdjacentHTML()` -  can be used to add a `<source>` element to the media element
20948     * - `innerHTML` -  can be used to add a `<source>` element to the media element
20949     *
20950     * @param {Html5} tech
20951     *        The tech object that sourceset is being setup on.
20952     */
20953  
20954  
20955    var firstSourceWatch = function firstSourceWatch(tech) {
20956      var el = tech.el(); // make sure firstSourceWatch isn't setup twice.
20957  
20958      if (el.resetSourceWatch_) {
20959        return;
20960      }
20961  
20962      var old = {};
20963      var innerDescriptor = getInnerHTMLDescriptor(tech);
20964  
20965      var appendWrapper = function appendWrapper(appendFn) {
20966        return function () {
20967          for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) {
20968            args[_key] = arguments[_key];
20969          }
20970  
20971          var retval = appendFn.apply(el, args);
20972          sourcesetLoad(tech);
20973          return retval;
20974        };
20975      };
20976  
20977      ['append', 'appendChild', 'insertAdjacentHTML'].forEach(function (k) {
20978        if (!el[k]) {
20979          return;
20980        } // store the old function
20981  
20982  
20983        old[k] = el[k]; // call the old function with a sourceset if a source
20984        // was loaded
20985  
20986        el[k] = appendWrapper(old[k]);
20987      });
20988      Object.defineProperty(el, 'innerHTML', mergeOptions(innerDescriptor, {
20989        set: appendWrapper(innerDescriptor.set)
20990      }));
20991  
20992      el.resetSourceWatch_ = function () {
20993        el.resetSourceWatch_ = null;
20994        Object.keys(old).forEach(function (k) {
20995          el[k] = old[k];
20996        });
20997        Object.defineProperty(el, 'innerHTML', innerDescriptor);
20998      }; // on the first sourceset, we need to revert our changes
20999  
21000  
21001      tech.one('sourceset', el.resetSourceWatch_);
21002    };
21003    /**
21004     * our implementation of a `src` descriptor for browsers
21005     * that do not have one.
21006     */
21007  
21008  
21009    var srcDescriptorPolyfill = Object.defineProperty({}, 'src', {
21010      get: function get() {
21011        if (this.hasAttribute('src')) {
21012          return getAbsoluteURL(window$3.Element.prototype.getAttribute.call(this, 'src'));
21013        }
21014  
21015        return '';
21016      },
21017      set: function set(v) {
21018        window$3.Element.prototype.setAttribute.call(this, 'src', v);
21019        return v;
21020      }
21021    });
21022  
21023    var getSrcDescriptor = function getSrcDescriptor(tech) {
21024      return getDescriptor([tech.el(), window$3.HTMLMediaElement.prototype, srcDescriptorPolyfill], 'src');
21025    };
21026    /**
21027     * setup `sourceset` handling on the `Html5` tech. This function
21028     * patches the following element properties/functions:
21029     *
21030     * - `src` - to determine when `src` is set
21031     * - `setAttribute()` - to determine when `src` is set
21032     * - `load()` - this re-triggers the source selection algorithm, and can
21033     *              cause a sourceset.
21034     *
21035     * If there is no source when we are adding `sourceset` support or during a `load()`
21036     * we also patch the functions listed in `firstSourceWatch`.
21037     *
21038     * @param {Html5} tech
21039     *        The tech to patch
21040     */
21041  
21042  
21043    var setupSourceset = function setupSourceset(tech) {
21044      if (!tech.featuresSourceset) {
21045        return;
21046      }
21047  
21048      var el = tech.el(); // make sure sourceset isn't setup twice.
21049  
21050      if (el.resetSourceset_) {
21051        return;
21052      }
21053  
21054      var srcDescriptor = getSrcDescriptor(tech);
21055      var oldSetAttribute = el.setAttribute;
21056      var oldLoad = el.load;
21057      Object.defineProperty(el, 'src', mergeOptions(srcDescriptor, {
21058        set: function set(v) {
21059          var retval = srcDescriptor.set.call(el, v); // we use the getter here to get the actual value set on src
21060  
21061          tech.triggerSourceset(el.src);
21062          return retval;
21063        }
21064      }));
21065  
21066      el.setAttribute = function (n, v) {
21067        var retval = oldSetAttribute.call(el, n, v);
21068  
21069        if (/src/i.test(n)) {
21070          tech.triggerSourceset(el.src);
21071        }
21072  
21073        return retval;
21074      };
21075  
21076      el.load = function () {
21077        var retval = oldLoad.call(el); // if load was called, but there was no source to fire
21078        // sourceset on. We have to watch for a source append
21079        // as that can trigger a `sourceset` when the media element
21080        // has no source
21081  
21082        if (!sourcesetLoad(tech)) {
21083          tech.triggerSourceset('');
21084          firstSourceWatch(tech);
21085        }
21086  
21087        return retval;
21088      };
21089  
21090      if (el.currentSrc) {
21091        tech.triggerSourceset(el.currentSrc);
21092      } else if (!sourcesetLoad(tech)) {
21093        firstSourceWatch(tech);
21094      }
21095  
21096      el.resetSourceset_ = function () {
21097        el.resetSourceset_ = null;
21098        el.load = oldLoad;
21099        el.setAttribute = oldSetAttribute;
21100        Object.defineProperty(el, 'src', srcDescriptor);
21101  
21102        if (el.resetSourceWatch_) {
21103          el.resetSourceWatch_();
21104        }
21105      };
21106    };
21107  
21108    /**
21109     * Object.defineProperty but "lazy", which means that the value is only set after
21110     * it retrieved the first time, rather than being set right away.
21111     *
21112     * @param {Object} obj the object to set the property on
21113     * @param {string} key the key for the property to set
21114     * @param {Function} getValue the function used to get the value when it is needed.
21115     * @param {boolean} setter wether a setter shoould be allowed or not
21116     */
21117    var defineLazyProperty = function defineLazyProperty(obj, key, getValue, setter) {
21118      if (setter === void 0) {
21119        setter = true;
21120      }
21121  
21122      var set = function set(value) {
21123        return Object.defineProperty(obj, key, {
21124          value: value,
21125          enumerable: true,
21126          writable: true
21127        });
21128      };
21129  
21130      var options = {
21131        configurable: true,
21132        enumerable: true,
21133        get: function get() {
21134          var value = getValue();
21135          set(value);
21136          return value;
21137        }
21138      };
21139  
21140      if (setter) {
21141        options.set = set;
21142      }
21143  
21144      return Object.defineProperty(obj, key, options);
21145    };
21146  
21147    /**
21148     * HTML5 Media Controller - Wrapper for HTML5 Media API
21149     *
21150     * @mixes Tech~SourceHandlerAdditions
21151     * @extends Tech
21152     */
21153  
21154    var Html5 = /*#__PURE__*/function (_Tech) {
21155      inheritsLoose(Html5, _Tech);
21156  
21157      /**
21158      * Create an instance of this Tech.
21159      *
21160      * @param {Object} [options]
21161      *        The key/value store of player options.
21162      *
21163      * @param {Component~ReadyCallback} ready
21164      *        Callback function to call when the `HTML5` Tech is ready.
21165      */
21166      function Html5(options, ready) {
21167        var _this;
21168  
21169        _this = _Tech.call(this, options, ready) || this;
21170        var source = options.source;
21171        var crossoriginTracks = false; // Set the source if one is provided
21172        // 1) Check if the source is new (if not, we want to keep the original so playback isn't interrupted)
21173        // 2) Check to see if the network state of the tag was failed at init, and if so, reset the source
21174        // anyway so the error gets fired.
21175  
21176        if (source && (_this.el_.currentSrc !== source.src || options.tag && options.tag.initNetworkState_ === 3)) {
21177          _this.setSource(source);
21178        } else {
21179          _this.handleLateInit_(_this.el_);
21180        } // setup sourceset after late sourceset/init
21181  
21182  
21183        if (options.enableSourceset) {
21184          _this.setupSourcesetHandling_();
21185        }
21186  
21187        if (_this.el_.hasChildNodes()) {
21188          var nodes = _this.el_.childNodes;
21189          var nodesLength = nodes.length;
21190          var removeNodes = [];
21191  
21192          while (nodesLength--) {
21193            var node = nodes[nodesLength];
21194            var nodeName = node.nodeName.toLowerCase();
21195  
21196            if (nodeName === 'track') {
21197              if (!_this.featuresNativeTextTracks) {
21198                // Empty video tag tracks so the built-in player doesn't use them also.
21199                // This may not be fast enough to stop HTML5 browsers from reading the tags
21200                // so we'll need to turn off any default tracks if we're manually doing
21201                // captions and subtitles. videoElement.textTracks
21202                removeNodes.push(node);
21203              } else {
21204                // store HTMLTrackElement and TextTrack to remote list
21205                _this.remoteTextTrackEls().addTrackElement_(node);
21206  
21207                _this.remoteTextTracks().addTrack(node.track);
21208  
21209                _this.textTracks().addTrack(node.track);
21210  
21211                if (!crossoriginTracks && !_this.el_.hasAttribute('crossorigin') && isCrossOrigin(node.src)) {
21212                  crossoriginTracks = true;
21213                }
21214              }
21215            }
21216          }
21217  
21218          for (var i = 0; i < removeNodes.length; i++) {
21219            _this.el_.removeChild(removeNodes[i]);
21220          }
21221        }
21222  
21223        _this.proxyNativeTracks_();
21224  
21225        if (_this.featuresNativeTextTracks && crossoriginTracks) {
21226          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.');
21227        } // prevent iOS Safari from disabling metadata text tracks during native playback
21228  
21229  
21230        _this.restoreMetadataTracksInIOSNativePlayer_(); // Determine if native controls should be used
21231        // Our goal should be to get the custom controls on mobile solid everywhere
21232        // so we can remove this all together. Right now this will block custom
21233        // controls on touch enabled laptops like the Chrome Pixel
21234  
21235  
21236        if ((TOUCH_ENABLED || IS_IPHONE || IS_NATIVE_ANDROID) && options.nativeControlsForTouch === true) {
21237          _this.setControls(true);
21238        } // on iOS, we want to proxy `webkitbeginfullscreen` and `webkitendfullscreen`
21239        // into a `fullscreenchange` event
21240  
21241  
21242        _this.proxyWebkitFullscreen_();
21243  
21244        _this.triggerReady();
21245  
21246        return _this;
21247      }
21248      /**
21249       * Dispose of `HTML5` media element and remove all tracks.
21250       */
21251  
21252  
21253      var _proto = Html5.prototype;
21254  
21255      _proto.dispose = function dispose() {
21256        if (this.el_ && this.el_.resetSourceset_) {
21257          this.el_.resetSourceset_();
21258        }
21259  
21260        Html5.disposeMediaElement(this.el_);
21261        this.options_ = null; // tech will handle clearing of the emulated track list
21262  
21263        _Tech.prototype.dispose.call(this);
21264      }
21265      /**
21266       * Modify the media element so that we can detect when
21267       * the source is changed. Fires `sourceset` just after the source has changed
21268       */
21269      ;
21270  
21271      _proto.setupSourcesetHandling_ = function setupSourcesetHandling_() {
21272        setupSourceset(this);
21273      }
21274      /**
21275       * When a captions track is enabled in the iOS Safari native player, all other
21276       * tracks are disabled (including metadata tracks), which nulls all of their
21277       * associated cue points. This will restore metadata tracks to their pre-fullscreen
21278       * state in those cases so that cue points are not needlessly lost.
21279       *
21280       * @private
21281       */
21282      ;
21283  
21284      _proto.restoreMetadataTracksInIOSNativePlayer_ = function restoreMetadataTracksInIOSNativePlayer_() {
21285        var textTracks = this.textTracks();
21286        var metadataTracksPreFullscreenState; // captures a snapshot of every metadata track's current state
21287  
21288        var takeMetadataTrackSnapshot = function takeMetadataTrackSnapshot() {
21289          metadataTracksPreFullscreenState = [];
21290  
21291          for (var i = 0; i < textTracks.length; i++) {
21292            var track = textTracks[i];
21293  
21294            if (track.kind === 'metadata') {
21295              metadataTracksPreFullscreenState.push({
21296                track: track,
21297                storedMode: track.mode
21298              });
21299            }
21300          }
21301        }; // snapshot each metadata track's initial state, and update the snapshot
21302        // each time there is a track 'change' event
21303  
21304  
21305        takeMetadataTrackSnapshot();
21306        textTracks.addEventListener('change', takeMetadataTrackSnapshot);
21307        this.on('dispose', function () {
21308          return textTracks.removeEventListener('change', takeMetadataTrackSnapshot);
21309        });
21310  
21311        var restoreTrackMode = function restoreTrackMode() {
21312          for (var i = 0; i < metadataTracksPreFullscreenState.length; i++) {
21313            var storedTrack = metadataTracksPreFullscreenState[i];
21314  
21315            if (storedTrack.track.mode === 'disabled' && storedTrack.track.mode !== storedTrack.storedMode) {
21316              storedTrack.track.mode = storedTrack.storedMode;
21317            }
21318          } // we only want this handler to be executed on the first 'change' event
21319  
21320  
21321          textTracks.removeEventListener('change', restoreTrackMode);
21322        }; // when we enter fullscreen playback, stop updating the snapshot and
21323        // restore all track modes to their pre-fullscreen state
21324  
21325  
21326        this.on('webkitbeginfullscreen', function () {
21327          textTracks.removeEventListener('change', takeMetadataTrackSnapshot); // remove the listener before adding it just in case it wasn't previously removed
21328  
21329          textTracks.removeEventListener('change', restoreTrackMode);
21330          textTracks.addEventListener('change', restoreTrackMode);
21331        }); // start updating the snapshot again after leaving fullscreen
21332  
21333        this.on('webkitendfullscreen', function () {
21334          // remove the listener before adding it just in case it wasn't previously removed
21335          textTracks.removeEventListener('change', takeMetadataTrackSnapshot);
21336          textTracks.addEventListener('change', takeMetadataTrackSnapshot); // remove the restoreTrackMode handler in case it wasn't triggered during fullscreen playback
21337  
21338          textTracks.removeEventListener('change', restoreTrackMode);
21339        });
21340      }
21341      /**
21342       * Attempt to force override of tracks for the given type
21343       *
21344       * @param {string} type - Track type to override, possible values include 'Audio',
21345       * 'Video', and 'Text'.
21346       * @param {boolean} override - If set to true native audio/video will be overridden,
21347       * otherwise native audio/video will potentially be used.
21348       * @private
21349       */
21350      ;
21351  
21352      _proto.overrideNative_ = function overrideNative_(type, override) {
21353        var _this2 = this;
21354  
21355        // If there is no behavioral change don't add/remove listeners
21356        if (override !== this["featuresNative" + type + "Tracks"]) {
21357          return;
21358        }
21359  
21360        var lowerCaseType = type.toLowerCase();
21361  
21362        if (this[lowerCaseType + "TracksListeners_"]) {
21363          Object.keys(this[lowerCaseType + "TracksListeners_"]).forEach(function (eventName) {
21364            var elTracks = _this2.el()[lowerCaseType + "Tracks"];
21365  
21366            elTracks.removeEventListener(eventName, _this2[lowerCaseType + "TracksListeners_"][eventName]);
21367          });
21368        }
21369  
21370        this["featuresNative" + type + "Tracks"] = !override;
21371        this[lowerCaseType + "TracksListeners_"] = null;
21372        this.proxyNativeTracksForType_(lowerCaseType);
21373      }
21374      /**
21375       * Attempt to force override of native audio tracks.
21376       *
21377       * @param {boolean} override - If set to true native audio will be overridden,
21378       * otherwise native audio will potentially be used.
21379       */
21380      ;
21381  
21382      _proto.overrideNativeAudioTracks = function overrideNativeAudioTracks(override) {
21383        this.overrideNative_('Audio', override);
21384      }
21385      /**
21386       * Attempt to force override of native video tracks.
21387       *
21388       * @param {boolean} override - If set to true native video will be overridden,
21389       * otherwise native video will potentially be used.
21390       */
21391      ;
21392  
21393      _proto.overrideNativeVideoTracks = function overrideNativeVideoTracks(override) {
21394        this.overrideNative_('Video', override);
21395      }
21396      /**
21397       * Proxy native track list events for the given type to our track
21398       * lists if the browser we are playing in supports that type of track list.
21399       *
21400       * @param {string} name - Track type; values include 'audio', 'video', and 'text'
21401       * @private
21402       */
21403      ;
21404  
21405      _proto.proxyNativeTracksForType_ = function proxyNativeTracksForType_(name) {
21406        var _this3 = this;
21407  
21408        var props = NORMAL[name];
21409        var elTracks = this.el()[props.getterName];
21410        var techTracks = this[props.getterName]();
21411  
21412        if (!this["featuresNative" + props.capitalName + "Tracks"] || !elTracks || !elTracks.addEventListener) {
21413          return;
21414        }
21415  
21416        var listeners = {
21417          change: function change(e) {
21418            var event = {
21419              type: 'change',
21420              target: techTracks,
21421              currentTarget: techTracks,
21422              srcElement: techTracks
21423            };
21424            techTracks.trigger(event); // if we are a text track change event, we should also notify the
21425            // remote text track list. This can potentially cause a false positive
21426            // if we were to get a change event on a non-remote track and
21427            // we triggered the event on the remote text track list which doesn't
21428            // contain that track. However, best practices mean looping through the
21429            // list of tracks and searching for the appropriate mode value, so,
21430            // this shouldn't pose an issue
21431  
21432            if (name === 'text') {
21433              _this3[REMOTE.remoteText.getterName]().trigger(event);
21434            }
21435          },
21436          addtrack: function addtrack(e) {
21437            techTracks.addTrack(e.track);
21438          },
21439          removetrack: function removetrack(e) {
21440            techTracks.removeTrack(e.track);
21441          }
21442        };
21443  
21444        var removeOldTracks = function removeOldTracks() {
21445          var removeTracks = [];
21446  
21447          for (var i = 0; i < techTracks.length; i++) {
21448            var found = false;
21449  
21450            for (var j = 0; j < elTracks.length; j++) {
21451              if (elTracks[j] === techTracks[i]) {
21452                found = true;
21453                break;
21454              }
21455            }
21456  
21457            if (!found) {
21458              removeTracks.push(techTracks[i]);
21459            }
21460          }
21461  
21462          while (removeTracks.length) {
21463            techTracks.removeTrack(removeTracks.shift());
21464          }
21465        };
21466  
21467        this[props.getterName + 'Listeners_'] = listeners;
21468        Object.keys(listeners).forEach(function (eventName) {
21469          var listener = listeners[eventName];
21470          elTracks.addEventListener(eventName, listener);
21471  
21472          _this3.on('dispose', function (e) {
21473            return elTracks.removeEventListener(eventName, listener);
21474          });
21475        }); // Remove (native) tracks that are not used anymore
21476  
21477        this.on('loadstart', removeOldTracks);
21478        this.on('dispose', function (e) {
21479          return _this3.off('loadstart', removeOldTracks);
21480        });
21481      }
21482      /**
21483       * Proxy all native track list events to our track lists if the browser we are playing
21484       * in supports that type of track list.
21485       *
21486       * @private
21487       */
21488      ;
21489  
21490      _proto.proxyNativeTracks_ = function proxyNativeTracks_() {
21491        var _this4 = this;
21492  
21493        NORMAL.names.forEach(function (name) {
21494          _this4.proxyNativeTracksForType_(name);
21495        });
21496      }
21497      /**
21498       * Create the `Html5` Tech's DOM element.
21499       *
21500       * @return {Element}
21501       *         The element that gets created.
21502       */
21503      ;
21504  
21505      _proto.createEl = function createEl() {
21506        var el = this.options_.tag; // Check if this browser supports moving the element into the box.
21507        // On the iPhone video will break if you move the element,
21508        // So we have to create a brand new element.
21509        // If we ingested the player div, we do not need to move the media element.
21510  
21511        if (!el || !(this.options_.playerElIngest || this.movingMediaElementInDOM)) {
21512          // If the original tag is still there, clone and remove it.
21513          if (el) {
21514            var clone = el.cloneNode(true);
21515  
21516            if (el.parentNode) {
21517              el.parentNode.insertBefore(clone, el);
21518            }
21519  
21520            Html5.disposeMediaElement(el);
21521            el = clone;
21522          } else {
21523            el = document.createElement('video'); // determine if native controls should be used
21524  
21525            var tagAttributes = this.options_.tag && getAttributes(this.options_.tag);
21526            var attributes = mergeOptions({}, tagAttributes);
21527  
21528            if (!TOUCH_ENABLED || this.options_.nativeControlsForTouch !== true) {
21529              delete attributes.controls;
21530            }
21531  
21532            setAttributes(el, assign(attributes, {
21533              id: this.options_.techId,
21534              "class": 'vjs-tech'
21535            }));
21536          }
21537  
21538          el.playerId = this.options_.playerId;
21539        }
21540  
21541        if (typeof this.options_.preload !== 'undefined') {
21542          setAttribute(el, 'preload', this.options_.preload);
21543        } // Update specific tag settings, in case they were overridden
21544        // `autoplay` has to be *last* so that `muted` and `playsinline` are present
21545        // when iOS/Safari or other browsers attempt to autoplay.
21546  
21547  
21548        var settingsAttrs = ['loop', 'muted', 'playsinline', 'autoplay'];
21549  
21550        for (var i = 0; i < settingsAttrs.length; i++) {
21551          var attr = settingsAttrs[i];
21552          var value = this.options_[attr];
21553  
21554          if (typeof value !== 'undefined') {
21555            if (value) {
21556              setAttribute(el, attr, attr);
21557            } else {
21558              removeAttribute(el, attr);
21559            }
21560  
21561            el[attr] = value;
21562          }
21563        }
21564  
21565        return el;
21566      }
21567      /**
21568       * This will be triggered if the loadstart event has already fired, before videojs was
21569       * ready. Two known examples of when this can happen are:
21570       * 1. If we're loading the playback object after it has started loading
21571       * 2. The media is already playing the (often with autoplay on) then
21572       *
21573       * This function will fire another loadstart so that videojs can catchup.
21574       *
21575       * @fires Tech#loadstart
21576       *
21577       * @return {undefined}
21578       *         returns nothing.
21579       */
21580      ;
21581  
21582      _proto.handleLateInit_ = function handleLateInit_(el) {
21583        if (el.networkState === 0 || el.networkState === 3) {
21584          // The video element hasn't started loading the source yet
21585          // or didn't find a source
21586          return;
21587        }
21588  
21589        if (el.readyState === 0) {
21590          // NetworkState is set synchronously BUT loadstart is fired at the
21591          // end of the current stack, usually before setInterval(fn, 0).
21592          // So at this point we know loadstart may have already fired or is
21593          // about to fire, and either way the player hasn't seen it yet.
21594          // We don't want to fire loadstart prematurely here and cause a
21595          // double loadstart so we'll wait and see if it happens between now
21596          // and the next loop, and fire it if not.
21597          // HOWEVER, we also want to make sure it fires before loadedmetadata
21598          // which could also happen between now and the next loop, so we'll
21599          // watch for that also.
21600          var loadstartFired = false;
21601  
21602          var setLoadstartFired = function setLoadstartFired() {
21603            loadstartFired = true;
21604          };
21605  
21606          this.on('loadstart', setLoadstartFired);
21607  
21608          var triggerLoadstart = function triggerLoadstart() {
21609            // We did miss the original loadstart. Make sure the player
21610            // sees loadstart before loadedmetadata
21611            if (!loadstartFired) {
21612              this.trigger('loadstart');
21613            }
21614          };
21615  
21616          this.on('loadedmetadata', triggerLoadstart);
21617          this.ready(function () {
21618            this.off('loadstart', setLoadstartFired);
21619            this.off('loadedmetadata', triggerLoadstart);
21620  
21621            if (!loadstartFired) {
21622              // We did miss the original native loadstart. Fire it now.
21623              this.trigger('loadstart');
21624            }
21625          });
21626          return;
21627        } // From here on we know that loadstart already fired and we missed it.
21628        // The other readyState events aren't as much of a problem if we double
21629        // them, so not going to go to as much trouble as loadstart to prevent
21630        // that unless we find reason to.
21631  
21632  
21633        var eventsToTrigger = ['loadstart']; // loadedmetadata: newly equal to HAVE_METADATA (1) or greater
21634  
21635        eventsToTrigger.push('loadedmetadata'); // loadeddata: newly increased to HAVE_CURRENT_DATA (2) or greater
21636  
21637        if (el.readyState >= 2) {
21638          eventsToTrigger.push('loadeddata');
21639        } // canplay: newly increased to HAVE_FUTURE_DATA (3) or greater
21640  
21641  
21642        if (el.readyState >= 3) {
21643          eventsToTrigger.push('canplay');
21644        } // canplaythrough: newly equal to HAVE_ENOUGH_DATA (4)
21645  
21646  
21647        if (el.readyState >= 4) {
21648          eventsToTrigger.push('canplaythrough');
21649        } // We still need to give the player time to add event listeners
21650  
21651  
21652        this.ready(function () {
21653          eventsToTrigger.forEach(function (type) {
21654            this.trigger(type);
21655          }, this);
21656        });
21657      }
21658      /**
21659       * Set current time for the `HTML5` tech.
21660       *
21661       * @param {number} seconds
21662       *        Set the current time of the media to this.
21663       */
21664      ;
21665  
21666      _proto.setCurrentTime = function setCurrentTime(seconds) {
21667        try {
21668          this.el_.currentTime = seconds;
21669        } catch (e) {
21670          log(e, 'Video is not ready. (Video.js)'); // this.warning(VideoJS.warnings.videoNotReady);
21671        }
21672      }
21673      /**
21674       * Get the current duration of the HTML5 media element.
21675       *
21676       * @return {number}
21677       *         The duration of the media or 0 if there is no duration.
21678       */
21679      ;
21680  
21681      _proto.duration = function duration() {
21682        var _this5 = this;
21683  
21684        // Android Chrome will report duration as Infinity for VOD HLS until after
21685        // playback has started, which triggers the live display erroneously.
21686        // Return NaN if playback has not started and trigger a durationupdate once
21687        // the duration can be reliably known.
21688        if (this.el_.duration === Infinity && IS_ANDROID && IS_CHROME && this.el_.currentTime === 0) {
21689          // Wait for the first `timeupdate` with currentTime > 0 - there may be
21690          // several with 0
21691          var checkProgress = function checkProgress() {
21692            if (_this5.el_.currentTime > 0) {
21693              // Trigger durationchange for genuinely live video
21694              if (_this5.el_.duration === Infinity) {
21695                _this5.trigger('durationchange');
21696              }
21697  
21698              _this5.off('timeupdate', checkProgress);
21699            }
21700          };
21701  
21702          this.on('timeupdate', checkProgress);
21703          return NaN;
21704        }
21705  
21706        return this.el_.duration || NaN;
21707      }
21708      /**
21709       * Get the current width of the HTML5 media element.
21710       *
21711       * @return {number}
21712       *         The width of the HTML5 media element.
21713       */
21714      ;
21715  
21716      _proto.width = function width() {
21717        return this.el_.offsetWidth;
21718      }
21719      /**
21720       * Get the current height of the HTML5 media element.
21721       *
21722       * @return {number}
21723       *         The height of the HTML5 media element.
21724       */
21725      ;
21726  
21727      _proto.height = function height() {
21728        return this.el_.offsetHeight;
21729      }
21730      /**
21731       * Proxy iOS `webkitbeginfullscreen` and `webkitendfullscreen` into
21732       * `fullscreenchange` event.
21733       *
21734       * @private
21735       * @fires fullscreenchange
21736       * @listens webkitendfullscreen
21737       * @listens webkitbeginfullscreen
21738       * @listens webkitbeginfullscreen
21739       */
21740      ;
21741  
21742      _proto.proxyWebkitFullscreen_ = function proxyWebkitFullscreen_() {
21743        var _this6 = this;
21744  
21745        if (!('webkitDisplayingFullscreen' in this.el_)) {
21746          return;
21747        }
21748  
21749        var endFn = function endFn() {
21750          this.trigger('fullscreenchange', {
21751            isFullscreen: false
21752          });
21753        };
21754  
21755        var beginFn = function beginFn() {
21756          if ('webkitPresentationMode' in this.el_ && this.el_.webkitPresentationMode !== 'picture-in-picture') {
21757            this.one('webkitendfullscreen', endFn);
21758            this.trigger('fullscreenchange', {
21759              isFullscreen: true,
21760              // set a flag in case another tech triggers fullscreenchange
21761              nativeIOSFullscreen: true
21762            });
21763          }
21764        };
21765  
21766        this.on('webkitbeginfullscreen', beginFn);
21767        this.on('dispose', function () {
21768          _this6.off('webkitbeginfullscreen', beginFn);
21769  
21770          _this6.off('webkitendfullscreen', endFn);
21771        });
21772      }
21773      /**
21774       * Check if fullscreen is supported on the current playback device.
21775       *
21776       * @return {boolean}
21777       *         - True if fullscreen is supported.
21778       *         - False if fullscreen is not supported.
21779       */
21780      ;
21781  
21782      _proto.supportsFullScreen = function supportsFullScreen() {
21783        if (typeof this.el_.webkitEnterFullScreen === 'function') {
21784          var userAgent = window$3.navigator && window$3.navigator.userAgent || ''; // Seems to be broken in Chromium/Chrome && Safari in Leopard
21785  
21786          if (/Android/.test(userAgent) || !/Chrome|Mac OS X 10.5/.test(userAgent)) {
21787            return true;
21788          }
21789        }
21790  
21791        return false;
21792      }
21793      /**
21794       * Request that the `HTML5` Tech enter fullscreen.
21795       */
21796      ;
21797  
21798      _proto.enterFullScreen = function enterFullScreen() {
21799        var video = this.el_;
21800  
21801        if (video.paused && video.networkState <= video.HAVE_METADATA) {
21802          // attempt to prime the video element for programmatic access
21803          // this isn't necessary on the desktop but shouldn't hurt
21804          silencePromise(this.el_.play()); // playing and pausing synchronously during the transition to fullscreen
21805          // can get iOS ~6.1 devices into a play/pause loop
21806  
21807          this.setTimeout(function () {
21808            video.pause();
21809  
21810            try {
21811              video.webkitEnterFullScreen();
21812            } catch (e) {
21813              this.trigger('fullscreenerror', e);
21814            }
21815          }, 0);
21816        } else {
21817          try {
21818            video.webkitEnterFullScreen();
21819          } catch (e) {
21820            this.trigger('fullscreenerror', e);
21821          }
21822        }
21823      }
21824      /**
21825       * Request that the `HTML5` Tech exit fullscreen.
21826       */
21827      ;
21828  
21829      _proto.exitFullScreen = function exitFullScreen() {
21830        if (!this.el_.webkitDisplayingFullscreen) {
21831          this.trigger('fullscreenerror', new Error('The video is not fullscreen'));
21832          return;
21833        }
21834  
21835        this.el_.webkitExitFullScreen();
21836      }
21837      /**
21838       * Create a floating video window always on top of other windows so that users may
21839       * continue consuming media while they interact with other content sites, or
21840       * applications on their device.
21841       *
21842       * @see [Spec]{@link https://wicg.github.io/picture-in-picture}
21843       *
21844       * @return {Promise}
21845       *         A promise with a Picture-in-Picture window.
21846       */
21847      ;
21848  
21849      _proto.requestPictureInPicture = function requestPictureInPicture() {
21850        return this.el_.requestPictureInPicture();
21851      }
21852      /**
21853       * A getter/setter for the `Html5` Tech's source object.
21854       * > Note: Please use {@link Html5#setSource}
21855       *
21856       * @param {Tech~SourceObject} [src]
21857       *        The source object you want to set on the `HTML5` techs element.
21858       *
21859       * @return {Tech~SourceObject|undefined}
21860       *         - The current source object when a source is not passed in.
21861       *         - undefined when setting
21862       *
21863       * @deprecated Since version 5.
21864       */
21865      ;
21866  
21867      _proto.src = function src(_src) {
21868        if (_src === undefined) {
21869          return this.el_.src;
21870        } // Setting src through `src` instead of `setSrc` will be deprecated
21871  
21872  
21873        this.setSrc(_src);
21874      }
21875      /**
21876       * Reset the tech by removing all sources and then calling
21877       * {@link Html5.resetMediaElement}.
21878       */
21879      ;
21880  
21881      _proto.reset = function reset() {
21882        Html5.resetMediaElement(this.el_);
21883      }
21884      /**
21885       * Get the current source on the `HTML5` Tech. Falls back to returning the source from
21886       * the HTML5 media element.
21887       *
21888       * @return {Tech~SourceObject}
21889       *         The current source object from the HTML5 tech. With a fallback to the
21890       *         elements source.
21891       */
21892      ;
21893  
21894      _proto.currentSrc = function currentSrc() {
21895        if (this.currentSource_) {
21896          return this.currentSource_.src;
21897        }
21898  
21899        return this.el_.currentSrc;
21900      }
21901      /**
21902       * Set controls attribute for the HTML5 media Element.
21903       *
21904       * @param {string} val
21905       *        Value to set the controls attribute to
21906       */
21907      ;
21908  
21909      _proto.setControls = function setControls(val) {
21910        this.el_.controls = !!val;
21911      }
21912      /**
21913       * Create and returns a remote {@link TextTrack} object.
21914       *
21915       * @param {string} kind
21916       *        `TextTrack` kind (subtitles, captions, descriptions, chapters, or metadata)
21917       *
21918       * @param {string} [label]
21919       *        Label to identify the text track
21920       *
21921       * @param {string} [language]
21922       *        Two letter language abbreviation
21923       *
21924       * @return {TextTrack}
21925       *         The TextTrack that gets created.
21926       */
21927      ;
21928  
21929      _proto.addTextTrack = function addTextTrack(kind, label, language) {
21930        if (!this.featuresNativeTextTracks) {
21931          return _Tech.prototype.addTextTrack.call(this, kind, label, language);
21932        }
21933  
21934        return this.el_.addTextTrack(kind, label, language);
21935      }
21936      /**
21937       * Creates either native TextTrack or an emulated TextTrack depending
21938       * on the value of `featuresNativeTextTracks`
21939       *
21940       * @param {Object} options
21941       *        The object should contain the options to initialize the TextTrack with.
21942       *
21943       * @param {string} [options.kind]
21944       *        `TextTrack` kind (subtitles, captions, descriptions, chapters, or metadata).
21945       *
21946       * @param {string} [options.label]
21947       *        Label to identify the text track
21948       *
21949       * @param {string} [options.language]
21950       *        Two letter language abbreviation.
21951       *
21952       * @param {boolean} [options.default]
21953       *        Default this track to on.
21954       *
21955       * @param {string} [options.id]
21956       *        The internal id to assign this track.
21957       *
21958       * @param {string} [options.src]
21959       *        A source url for the track.
21960       *
21961       * @return {HTMLTrackElement}
21962       *         The track element that gets created.
21963       */
21964      ;
21965  
21966      _proto.createRemoteTextTrack = function createRemoteTextTrack(options) {
21967        if (!this.featuresNativeTextTracks) {
21968          return _Tech.prototype.createRemoteTextTrack.call(this, options);
21969        }
21970  
21971        var htmlTrackElement = document.createElement('track');
21972  
21973        if (options.kind) {
21974          htmlTrackElement.kind = options.kind;
21975        }
21976  
21977        if (options.label) {
21978          htmlTrackElement.label = options.label;
21979        }
21980  
21981        if (options.language || options.srclang) {
21982          htmlTrackElement.srclang = options.language || options.srclang;
21983        }
21984  
21985        if (options["default"]) {
21986          htmlTrackElement["default"] = options["default"];
21987        }
21988  
21989        if (options.id) {
21990          htmlTrackElement.id = options.id;
21991        }
21992  
21993        if (options.src) {
21994          htmlTrackElement.src = options.src;
21995        }
21996  
21997        return htmlTrackElement;
21998      }
21999      /**
22000       * Creates a remote text track object and returns an html track element.
22001       *
22002       * @param {Object} options The object should contain values for
22003       * kind, language, label, and src (location of the WebVTT file)
22004       * @param {boolean} [manualCleanup=true] if set to false, the TextTrack will be
22005       * automatically removed from the video element whenever the source changes
22006       * @return {HTMLTrackElement} An Html Track Element.
22007       * This can be an emulated {@link HTMLTrackElement} or a native one.
22008       * @deprecated The default value of the "manualCleanup" parameter will default
22009       * to "false" in upcoming versions of Video.js
22010       */
22011      ;
22012  
22013      _proto.addRemoteTextTrack = function addRemoteTextTrack(options, manualCleanup) {
22014        var htmlTrackElement = _Tech.prototype.addRemoteTextTrack.call(this, options, manualCleanup);
22015  
22016        if (this.featuresNativeTextTracks) {
22017          this.el().appendChild(htmlTrackElement);
22018        }
22019  
22020        return htmlTrackElement;
22021      }
22022      /**
22023       * Remove remote `TextTrack` from `TextTrackList` object
22024       *
22025       * @param {TextTrack} track
22026       *        `TextTrack` object to remove
22027       */
22028      ;
22029  
22030      _proto.removeRemoteTextTrack = function removeRemoteTextTrack(track) {
22031        _Tech.prototype.removeRemoteTextTrack.call(this, track);
22032  
22033        if (this.featuresNativeTextTracks) {
22034          var tracks = this.$$('track');
22035          var i = tracks.length;
22036  
22037          while (i--) {
22038            if (track === tracks[i] || track === tracks[i].track) {
22039              this.el().removeChild(tracks[i]);
22040            }
22041          }
22042        }
22043      }
22044      /**
22045       * Gets available media playback quality metrics as specified by the W3C's Media
22046       * Playback Quality API.
22047       *
22048       * @see [Spec]{@link https://wicg.github.io/media-playback-quality}
22049       *
22050       * @return {Object}
22051       *         An object with supported media playback quality metrics
22052       */
22053      ;
22054  
22055      _proto.getVideoPlaybackQuality = function getVideoPlaybackQuality() {
22056        if (typeof this.el().getVideoPlaybackQuality === 'function') {
22057          return this.el().getVideoPlaybackQuality();
22058        }
22059  
22060        var videoPlaybackQuality = {};
22061  
22062        if (typeof this.el().webkitDroppedFrameCount !== 'undefined' && typeof this.el().webkitDecodedFrameCount !== 'undefined') {
22063          videoPlaybackQuality.droppedVideoFrames = this.el().webkitDroppedFrameCount;
22064          videoPlaybackQuality.totalVideoFrames = this.el().webkitDecodedFrameCount;
22065        }
22066  
22067        if (window$3.performance && typeof window$3.performance.now === 'function') {
22068          videoPlaybackQuality.creationTime = window$3.performance.now();
22069        } else if (window$3.performance && window$3.performance.timing && typeof window$3.performance.timing.navigationStart === 'number') {
22070          videoPlaybackQuality.creationTime = window$3.Date.now() - window$3.performance.timing.navigationStart;
22071        }
22072  
22073        return videoPlaybackQuality;
22074      };
22075  
22076      return Html5;
22077    }(Tech);
22078    /* HTML5 Support Testing ---------------------------------------------------- */
22079  
22080    /**
22081     * Element for testing browser HTML5 media capabilities
22082     *
22083     * @type {Element}
22084     * @constant
22085     * @private
22086     */
22087  
22088  
22089    defineLazyProperty(Html5, 'TEST_VID', function () {
22090      if (!isReal()) {
22091        return;
22092      }
22093  
22094      var video = document.createElement('video');
22095      var track = document.createElement('track');
22096      track.kind = 'captions';
22097      track.srclang = 'en';
22098      track.label = 'English';
22099      video.appendChild(track);
22100      return video;
22101    });
22102    /**
22103     * Check if HTML5 media is supported by this browser/device.
22104     *
22105     * @return {boolean}
22106     *         - True if HTML5 media is supported.
22107     *         - False if HTML5 media is not supported.
22108     */
22109  
22110    Html5.isSupported = function () {
vendor: 4,248 bytes, lines 22111-22254
22111      // IE with no Media Player is a LIAR! (#984)
22112      try {
22113        Html5.TEST_VID.volume = 0.5;
22114      } catch (e) {
22115        return false;
22116      }
22117  
22118      return !!(Html5.TEST_VID && Html5.TEST_VID.canPlayType);
22119    };
22120    /**
22121     * Check if the tech can support the given type
22122     *
22123     * @param {string} type
22124     *        The mimetype to check
22125     * @return {string} 'probably', 'maybe', or '' (empty string)
22126     */
22127  
22128  
22129    Html5.canPlayType = function (type) {
22130      return Html5.TEST_VID.canPlayType(type);
22131    };
22132    /**
22133     * Check if the tech can support the given source
22134     *
22135     * @param {Object} srcObj
22136     *        The source object
22137     * @param {Object} options
22138     *        The options passed to the tech
22139     * @return {string} 'probably', 'maybe', or '' (empty string)
22140     */
22141  
22142  
22143    Html5.canPlaySource = function (srcObj, options) {
22144      return Html5.canPlayType(srcObj.type);
22145    };
22146    /**
22147     * Check if the volume can be changed in this browser/device.
22148     * Volume cannot be changed in a lot of mobile devices.
22149     * Specifically, it can't be changed from 1 on iOS.
22150     *
22151     * @return {boolean}
22152     *         - True if volume can be controlled
22153     *         - False otherwise
22154     */
22155  
22156  
22157    Html5.canControlVolume = function () {
22158      // IE will error if Windows Media Player not installed #3315
22159      try {
22160        var volume = Html5.TEST_VID.volume;
22161        Html5.TEST_VID.volume = volume / 2 + 0.1;
22162        return volume !== Html5.TEST_VID.volume;
22163      } catch (e) {
22164        return false;
22165      }
22166    };
22167    /**
22168     * Check if the volume can be muted in this browser/device.
22169     * Some devices, e.g. iOS, don't allow changing volume
22170     * but permits muting/unmuting.
22171     *
22172     * @return {bolean}
22173     *      - True if volume can be muted
22174     *      - False otherwise
22175     */
22176  
22177  
22178    Html5.canMuteVolume = function () {
22179      try {
22180        var muted = Html5.TEST_VID.muted; // in some versions of iOS muted property doesn't always
22181        // work, so we want to set both property and attribute
22182  
22183        Html5.TEST_VID.muted = !muted;
22184  
22185        if (Html5.TEST_VID.muted) {
22186          setAttribute(Html5.TEST_VID, 'muted', 'muted');
22187        } else {
22188          removeAttribute(Html5.TEST_VID, 'muted', 'muted');
22189        }
22190  
22191        return muted !== Html5.TEST_VID.muted;
22192      } catch (e) {
22193        return false;
22194      }
22195    };
22196    /**
22197     * Check if the playback rate can be changed in this browser/device.
22198     *
22199     * @return {boolean}
22200     *         - True if playback rate can be controlled
22201     *         - False otherwise
22202     */
22203  
22204  
22205    Html5.canControlPlaybackRate = function () {
22206      // Playback rate API is implemented in Android Chrome, but doesn't do anything
22207      // https://github.com/videojs/video.js/issues/3180
22208      if (IS_ANDROID && IS_CHROME && CHROME_VERSION < 58) {
22209        return false;
22210      } // IE will error if Windows Media Player not installed #3315
22211  
22212  
22213      try {
22214        var playbackRate = Html5.TEST_VID.playbackRate;
22215        Html5.TEST_VID.playbackRate = playbackRate / 2 + 0.1;
22216        return playbackRate !== Html5.TEST_VID.playbackRate;
22217      } catch (e) {
22218        return false;
22219      }
22220    };
22221    /**
22222     * Check if we can override a video/audio elements attributes, with
22223     * Object.defineProperty.
22224     *
22225     * @return {boolean}
22226     *         - True if builtin attributes can be overridden
22227     *         - False otherwise
22228     */
22229  
22230  
22231    Html5.canOverrideAttributes = function () {
22232      // if we cannot overwrite the src/innerHTML property, there is no support
22233      // iOS 7 safari for instance cannot do this.
22234      try {
22235        var noop = function noop() {};
22236  
22237        Object.defineProperty(document.createElement('video'), 'src', {
22238          get: noop,
22239          set: noop
22240        });
22241        Object.defineProperty(document.createElement('audio'), 'src', {
22242          get: noop,
22243          set: noop
22244        });
22245        Object.defineProperty(document.createElement('video'), 'innerHTML', {
22246          get: noop,
22247          set: noop
22248        });
22249        Object.defineProperty(document.createElement('audio'), 'innerHTML', {
22250          get: noop,
22251          set: noop
22252        });
22253      } catch (e) {
22254        return false;
22255      }
22256  
22257      return true;
22258    };
22259    /**
22260     * Check to see if native `TextTrack`s are supported by this browser/device.
22261     *
22262     * @return {boolean}
22263     *         - True if native `TextTrack`s are supported.
22264     *         - False otherwise
22265     */
22266  
22267  
22268    Html5.supportsNativeTextTracks = function () {
22269      return IS_ANY_SAFARI || IS_IOS && IS_CHROME;
22270    };
22271    /**
22272     * Check to see if native `VideoTrack`s are supported by this browser/device
22273     *
22274     * @return {boolean}
22275     *        - True if native `VideoTrack`s are supported.
22276     *        - False otherwise
22277     */
22278  
22279  
22280    Html5.supportsNativeVideoTracks = function () {
22281      return !!(Html5.TEST_VID && Html5.TEST_VID.videoTracks);
22282    };
22283    /**
22284     * Check to see if native `AudioTrack`s are supported by this browser/device
22285     *
22286     * @return {boolean}
22287     *        - True if native `AudioTrack`s are supported.
22288     *        - False otherwise
22289     */
22290  
22291  
22292    Html5.supportsNativeAudioTracks = function () {
22293      return !!(Html5.TEST_VID && Html5.TEST_VID.audioTracks);
22294    };
22295    /**
22296     * An array of events available on the Html5 tech.
22297     *
22298     * @private
22299     * @type {Array}
22300     */
22301  
22302  
22303    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'];
22304    /**
22305     * Boolean indicating whether the `Tech` supports volume control.
22306     *
22307     * @type {boolean}
22308     * @default {@link Html5.canControlVolume}
22309     */
22310  
22311    /**
22312     * Boolean indicating whether the `Tech` supports muting volume.
22313     *
22314     * @type {bolean}
22315     * @default {@link Html5.canMuteVolume}
22316     */
22317  
22318    /**
22319     * Boolean indicating whether the `Tech` supports changing the speed at which the media
22320     * plays. Examples:
22321     *   - Set player to play 2x (twice) as fast
22322     *   - Set player to play 0.5x (half) as fast
22323     *
22324     * @type {boolean}
22325     * @default {@link Html5.canControlPlaybackRate}
22326     */
22327  
22328    /**
22329     * Boolean indicating whether the `Tech` supports the `sourceset` event.
22330     *
22331     * @type {boolean}
22332     * @default
22333     */
22334  
22335    /**
22336     * Boolean indicating whether the `HTML5` tech currently supports native `TextTrack`s.
22337     *
22338     * @type {boolean}
22339     * @default {@link Html5.supportsNativeTextTracks}
22340     */
22341  
22342    /**
22343     * Boolean indicating whether the `HTML5` tech currently supports native `VideoTrack`s.
22344     *
22345     * @type {boolean}
22346     * @default {@link Html5.supportsNativeVideoTracks}
22347     */
22348  
22349    /**
22350     * Boolean indicating whether the `HTML5` tech currently supports native `AudioTrack`s.
22351     *
22352     * @type {boolean}
22353     * @default {@link Html5.supportsNativeAudioTracks}
22354     */
22355  
22356    [['featuresVolumeControl', 'canControlVolume'], ['featuresMuteControl', 'canMuteVolume'], ['featuresPlaybackRate', 'canControlPlaybackRate'], ['featuresSourceset', 'canOverrideAttributes'], ['featuresNativeTextTracks', 'supportsNativeTextTracks'], ['featuresNativeVideoTracks', 'supportsNativeVideoTracks'], ['featuresNativeAudioTracks', 'supportsNativeAudioTracks']].forEach(function (_ref) {
22357      var key = _ref[0],
22358          fn = _ref[1];
22359      defineLazyProperty(Html5.prototype, key, function () {
22360        return Html5[fn]();
22361      }, true);
22362    });
22363    /**
22364     * Boolean indicating whether the `HTML5` tech currently supports the media element
22365     * moving in the DOM. iOS breaks if you move the media element, so this is set this to
22366     * false there. Everywhere else this should be true.
22367     *
22368     * @type {boolean}
22369     * @default
22370     */
22371  
22372    Html5.prototype.movingMediaElementInDOM = !IS_IOS; // TODO: Previous comment: No longer appears to be used. Can probably be removed.
22373    //       Is this true?
22374  
22375    /**
22376     * Boolean indicating whether the `HTML5` tech currently supports automatic media resize
22377     * when going into fullscreen.
22378     *
22379     * @type {boolean}
22380     * @default
22381     */
22382  
22383    Html5.prototype.featuresFullscreenResize = true;
22384    /**
22385     * Boolean indicating whether the `HTML5` tech currently supports the progress event.
22386     * If this is false, manual `progress` events will be triggered instead.
22387     *
22388     * @type {boolean}
22389     * @default
22390     */
22391  
22392    Html5.prototype.featuresProgressEvents = true;
22393    /**
22394     * Boolean indicating whether the `HTML5` tech currently supports the timeupdate event.
22395     * If this is false, manual `timeupdate` events will be triggered instead.
22396     *
22397     * @default
22398     */
22399  
22400    Html5.prototype.featuresTimeupdateEvents = true; // HTML5 Feature detection and Device Fixes --------------------------------- //
22401  
22402    var canPlayType;
22403  
22404    Html5.patchCanPlayType = function () {
22405      // Android 4.0 and above can play HLS to some extent but it reports being unable to do so
22406      // Firefox and Chrome report correctly
22407      if (ANDROID_VERSION >
vendor: 15,215 bytes, lines 22407-22799
22407= 4.0 && !IS_FIREFOX && !IS_CHROME) {
22408        canPlayType = Html5.TEST_VID && Html5.TEST_VID.constructor.prototype.canPlayType;
22409  
22410        Html5.TEST_VID.constructor.prototype.canPlayType = function (type) {
22411          var mpegurlRE = /^application\/(?:x-|vnd\.apple\.)mpegurl/i;
22412  
22413          if (type && mpegurlRE.test(type)) {
22414            return 'maybe';
22415          }
22416  
22417          return canPlayType.call(this, type);
22418        };
22419      }
22420    };
22421  
22422    Html5.unpatchCanPlayType = function () {
22423      var r = Html5.TEST_VID.constructor.prototype.canPlayType;
22424  
22425      if (canPlayType) {
22426        Html5.TEST_VID.constructor.prototype.canPlayType = canPlayType;
22427      }
22428  
22429      return r;
22430    }; // by default, patch the media element
22431  
22432  
22433    Html5.patchCanPlayType();
22434  
22435    Html5.disposeMediaElement = function (el) {
22436      if (!el) {
22437        return;
22438      }
22439  
22440      if (el.parentNode) {
22441        el.parentNode.removeChild(el);
22442      } // remove any child track or source nodes to prevent their loading
22443  
22444  
22445      while (el.hasChildNodes()) {
22446        el.removeChild(el.firstChild);
22447      } // remove any src reference. not setting `src=''` because that causes a warning
22448      // in firefox
22449  
22450  
22451      el.removeAttribute('src'); // force the media element to update its loading state by calling load()
22452      // however IE on Windows 7N has a bug that throws an error so need a try/catch (#793)
22453  
22454      if (typeof el.load === 'function') {
22455        // wrapping in an iife so it's not deoptimized (#1060#discussion_r10324473)
22456        (function () {
22457          try {
22458            el.load();
22459          } catch (e) {// not supported
22460          }
22461        })();
22462      }
22463    };
22464  
22465    Html5.resetMediaElement = function (el) {
22466      if (!el) {
22467        return;
22468      }
22469  
22470      var sources = el.querySelectorAll('source');
22471      var i = sources.length;
22472  
22473      while (i--) {
22474        el.removeChild(sources[i]);
22475      } // remove any src reference.
22476      // not setting `src=''` because that throws an error
22477  
22478  
22479      el.removeAttribute('src');
22480  
22481      if (typeof el.load === 'function') {
22482        // wrapping in an iife so it's not deoptimized (#1060#discussion_r10324473)
22483        (function () {
22484          try {
22485            el.load();
22486          } catch (e) {// satisfy linter
22487          }
22488        })();
22489      }
22490    };
22491    /* Native HTML5 element property wrapping ----------------------------------- */
22492    // Wrap native boolean attributes with getters that check both property and attribute
22493    // The list is as followed:
22494    // muted, defaultMuted, autoplay, controls, loop, playsinline
22495  
22496  
22497    [
22498    /**
22499     * Get the value of `muted` from the media element. `muted` indicates
22500     * that the volume for the media should be set to silent. This does not actually change
22501     * the `volume` attribute.
22502     *
22503     * @method Html5#muted
22504     * @return {boolean}
22505     *         - True if the value of `volume` should be ignored and the audio set to silent.
22506     *         - False if the value of `volume` should be used.
22507     *
22508     * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-media-muted}
22509     */
22510    'muted',
22511    /**
22512     * Get the value of `defaultMuted` from the media element. `defaultMuted` indicates
22513     * whether the media should start muted or not. Only changes the default state of the
22514     * media. `muted` and `defaultMuted` can have different values. {@link Html5#muted} indicates the
22515     * current state.
22516     *
22517     * @method Html5#defaultMuted
22518     * @return {boolean}
22519     *         - The value of `defaultMuted` from the media element.
22520     *         - True indicates that the media should start muted.
22521     *         - False indicates that the media should not start muted
22522     *
22523     * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-media-defaultmuted}
22524     */
22525    'defaultMuted',
22526    /**
22527     * Get the value of `autoplay` from the media element. `autoplay` indicates
22528     * that the media should start to play as soon as the page is ready.
22529     *
22530     * @method Html5#autoplay
22531     * @return {boolean}
22532     *         - The value of `autoplay` from the media element.
22533     *         - True indicates that the media should start as soon as the page loads.
22534     *         - False indicates that the media should not start as soon as the page loads.
22535     *
22536     * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#attr-media-autoplay}
22537     */
22538    'autoplay',
22539    /**
22540     * Get the value of `controls` from the media element. `controls` indicates
22541     * whether the native media controls should be shown or hidden.
22542     *
22543     * @method Html5#controls
22544     * @return {boolean}
22545     *         - The value of `controls` from the media element.
22546     *         - True indicates that native controls should be showing.
22547     *         - False indicates that native controls should be hidden.
22548     *
22549     * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#attr-media-controls}
22550     */
22551    'controls',
22552    /**
22553     * Get the value of `loop` from the media element. `loop` indicates
22554     * that the media should return to the start of the media and continue playing once
22555     * it reaches the end.
22556     *
22557     * @method Html5#loop
22558     * @return {boolean}
22559     *         - The value of `loop` from the media element.
22560     *         - True indicates that playback should seek back to start once
22561     *           the end of a media is reached.
22562     *         - False indicates that playback should not loop back to the start when the
22563     *           end of the media is reached.
22564     *
22565     * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#attr-media-loop}
22566     */
22567    'loop',
22568    /**
22569     * Get the value of `playsinline` from the media element. `playsinline` indicates
22570     * to the browser that non-fullscreen playback is preferred when fullscreen
22571     * playback is the native default, such as in iOS Safari.
22572     *
22573     * @method Html5#playsinline
22574     * @return {boolean}
22575     *         - The value of `playsinline` from the media element.
22576     *         - True indicates that the media should play inline.
22577     *         - False indicates that the media should not play inline.
22578     *
22579     * @see [Spec]{@link https://html.spec.whatwg.org/#attr-video-playsinline}
22580     */
22581    'playsinline'].forEach(function (prop) {
22582      Html5.prototype[prop] = function () {
22583        return this.el_[prop] || this.el_.hasAttribute(prop);
22584      };
22585    }); // Wrap native boolean attributes with setters that set both property and attribute
22586    // The list is as followed:
22587    // setMuted, setDefaultMuted, setAutoplay, setLoop, setPlaysinline
22588    // setControls is special-cased above
22589  
22590    [
22591    /**
22592     * Set the value of `muted` on the media element. `muted` indicates that the current
22593     * audio level should be silent.
22594     *
22595     * @method Html5#setMuted
22596     * @param {boolean} muted
22597     *        - True if the audio should be set to silent
22598     *        - False otherwise
22599     *
22600     * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-media-muted}
22601     */
22602    'muted',
22603    /**
22604     * Set the value of `defaultMuted` on the media element. `defaultMuted` indicates that the current
22605     * audio level should be silent, but will only effect the muted level on intial playback..
22606     *
22607     * @method Html5.prototype.setDefaultMuted
22608     * @param {boolean} defaultMuted
22609     *        - True if the audio should be set to silent
22610     *        - False otherwise
22611     *
22612     * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-media-defaultmuted}
22613     */
22614    'defaultMuted',
22615    /**
22616     * Set the value of `autoplay` on the media element. `autoplay` indicates
22617     * that the media should start to play as soon as the page is ready.
22618     *
22619     * @method Html5#setAutoplay
22620     * @param {boolean} autoplay
22621     *         - True indicates that the media should start as soon as the page loads.
22622     *         - False indicates that the media should not start as soon as the page loads.
22623     *
22624     * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#attr-media-autoplay}
22625     */
22626    'autoplay',
22627    /**
22628     * Set the value of `loop` on the media element. `loop` indicates
22629     * that the media should return to the start of the media and continue playing once
22630     * it reaches the end.
22631     *
22632     * @method Html5#setLoop
22633     * @param {boolean} loop
22634     *         - True indicates that playback should seek back to start once
22635     *           the end of a media is reached.
22636     *         - False indicates that playback should not loop back to the start when the
22637     *           end of the media is reached.
22638     *
22639     * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#attr-media-loop}
22640     */
22641    'loop',
22642    /**
22643     * Set the value of `playsinline` from the media element. `playsinline` indicates
22644     * to the browser that non-fullscreen playback is preferred when fullscreen
22645     * playback is the native default, such as in iOS Safari.
22646     *
22647     * @method Html5#setPlaysinline
22648     * @param {boolean} playsinline
22649     *         - True indicates that the media should play inline.
22650     *         - False indicates that the media should not play inline.
22651     *
22652     * @see [Spec]{@link https://html.spec.whatwg.org/#attr-video-playsinline}
22653     */
22654    'playsinline'].forEach(function (prop) {
22655      Html5.prototype['set' + toTitleCase(prop)] = function (v) {
22656        this.el_[prop] = v;
22657  
22658        if (v) {
22659          this.el_.setAttribute(prop, prop);
22660        } else {
22661          this.el_.removeAttribute(prop);
22662        }
22663      };
22664    }); // Wrap native properties with a getter
22665    // The list is as followed
22666    // paused, currentTime, buffered, volume, poster, preload, error, seeking
22667    // seekable, ended, playbackRate, defaultPlaybackRate, played, networkState
22668    // readyState, videoWidth, videoHeight, crossOrigin
22669  
22670    [
22671    /**
22672     * Get the value of `paused` from the media element. `paused` indicates whether the media element
22673     * is currently paused or not.
22674     *
22675     * @method Html5#paused
22676     * @return {boolean}
22677     *         The value of `paused` from the media element.
22678     *
22679     * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-media-paused}
22680     */
22681    'paused',
22682    /**
22683     * Get the value of `currentTime` from the media element. `currentTime` indicates
22684     * the current second that the media is at in playback.
22685     *
22686     * @method Html5#currentTime
22687     * @return {number}
22688     *         The value of `currentTime` from the media element.
22689     *
22690     * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-media-currenttime}
22691     */
22692    'currentTime',
22693    /**
22694     * Get the value of `buffered` from the media element. `buffered` is a `TimeRange`
22695     * object that represents the parts of the media that are already downloaded and
22696     * available for playback.
22697     *
22698     * @method Html5#buffered
22699     * @return {TimeRange}
22700     *         The value of `buffered` from the media element.
22701     *
22702     * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-media-buffered}
22703     */
22704    'buffered',
22705    /**
22706     * Get the value of `volume` from the media element. `volume` indicates
22707     * the current playback volume of audio for a media. `volume` will be a value from 0
22708     * (silent) to 1 (loudest and default).
22709     *
22710     * @method Html5#volume
22711     * @return {number}
22712     *         The value of `volume` from the media element. Value will be between 0-1.
22713     *
22714     * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-a-volume}
22715     */
22716    'volume',
22717    /**
22718     * Get the value of `poster` from the media element. `poster` indicates
22719     * that the url of an image file that can/will be shown when no media data is available.
22720     *
22721     * @method Html5#poster
22722     * @return {string}
22723     *         The value of `poster` from the media element. Value will be a url to an
22724     *         image.
22725     *
22726     * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#attr-video-poster}
22727     */
22728    'poster',
22729    /**
22730     * Get the value of `preload` from the media element. `preload` indicates
22731     * what should download before the media is interacted with. It can have the following
22732     * values:
22733     * - none: nothing should be downloaded
22734     * - metadata: poster and the first few frames of the media may be downloaded to get
22735     *   media dimensions and other metadata
22736     * - auto: allow the media and metadata for the media to be downloaded before
22737     *    interaction
22738     *
22739     * @method Html5#preload
22740     * @return {string}
22741     *         The value of `preload` from the media element. Will be 'none', 'metadata',
22742     *         or 'auto'.
22743     *
22744     * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#attr-media-preload}
22745     */
22746    'preload',
22747    /**
22748     * Get the value of the `error` from the media element. `error` indicates any
22749     * MediaError that may have occurred during playback. If error returns null there is no
22750     * current error.
22751     *
22752     * @method Html5#error
22753     * @return {MediaError|null}
22754     *         The value of `error` from the media element. Will be `MediaError` if there
22755     *         is a current error and null otherwise.
22756     *
22757     * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-media-error}
22758     */
22759    'error',
22760    /**
22761     * Get the value of `seeking` from the media element. `seeking` indicates whether the
22762     * media is currently seeking to a new position or not.
22763     *
22764     * @method Html5#seeking
22765     * @return {boolean}
22766     *         - The value of `seeking` from the media element.
22767     *         - True indicates that the media is currently seeking to a new position.
22768     *         - False indicates that the media is not seeking to a new position at this time.
22769     *
22770     * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-media-seeking}
22771     */
22772    'seeking',
22773    /**
22774     * Get the value of `seekable` from the media element. `seekable` returns a
22775     * `TimeRange` object indicating ranges of time that can currently be `seeked` to.
22776     *
22777     * @method Html5#seekable
22778     * @return {TimeRange}
22779     *         The value of `seekable` from the media element. A `TimeRange` object
22780     *         indicating the current ranges of time that can be seeked to.
22781     *
22782     * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-media-seekable}
22783     */
22784    'seekable',
22785    /**
22786     * Get the value of `ended` from the media element. `ended` indicates whether
22787     * the media has reached the end or not.
22788     *
22789     * @method Html5#ended
22790     * @return {boolean}
22791     *         - The value of `ended` from the media element.
22792     *         - True indicates that the media has ended.
22793     *         - False indicates that the media has not ended.
22794     *
22795     * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-media-ended}
22796     */
22797    'ended',
22798    /**
22799     * Get the value of `playbackRate` from the media element. `playbackRate` indicates
22800     * the rate at which the media is currently playing back. Examples:
22801     *   - if playbackRate is set to 2, media will play twice as fast.
22802     *   - if playbackRate is set to 0.5, media will play half as fast.
22803     *
22804     * @method Html5#playbackRate
22805     * @return {number}
22806     *         The value of `playbackRate` from the media element. A number indicating
22807     *         the current playback speed of the media, where 1 is normal speed.
22808     *
22809     * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-media-playbackrate}
22810     */
22811    'playbackRate',
22812    /**
22813     * Get the value of `defaultPlaybackRate` from the media element. `defaultPlaybackRate` indicates
22814     * the rate at which the media is currently playing back. This value will not indicate the current
22815     * `playbackRate` after playback has started, use {@link Html5#playbackRate} for that.
22816     *
22817     * Examples:
22818     *   - if defaultPlaybackRate is set to 2, media will play twice as fast.
22819     *   - if defaultPlaybackRate is set to 0.5, media will play half as fast.
22820     *
22821     * @method Html5.prototype.defaultPlaybackRate
22822     * @return {number}
22823     *         The value of `defaultPlaybackRate` from the media element. A number indicating
22824     *         the current playback speed of the media, where 1 is normal speed.
22825     *
22826     * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-media-playbackrate}
22827     */
22828    'defaultPlaybackRate',
22829    /**
22830     * Get the value of `played` from the media element. `played` returns a `TimeRange`
22831     * object representing points in the media timeline that have been played.
22832     *
22833     * @method Html5#played
22834     * @return {TimeRange}
22835     *         The value of `played` from the media element. A `TimeRange` object indicating
22836     *         the ranges of time that have been played.
22837     *
22838     * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-media-played}
22839     */
22840    'played',
22841    /**
22842     * Get the value of `networkState` from the media element. `networkState` indicates
22843     * the current network state. It returns an enumeration from the following list:
22844     * - 0: NETWORK_EMPTY
22845     * - 1: NETWORK_IDLE
22846     * - 2: NETWORK_LOADING
22847     * - 3: NETWORK_NO_SOURCE
22848     *
22849     * @method Html5#networkState
22850     * @return {number}
22851     *         The value of `networkState` from the media element. This will be a number
22852     *         from the list in the description.
22853     *
22854     * @see [Spec] {@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-media-networkstate}
22855     */
22856    'networkState',
22857    /**
22858     * Get the value of `readyState` from the media element. `readyState` indicates
22859     * the current state of the media element. It returns an enumeration from the
22860     * following list:
22861     * - 0: HAVE_NOTHING
22862     * - 1: HAVE_METADATA
22863     * - 2: HAVE_CURRENT_DATA
22864     * - 3: HAVE_FUTURE_DATA
22865     * - 4: HAVE_ENOUGH_DATA
22866     *
22867     * @method Html5#readyState
22868     * @return {number}
22869     *         The value of `readyState` from the media element. This will be a number
22870     *         from the list in the description.
22871     *
22872     * @see [Spec] {@link https://www.w3.org/TR/html5/embedded-content-0.html#ready-states}
22873     */
22874    'readyState',
22875    /**
22876     * Get the value of `videoWidth` from the video element. `videoWidth` indicates
22877     * the current width of the video in css pixels.
22878     *
22879     * @method Html5#videoWidth
22880     * @return {number}
22881     *         The value of `videoWidth` from the video element. This will be a number
22882     *         in css pixels.
22883     *
22884     * @see [Spec] {@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-video-videowidth}
22885     */
22886    'videoWidth',
22887    /**
22888     * Get the value of `videoHeight` from the video element. `videoHeight` indicates
22889     * the current height of the video in css pixels.
22890     *
22891     * @method Html5#videoHeight
22892     * @return {number}
22893     *         The value of `videoHeight` from the video element. This will be a number
22894     *         in css pixels.
22895     *
22896     * @see [Spec] {@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-video-videowidth}
22897     */
22898    'videoHeight',
22899    /**
22900     * Get the value of `crossOrigin` from the media element. `crossOrigin` indicates
22901     * to the browser that should sent the cookies along with the requests for the
22902     * different assets/playlists
22903     *
22904     * @method Html5#crossOrigin
22905     * @return {string}
22906     *         - anonymous indicates that the media should not sent cookies.
22907     *         - use-credentials indicates that the media should sent cookies along the requests.
22908     *
22909     * @see [Spec]{@link https://html.spec.whatwg.org/#attr-media-crossorigin}
22910     */
22911    'crossOrigin'].forEach(function (prop) {
22912      Html5.prototype[prop] = function () {
22913        return this.el_[prop];
22914      };
22915    }); // Wrap native properties with a setter in this format:
22916    // set + toTitleCase(name)
22917    // The list is as follows:
22918    // setVolume, setSrc, setPoster, setPreload, setPlaybackRate, setDefaultPlaybackRate, setCrossOrigin
22919  
22920    [
22921    /**
22922     * Set the value of `volume` on the media element. `volume` indicates the current
22923     * audio level as a percentage in decimal form. This means that 1 is 100%, 0.5 is 50%, and
22924     * so on.
22925     *
22926     * @method Html5#setVolume
22927     * @param {number} percentAsDecimal
22928     *        The volume percent as a decimal. Valid range is from 0-1.
22929     *
22930     * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-a-volume}
22931     */
22932    'volume',
22933    /**
22934     * Set the value of `src` on the media element. `src` indicates the current
22935     * {@link Tech~SourceObject} for the media.
22936     *
22937     * @method Html5#setSrc
22938     * @param {Tech~SourceObject} src
22939     *        The source object to set as the current source.
22940     *
22941     * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-media-src}
22942     */
22943    'src',
22944    /**
22945     * Set the value of `poster` on the media element. `poster` is the url to
22946     * an image file that can/will be shown when no media data is available.
22947     *
22948     * @method Html5#setPoster
22949     * @param {string} poster
22950     *        The url to an image that should be used as the `poster` for the media
22951     *        element.
22952     *
22953     * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#attr-media-poster}
22954     */
22955    'poster',
22956    /**
22957     * Set the value of `preload` on the media element. `preload` indicates
22958     * what should download before the media is interacted with. It can have the following
22959     * values:
22960     * - none: nothing should be downloaded
22961     * - metadata: poster and the first few frames of the media may be downloaded to get
22962     *   media dimensions and other metadata
22963     * - auto: allow the media and metadata for the media to be downloaded before
22964     *    interaction
22965     *
22966     * @method Html5#setPreload
22967     * @param {string} preload
22968     *         The value of `preload` to set on the media element. Must be 'none', 'metadata',
22969     *         or 'auto'.
22970     *
22971     * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#attr-media-preload}
22972     */
22973    'preload',
22974    /**
22975     * Set the value of `playbackRate` on the media element. `playbackRate` indicates
22976     * the rate at which the media should play back. Examples:
22977     *   - if playbackRate is set to 2, media will play twice as fast.
22978     *   - if playbackRate is set to 0.5, media will play half as fast.
22979     *
22980     * @method Html5#setPlaybackRate
22981     * @return {number}
22982     *         The value of `playbackRate` from the media element. A number indicating
22983     *         the current playback speed of the media, where 1 is normal speed.
22984     *
22985     * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-media-playbackrate}
22986     */
22987    'playbackRate',
22988    /**
22989     * Set the value of `defaultPlaybackRate` on the media element. `defaultPlaybackRate` indicates
22990     * the rate at which the media should play back upon initial startup. Changing this value
22991     * after a video has started will do nothing. Instead you should used {@link Html5#setPlaybackRate}.
22992     *
22993     * Example Values:
22994     *   - if playbackRate is set to 2, media will play twice as fast.
22995     *   - if playbackRate is set to 0.5, media will play half as fast.
22996     *
22997     * @method Html5.prototype.setDefaultPlaybackRate
22998     * @return {number}
22999     *         The value of `defaultPlaybackRate` from the media element. A number indicating
23000     *         the current playback speed of the media, where 1 is normal speed.
23001     *
23002     * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-media-defaultplaybackrate}
23003     */
23004    'defaultPlaybackRate',
23005    /**
23006     * Set the value of `crossOrigin` from the media element. `crossOrigin` indicates
23007     * to the browser that should sent the cookies along with the requests for the
23008     * different assets/playlists
23009     *
23010     * @method Html5#setCrossOrigin
23011     * @param {string} crossOrigin
23012     *         - anonymous indicates that the media should not sent cookies.
23013     *         - use-credentials indicates that the media should sent cookies along the requests.
23014     *
23015     * @see [Spec]{@link https://html.spec.whatwg.org/#attr-media-crossorigin}
23016     */
23017    'crossOrigin'].forEach(function (prop) {
23018      Html5.prototype['set' + toTitleCase(prop)] = function (v) {
23019        this.el_[prop] = v;
23020      };
23021    }); // wrap native functions with a function
23022    // The list is as follows:
23023    // pause, load, play
23024  
23025    [
23026    /**
23027     * A wrapper around the media elements `pause` function. This will call the `HTML5`
23028     * media elements `pause` function.
23029     *
23030     * @method Html5#pause
23031     * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-media-pause}
23032     */
23033    'pause',
23034    /**
23035     * A wrapper around the media elements `load` function. This will call the `HTML5`s
23036     * media element `load` function.
23037     *
23038     * @method Html5#load
23039     * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-media-load}
23040     */
23041    'load',
23042    /**
23043     * A wrapper around the media elements `play` function. This will call the `HTML5`s
23044     * media element `play` function.
23045     *
23046     * @method Html5#play
23047     * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-media-play}
23048     */
23049    'play'].forEach(function (prop) {
23050      Html5.prototype[prop] = function () {
23051        return this.el_[prop]();
23052      };
23053    });
23054    Tech.withSourceHandlers(Html5);
23055    /**
23056     * Native source handler for Html5, simply passes the source to the media element.
23057     *
23058     * @property {Tech~SourceObject} source
23059     *        The source object
23060     *
23061     * @property {Html5} tech
23062     *        The instance of the HTML5 tech.
23063     */
23064  
23065    Html5.nativeSourceHandler = {};
23066    /**
23067     * Check if the media element can play the given mime type.
23068     *
23069     * @param {string} type
23070     *        The mimetype to check
23071     *
23072     * @return {string}
23073     *         'probably', 'maybe', or '' (empty string)
23074     */
23075  
23076    Html5.nativeSourceHandler.canPlayType = function (type) {
23077      // IE without MediaPlayer throws an error (#519)
23078      try {
23079        return Html5.TEST_VID.canPlayType(type);
23080      } catch (e) {
23081        return '';
23082      }
23083    };
23084    /**
23085     * Check if the media element can handle a source natively.
23086     *
23087     * @param {Tech~SourceObject} source
23088     *         The source object
23089     *
23090     * @param {Object} [options]
23091     *         Options to be passed to the tech.
23092     *
23093     * @return {string}
23094     *         'probably', 'maybe', or '' (empty string).
23095     */
23096  
23097  
23098    Html5.nativeSourceHandler.canHandleSource = function (source, options) {
23099      // If a type was provided we should rely on that
23100      if (source.type) {
23101        return Html5.nativeSourceHandler.canPlayType(source.type); // If no type, fall back to checking 'video/[EXTENSION]'
23102      } else if (source.src) {
23103        var ext = getFileExtension(source.src);
23104        return Html5.nativeSourceHandler.canPlayType("video/" + ext);
23105      }
23106  
23107      return '';
23108    };
23109    /**
23110     * Pass the source to the native media element.
23111     *
23112     * @param {Tech~SourceObject} source
23113     *        The source object
23114     *
23115     * @param {Html5} tech
23116     *        The instance of the Html5 tech
23117     *
23118     * @param {Object} [options]
23119     *        The options to pass to the source
23120     */
23121  
23122  
23123    Html5.nativeSourceHandler.handleSource = function (source, tech, options) {
23124      tech.setSrc(source.src);
23125    };
23126    /**
23127     * A noop for the native dispose function, as cleanup is not needed.
23128     */
23129  
23130  
23131    Html5.nativeSourceHandler.dispose = function () {}; // Register the native source handler
23132  
23133  
23134    Html5.registerSourceHandler(Html5.nativeSourceHandler);
23135    Tech.registerTech('Html5', Html5);
23136  
23137    // on the player when they happen
23138  
23139    var TECH_EVENTS_RETRIGGER = [
23140    /**
23141     * Fired while the user agent is downloading media data.
23142     *
23143     * @event Player#progress
23144     * @type {EventTarget~Event}
23145     */
23146  
23147    /**
23148     * Retrigger the `progress` event that was triggered by the {@link Tech}.
23149     *
23150     * @private
23151     * @method Player#handleTechProgress_
23152     * @fires Player#progress
23153     * @listens Tech#progress
23154     */
23155    'progress',
23156    /**
23157     * Fires when the loading of an audio/video is aborted.
23158     *
23159     * @event Player#abort
23160     * @type {EventTarget~Event}
23161     */
23162  
23163    /**
23164     * Retrigger the `abort` event that was triggered by the {@link Tech}.
23165     *
23166     * @private
23167     * @method Player#handleTechAbort_
23168     * @fires Player#abort
23169     * @listens Tech#abort
23170     */
23171    'abort',
23172    /**
23173     * Fires when the browser is intentionally not getting media data.
23174     *
23175     * @event Player#suspend
23176     * @type {EventTarget~Event}
23177     */
23178  
23179    /**
23180     * Retrigger the `suspend` event that was triggered by the {@link Tech}.
23181     *
23182     * @private
23183     * @method Player#handleTechSuspend_
23184     * @fires Player#suspend
23185     * @listens Tech#suspend
23186     */
23187    'suspend',
23188    /**
23189     * Fires when the current playlist is empty.
23190     *
23191     * @event Player#emptied
23192     * @type {EventTarget~Event}
23193     */
23194  
23195    /**
23196     * Retrigger the `emptied` event that was triggered by the {@link Tech}.
23197     *
23198     * @private
23199     * @method Player#handleTechEmptied_
23200     * @fires Player#emptied
23201     * @listens Tech#emptied
23202     */
23203    'emptied',
23204    /**
23205     * Fires when the browser is trying to get media data, but data is not available.
23206     *
23207     * @event Player#stalled
23208     * @type {EventTarget~Event}
23209     */
23210  
23211    /**
23212     * Retrigger the `stalled` event that was triggered by the {@link Tech}.
23213     *
23214     * @private
23215     * @method Player#handleTechStalled_
23216     * @fires Player#stalled
23217     * @listens Tech#stalled
23218     */
23219    'stalled',
23220    /**
23221     * Fires when the browser has loaded meta data for the audio/video.
23222     *
23223     * @event Player#loadedmetadata
23224     * @type {EventTarget~Event}
23225     */
23226  
23227    /**
23228     * Retrigger the `stalled` event that was triggered by the {@link Tech}.
23229     *
23230     * @private
23231     * @method Player#handleTechLoadedmetadata_
23232     * @fires Player#loadedmetadata
23233     * @listens Tech#loadedmetadata
23234     */
23235    'loadedmetadata',
23236    /**
23237     * Fires when the browser has loaded the current frame of the audio/video.
23238     *
23239     * @event Player#loadeddata
23240     * @type {event}
23241     */
23242  
23243    /**
23244     * Retrigger the `loadeddata` event that was triggered by the {@link Tech}.
23245     *
23246     * @private
23247     * @method Player#handleTechLoaddeddata_
23248     * @fires Player#loadeddata
23249     * @listens Tech#loadeddata
23250     */
23251    'loadeddata',
23252    /**
23253     * Fires when the current playback position has changed.
23254     *
23255     * @event Player#timeupdate
23256     * @type {event}
23257     */
23258  
23259    /**
23260     * Retrigger the `timeupdate` event that was triggered by the {@link Tech}.
23261     *
23262     * @private
23263     * @method Player#handleTechTimeUpdate_
23264     * @fires Player#timeupdate
23265     * @listens Tech#timeupdate
23266     */
23267    'timeupdate',
23268    /**
23269     * Fires when the video's intrinsic dimensions change
23270     *
23271     * @event Player#resize
23272     * @type {event}
23273     */
23274  
23275    /**
23276     * Retrigger the `resize` event that was triggered by the {@link Tech}.
23277     *
23278     * @private
23279     * @method Player#handleTechResize_
23280     * @fires Player#resize
23281     * @listens Tech#resize
23282     */
23283    'resize',
23284    /**
23285     * Fires when the volume has been changed
23286     *
23287     * @event Player#volumechange
23288     * @type {event}
23289     */
23290  
23291    /**
23292     * Retrigger the `volumechange` event that was triggered by the {@link Tech}.
23293     *
23294     * @private
23295     * @method Player#handleTechVolumechange_
23296     * @fires Player#volumechange
23297     * @listens Tech#volumechange
23298     */
23299    'volumechange',
23300    /**
23301     * Fires when the text track has been changed
23302     *
23303     * @event Player#texttrackchange
23304     * @type {event}
23305     */
23306  
23307    /**
23308     * Retrigger the `texttrackchange` event that was triggered by the {@link Tech}.
23309     *
23310     * @private
23311     * @method Player#handleTechTexttrackchange_
23312     * @fires Player#texttrackchange
23313     * @listens Tech#texttrackchange
23314     */
23315    'texttrackchange']; // events to queue when playback rate is zero
23316    // this is a hash for the sole purpose of mapping non-camel-cased event names
23317    // to camel-cased function names
23318  
23319    var TECH_EVENTS_QUEUE = {
23320      canplay: 'CanPlay',
23321      canplaythrough: 'CanPlayThrough',
23322      playing: 'Playing',
23323      seeked: 'Seeked'
23324    };
23325    var BREAKPOINT_ORDER = ['tiny', 'xsmall', 'small', 'medium', 'large', 'xlarge', 'huge'];
23326    var BREAKPOINT_CLASSES = {}; // grep: vjs-layout-tiny
23327    // grep: vjs-layout-x-small
23328    // grep: vjs-layout-small
23329    // grep: vjs-layout-medium
23330    // grep: vjs-layout-large
23331    // grep: vjs-layout-x-large
23332    // grep: vjs-layout-huge
23333  
23334    BREAKPOINT_ORDER.forEach(function (k) {
23335      var v = k.charAt(0) === 'x' ? "x-" + k.substring(1) : k;
23336      BREAKPOINT_CLASSES[k] = "vjs-layout-" + v;
23337    });
23338    var DEFAULT_BREAKPOINTS = {
23339      tiny: 210,
23340      xsmall: 320,
23341      small: 425,
23342      medium: 768,
23343      large: 1440,
23344      xlarge: 2560,
23345      huge: Infinity
23346    };
23347    /**
23348     * An instance of the `Player` class is created when any of the Video.js setup methods
23349     * are used to initialize a video.
23350     *
23351     * After an instance has been created it can be accessed globally in two ways:
23352     * 1. By calling `videojs('example_video_1');`
23353     * 2. By using it directly via  `videojs.players.example_video_1;`
23354     *
23355     * @extends Component
23356     */
23357  
23358    var Player = /*#__PURE__*/function (_Component) {
23359      inheritsLoose(Player, _Component);
23360  
23361      /**
23362       * Create an instance of this class.
23363       *
23364       * @param {Element} tag
23365       *        The original video DOM element used for configuring options.
23366       *
23367       * @param {Object} [options]
23368       *        Object of option names and values.
23369       *
23370       * @param {Component~ReadyCallback} [ready]
23371       *        Ready callback function.
23372       */
23373      function Player(tag, options, ready) {
23374        var _this;
23375  
23376        // Make sure tag ID exists
23377        tag.id = tag.id || options.id || "vjs_video_" + newGUID(); // Set Options
23378        // The options argument overrides options set in the video tag
23379        // which overrides globally set options.
23380        // This latter part coincides with the load order
23381        // (tag must exist before Player)
23382  
23383        options = assign(Player.getTagSettings(tag), options); // Delay the initialization of children because we need to set up
23384        // player properties first, and can't use `this` before `super()`
23385  
23386        options.initChildren = false; // Same with creating the element
23387  
23388        options.createEl = false; // don't auto mixin the evented mixin
23389  
23390        options.evented = false; // we don't want the player to report touch activity on itself
23391        // see enableTouchActivity in Component
23392  
23393        options.reportTouchActivity = false; // If language is not set, get the closest lang attribute
23394  
23395        if (!options.language) {
23396          if (typeof tag.closest === 'function') {
23397            var closest = tag.closest('[lang]');
23398  
23399            if (closest && closest.getAttribute) {
23400              options.language = closest.getAttribute('lang');
23401            }
23402          } else {
23403            var element = tag;
23404  
23405            while (element && element.nodeType === 1) {
23406              if (getAttributes(element).hasOwnProperty('lang')) {
23407                options.language = element.getAttribute('lang');
23408                break;
23409              }
23410  
23411              element = element.parentNode;
23412            }
23413          }
23414        } // Run base component initializing with new options
23415  
23416  
23417        _this = _Component.call(this, null, options, ready) || this; // Create bound methods for document listeners.
23418  
23419        _this.boundDocumentFullscreenChange_ = bind(assertThisInitialized(_this), _this.documentFullscreenChange_);
23420        _this.boundFullWindowOnEscKey_ = bind(assertThisInitialized(_this), _this.fullWindowOnEscKey); // default isFullscreen_ to false
23421  
23422        _this.isFullscreen_ = false; // create logger
23423  
23424        _this.log = createLogger$1(_this.id_); // Hold our own reference to fullscreen api so it can be mocked in tests
23425  
23426        _this.fsApi_ = FullscreenApi; // Tracks when a tech changes the poster
23427  
23428        _this.isPosterFromTech_ = false; // Holds callback info that gets queued when playback rate is zero
23429        // and a seek is happening
23430  
23431        _this.queuedCallbacks_ = []; // Turn off API access because we're loading a new tech that might load asynchronously
23432  
23433        _this.isReady_ = false; // Init state hasStarted_
23434  
23435        _this.hasStarted_ = false; // Init state userActive_
23436  
23437        _this.userActive_ = false; // if the global option object was accidentally blown away by
23438        // someone, bail early with an informative error
23439  
23440        if (!_this.options_ || !_this.options_.techOrder || !_this.options_.techOrder.length) {
23441          throw new Error('No techOrder specified. Did you overwrite ' + 'videojs.options instead of just changing the ' + 'properties you want to override?');
23442        } // Store the original tag used to set options
23443  
23444  
23445        _this.tag = tag; // Store the tag attributes used to restore html5 element
23446  
23447        _this.tagAttributes = tag && getAttributes(tag); // Update current language
23448  
23449        _this.language(_this.options_.language); // Update Supported Languages
23450  
23451  
23452        if (options.languages) {
23453          // Normalise player option languages to lowercase
23454          var languagesToLower = {};
23455          Object.getOwnPropertyNames(options.languages).forEach(function (name) {
23456            languagesToLower[name.toLowerCase()] = options.languages[name];
23457          });
23458          _this.languages_ = languagesToLower;
23459        } else {
23460          _this.languages_ = Player.prototype.options_.languages;
23461        }
23462  
23463        _this.resetCache_(); // Set poster
23464  
23465  
23466        _this.poster_ = options.poster || ''; // Set controls
23467  
23468        _this.controls_ = !!options.controls; // Original tag settings stored in options
23469        // now remove immediately so native controls don't flash.
23470        // May be turned back on by HTML5 tech if nativeControlsForTouch is true
23471  
23472        tag.controls = false;
23473        tag.removeAttribute('controls');
23474        _this.changingSrc_ = false;
23475        _this.playCallbacks_ = [];
23476        _this.playTerminatedQueue_ = []; // the attribute overrides the option
23477  
23478        if (tag.hasAttribute('autoplay')) {
23479          _this.autoplay(true);
23480        } else {
23481          // otherwise use the setter to validate and
23482          // set the correct value.
23483          _this.autoplay(_this.options_.autoplay);
23484        } // check plugins
23485  
23486  
23487        if (options.plugins) {
23488          Object.keys(options.plugins).forEach(function (name) {
23489            if (typeof _this[name] !== 'function') {
23490              throw new Error("plugin \"" + name + "\" does not exist");
23491            }
23492          });
23493        }
23494        /*
23495         * Store the internal state of scrubbing
23496         *
23497         * @private
23498         * @return {Boolean} True if the user is scrubbing
23499         */
23500  
23501  
23502        _this.scrubbing_ = false;
23503        _this.el_ = _this.createEl(); // Make this an evented object and use `el_` as its event bus.
23504  
23505        evented(assertThisInitialized(_this), {
23506          eventBusKey: 'el_'
23507        }); // listen to document and player fullscreenchange handlers so we receive those events
23508        // before a user can receive them so we can update isFullscreen appropriately.
23509        // make sure that we listen to fullscreenchange events before everything else to make sure that
23510        // our isFullscreen method is updated properly for internal components as well as external.
23511  
23512        if (_this.fsApi_.requestFullscreen) {
23513          on(document, _this.fsApi_.fullscreenchange, _this.boundDocumentFullscreenChange_);
23514  
23515          _this.on(_this.fsApi_.fullscreenchange, _this.boundDocumentFullscreenChange_);
23516        }
23517  
23518        if (_this.fluid_) {
23519          _this.on('playerreset', _this.updateStyleEl_);
23520        } // We also want to pass the original player options to each component and plugin
23521        // as well so they don't need to reach back into the player for options later.
23522        // We also need to do another copy of this.options_ so we don't end up with
23523        // an infinite loop.
23524  
23525  
23526        var playerOptionsCopy = mergeOptions(_this.options_); // Load plugins
23527  
23528        if (options.plugins) {
23529          Object.keys(options.plugins).forEach(function (name) {
23530            _this[name](options.plugins[name]);
23531          });
23532        }
23533  
23534        _this.options_.playerOptions = playerOptionsCopy;
23535        _this.middleware_ = [];
23536  
23537        _this.initChildren(); // Set isAudio based on whether or not an audio tag was used
23538  
23539  
23540        _this.isAudio(tag.nodeName.toLowerCase() === 'audio'); // Update controls className. Can't do this when the controls are initially
23541        // set because the element doesn't exist yet.
23542  
23543  
23544        if (_this.controls()) {
23545          _this.addClass('vjs-controls-enabled');
23546        } else {
23547          _this.addClass('vjs-controls-disabled');
23548        } // Set ARIA label and region role depending on player type
23549  
23550  
23551        _this.el_.setAttribute('role', 'region');
23552  
23553        if (_this.isAudio()) {
23554          _this.el_.setAttribute('aria-label', _this.localize('Audio Player'));
23555        } else {
23556          _this.el_.setAttribute('aria-label', _this.localize('Video Player'));
23557        }
23558  
23559        if (_this.isAudio()) {
23560          _this.addClass('vjs-audio');
23561        }
23562  
23563        if (_this.flexNotSupported_()) {
23564          _this.addClass('vjs-no-flex');
23565        } // TODO: Make this smarter. Toggle user state between touching/mousing
23566        // using events, since devices can have both touch and mouse events.
23567        // TODO: Make this check be performed again when the window switches between monitors
23568        // (See https://github.com/videojs/video.js/issues/5683)
23569  
23570  
23571        if (TOUCH_ENABLED) {
23572          _this.addClass('vjs-touch-enabled');
23573        } // iOS Safari has broken hover handling
23574  
23575  
23576        if (!IS_IOS) {
23577          _this.addClass('vjs-workinghover');
23578        } // Make player easily findable by ID
23579  
23580  
23581        Player.players[_this.id_] = assertThisInitialized(_this); // Add a major version class to aid css in plugins
23582  
23583        var majorVersion = version.split('.')[0];
23584  
23585        _this.addClass("vjs-v" + majorVersion); // When the player is first initialized, trigger activity so components
23586        // like the control bar show themselves if needed
23587  
23588  
vendor: 1,787 bytes, lines 23589-23651
23589        _this.userActive(true);
23590  
23591        _this.reportUserActivity();
23592  
23593        _this.one('play', _this.listenForUserActivity_);
23594  
23595        _this.on('stageclick', _this.handleStageClick_);
23596  
23597        _this.on('keydown', _this.handleKeyDown);
23598  
23599        _this.breakpoints(_this.options_.breakpoints);
23600  
23601        _this.responsive(_this.options_.responsive);
23602  
23603        return _this;
23604      }
23605      /**
23606       * Destroys the video player and does any necessary cleanup.
23607       *
23608       * This is especially helpful if you are dynamically adding and removing videos
23609       * to/from the DOM.
23610       *
23611       * @fires Player#dispose
23612       */
23613  
23614  
23615      var _proto = Player.prototype;
23616  
23617      _proto.dispose = function dispose() {
23618        var _this2 = this;
23619  
23620        /**
23621         * Called when the player is being disposed of.
23622         *
23623         * @event Player#dispose
23624         * @type {EventTarget~Event}
23625         */
23626        this.trigger('dispose'); // prevent dispose from being called twice
23627  
23628        this.off('dispose'); // Make sure all player-specific document listeners are unbound. This is
23629  
23630        off(document, this.fsApi_.fullscreenchange, this.boundDocumentFullscreenChange_);
23631        off(document, 'keydown', this.boundFullWindowOnEscKey_);
23632  
23633        if (this.styleEl_ && this.styleEl_.parentNode) {
23634          this.styleEl_.parentNode.removeChild(this.styleEl_);
23635          this.styleEl_ = null;
23636        } // Kill reference to this player
23637  
23638  
23639        Player.players[this.id_] = null;
23640  
23641        if (this.tag && this.tag.player) {
23642          this.tag.player = null;
23643        }
23644  
23645        if (this.el_ && this.el_.player) {
23646          this.el_.player = null;
23647        }
23648  
23649        if (this.tech_) {
23650          this.tech_.dispose();
23651          this.isPosterFromTech_ = false;
23652          this.poster_ = '';
23653        }
23654  
23655        if (this.playerElIngest_) {
23656          this.playerElIngest_ = null;
23657        }
23658  
23659        if (this.tag) {
23660          this.tag = null;
23661        }
23662  
23663        clearCacheForPlayer(this); // remove all event handlers for track lists
23664        // all tracks and track listeners are removed on
23665        // tech dispose
23666  
23667        ALL.names.forEach(function (name) {
23668          var props = ALL[name];
23669  
23670          var list = _this2[props.getterName](); // if it is not a native list
23671          // we have to manually remove event listeners
23672  
23673  
23674          if (list && list.off) {
23675            list.off();
23676          }
23677        }); // the actual .el_ is removed here
23678  
23679        _Component.prototype.dispose.call(this);
23680      }
23681      /**
23682       * Create the `Player`'s DOM element.
23683       *
23684       * @return {Element}
23685       *         The DOM element that gets created.
23686       */
23687      ;
23688  
23689      _proto.createEl = function createEl() {
23690        var tag = this.tag;
23691        var el;
23692        var playerElIngest = this.playerElIngest_ = tag.parentNode && tag.parentNode.hasAttribute && tag.parentNode.hasAttribute('data-vjs-player');
23693        var divEmbed = this.tag.tagName.toLowerCase() === 'video-js';
23694  
23695        if (playerElIngest) {
23696          el = this.el_ = tag.parentNode;
23697        } else if (!divEmbed) {
23698          el = this.el_ = _Component.prototype.createEl.call(this, 'div');
23699        } // Copy over all the attributes from the tag, including ID and class
23700        // ID will now reference player box, not the video tag
23701  
23702  
23703        var attrs = getAttributes(tag);
23704  
23705        if (divEmbed) {
23706          el = this.el_ = tag;
23707          tag = this.tag = document.createElement('video');
23708  
23709          while (el.children.length) {
23710            tag.appendChild(el.firstChild);
23711          }
23712  
23713          if (!hasClass(el, 'video-js')) {
23714            addClass(el, 'video-js');
23715          }
23716  
23717          el.appendChild(tag);
23718          playerElIngest = this.playerElIngest_ = el; // move properties over from our custom `video-js` element
23719          // to our new `video` element. This will move things like
23720          // `src` or `controls` that were set via js before the player
23721          // was initialized.
23722  
23723          Object.keys(el).forEach(function (k) {
23724            try {
23725              tag[k] = el[k];
23726            } catch (e) {// we got a a property like outerHTML which we can't actually copy, ignore it
23727            }
23728          });
23729        } // set tabindex to -1 to remove the video element from the focus order
23730  
23731  
23732        tag.setAttribute('tabindex', '-1');
23733        attrs.tabindex = '-1'; // Workaround for #4583 (JAWS+IE doesn't announce BPB or play button), and
23734        // for the same issue with Chrome (on Windows) with JAWS.
23735        // See https://github.com/FreedomScientific/VFO-standards-support/issues/78
23736        // Note that we can't detect if JAWS is being used, but this ARIA attribute
23737        //  doesn't change behavior of IE11 or Chrome if JAWS is not being used
23738  
23739        if (IE_VERSION || IS_CHROME && IS_WINDOWS) {
23740          tag.setAttribute('role', 'application');
23741          attrs.role = 'application';
23742        } // Remove width/height attrs from tag so CSS can make it 100% width/height
23743  
23744  
23745        tag.removeAttribute('width');
23746        tag.removeAttribute('height');
23747  
23748        if ('width' in attrs) {
23749          delete attrs.width;
23750        }
23751  
23752        if ('height' in attrs) {
23753          delete attrs.height;
23754        }
23755  
23756        Object.getOwnPropertyNames(attrs).forEach(function (attr) {
23757          // don't copy over the class attribute to the player element when we're in a div embed
23758          // the class is already set up properly in the divEmbed case
23759          // and we want to make sure that the `video-js` class doesn't get lost
23760          if (!(divEmbed && attr === 'class')) {
23761            el.setAttribute(attr, attrs[attr]);
23762          }
23763  
23764          if (divEmbed) {
23765            tag.setAttribute(attr, attrs[attr]);
23766          }
23767        }); // Update tag id/class for use as HTML5 playback tech
23768        // Might think we should do this after embedding in container so .vjs-tech class
23769        // doesn't flash 100% width/height, but class only applies with .video-js parent
23770  
23771        tag.playerId = tag.id;
23772        tag.id += '_html5_api';
23773        tag.className = 'vjs-tech'; // Make player findable on elements
23774  
23775        tag.player = el.player = this; // Default state of video is paused
23776  
23777        this.addClass('vjs-paused'); // Add a style element in the player that we'll use to set the width/height
23778        // of the player in a way that's still overrideable by CSS, just like the
23779        // video element
23780  
23781        if (window$3.VIDEOJS_NO_DYNAMIC_STYLE !== true) {
23782          this.styleEl_ = createStyleElement('vjs-styles-dimensions');
23783          var defaultsStyleEl = $('.vjs-styles-defaults');
23784          var head = $('head');
23785          head.insertBefore(this.styleEl_, defaultsStyleEl ? defaultsStyleEl.nextSibling : head.firstChild);
23786        }
23787  
23788        this.fill_ = false;
23789        this.fluid_ = false; // Pass in the width/height/aspectRatio options which will update the style el
23790  
23791        this.width(this.options_.width);
23792        this.height(this.options_.height);
23793        this.fill(this.options_.fill);
23794        this.fluid(this.options_.fluid);
23795        this.aspectRatio(this.options_.aspectRatio); // support both crossOrigin and crossorigin to reduce confusion and issues around the name
23796  
23797        this.crossOrigin(this.options_.crossOrigin || this.options_.crossorigin); // Hide any links within the video/audio tag,
23798        // because IE doesn't hide them completely from screen readers.
23799  
23800        var links = tag.getElementsByTagName('a');
23801  
23802        for (var i = 0; i < links.length; i++) {
23803          var linkEl = links.item(i);
23804          addClass(linkEl, 'vjs-hidden');
23805          linkEl.setAttribute('hidden', 'hidden');
23806        } // insertElFirst seems to cause the networkState to flicker from 3 to 2, so
23807        // keep track of the original for later so we can know if the source originally failed
23808  
23809  
23810        tag.initNetworkState_ = tag.networkState; // Wrap video tag in div (el/box) container
23811  
23812        if (tag.parentNode && !playerElIngest) {
23813          tag.parentNode.insertBefore(el, tag);
23814        } // insert the tag as the first child of the player element
23815        // then manually add it to the children array so that this.addChild
23816        // will work properly for other components
23817        //
23818        // Breaks iPhone, fixed in HTML5 setup.
23819  
23820  
23821        prependTo(tag, el);
23822        this.children_.unshift(tag); // Set lang attr on player to ensure CSS :lang() in consistent with player
23823        // if it's been set to something different to the doc
23824  
23825        this.el_.setAttribute('lang', this.language_);
23826        this.el_ = el;
23827        return el;
23828      }
23829      /**
23830       * Get or set the `Player`'s crossOrigin option. For the HTML5 player, this
23831       * sets the `crossOrigin` property on the `<video>` tag to control the CORS
23832       * behavior.
23833       *
23834       * @see [Video Element Attributes]{@link https://developer.mozilla.org/en-US/docs/Web/HTML/Element/video#attr-crossorigin}
23835       *
23836       * @param {string} [value]
23837       *        The value to set the `Player`'s crossOrigin to. If an argument is
23838       *        given, must be one of `anonymous` or `use-credentials`.
23839       *
23840       * @return {string|undefined}
23841       *         - The current crossOrigin value of the `Player` when getting.
23842       *         - undefined when setting
23843       */
23844      ;
23845  
23846      _proto.crossOrigin = function crossOrigin(value) {
23847        if (!value) {
23848          return this.techGet_('crossOrigin');
23849        }
23850  
23851        if (value !== 'anonymous' && value !== 'use-credentials') {
23852          log.warn("crossOrigin must be \"anonymous\" or \"use-credentials\", given \"" + value + "\"");
23853          return;
23854        }
23855  
23856        this.techCall_('setCrossOrigin', value);
23857        return;
23858      }
23859      /**
23860       * A getter/setter for the `Player`'s width. Returns the player's configured value.
23861       * To get the current width use `currentWidth()`.
23862       *
23863       * @param {number} [value]
23864       *        The value to set the `Player`'s width to.
23865       *
23866       * @return {number}
23867       *         The current width of the `Player` when getting.
23868       */
23869      ;
23870  
23871      _proto.width = function width(value) {
23872        return this.dimension('width', value);
23873      }
23874      /**
23875       * A getter/setter for the `Player`'s height. Returns the player's configured value.
23876       * To get the current height use `currentheight()`.
23877       *
23878       * @param {number} [value]
23879       *        The value to set the `Player`'s heigth to.
23880       *
23881       * @return {number}
23882       *         The current height of the `Player` when getting.
23883       */
23884      ;
23885  
23886      _proto.height = function height(value) {
23887        return this.dimension('height', value);
23888      }
23889      /**
23890       * A getter/setter for the `Player`'s width & height.
23891       *
23892       * @param {string} dimension
23893       *        This string can be:
23894       *        - 'width'
23895       *        - 'height'
23896       *
23897       * @param {number} [value]
23898       *        Value for dimension specified in the first argument.
23899       *
23900       * @return {number}
23901       *         The dimension arguments value when getting (width/height).
23902       */
23903      ;
23904  
23905      _proto.dimension = function dimension(_dimension, value) {
23906        var privDimension = _dimension + '_';
23907  
23908        if (value === undefined) {
23909          return this[privDimension] || 0;
23910        }
23911  
23912        if (value === '' || value === 'auto') {
23913          // If an empty string is given, reset the dimension to be automatic
23914          this[privDimension] = undefined;
23915          this.updateStyleEl_();
23916          return;
23917        }
23918  
23919        var parsedVal = parseFloat(value);
23920  
23921        if (isNaN(parsedVal)) {
23922          log.error("Improper value \"" + value + "\" supplied for for " + _dimension);
23923          return;
23924        }
23925  
23926        this[privDimension] = parsedVal;
23927        this.updateStyleEl_();
23928      }
23929      /**
23930       * A getter/setter/toggler for the vjs-fluid `className` on the `Player`.
23931       *
23932       * Turning this on will turn off fill mode.
23933       *
23934       * @param {boolean} [bool]
23935       *        - A value of true adds the class.
23936       *        - A value of false removes the class.
23937       *        - No value will be a getter.
23938       *
23939       * @return {boolean|undefined}
23940       *         - The value of fluid when getting.
23941       *         - `undefined` when setting.
23942       */
23943      ;
23944  
23945      _proto.fluid = function fluid(bool) {
23946        if (bool === undefined) {
23947          return !!this.fluid_;
23948        }
23949  
23950        this.fluid_ = !!bool;
23951  
23952        if (isEvented(this)) {
23953          this.off('playerreset', this.updateStyleEl_);
23954        }
23955  
23956        if (bool) {
23957          this.addClass('vjs-fluid');
23958          this.fill(false);
23959          addEventedCallback(function () {
23960            this.on('playerreset', this.updateStyleEl_);
23961          });
23962        } else {
23963          this.removeClass('vjs-fluid');
23964        }
23965  
23966        this.updateStyleEl_();
23967      }
23968      /**
23969       * A getter/setter/toggler for the vjs-fill `className` on the `Player`.
23970       *
23971       * Turning this on will turn off fluid mode.
23972       *
23973       * @param {boolean} [bool]
23974       *        - A value of true adds the class.
23975       *        - A value of false removes the class.
23976       *        - No value will be a getter.
23977       *
23978       * @return {boolean|undefined}
23979       *         - The value of fluid when getting.
23980       *         - `undefined` when setting.
23981       */
23982      ;
23983  
23984      _proto.fill = function fill(bool) {
23985        if (bool === undefined) {
23986          return !!this.fill_;
23987        }
23988  
23989        this.fill_ = !!bool;
23990  
23991        if (bool) {
23992          this.addClass('vjs-fill');
23993          this.fluid(false);
23994        } else {
23995          this.removeClass('vjs-fill');
23996        }
23997      }
23998      /**
23999       * Get/Set the aspect ratio
24000       *
24001       * @param {string} [ratio]
24002       *        Aspect ratio for player
24003       *
24004       * @return {string|undefined}
24005       *         returns the current aspect ratio when getting
24006       */
24007  
24008      /**
24009       * A getter/setter for the `Player`'s aspect ratio.
24010       *
24011       * @param {string} [ratio]
24012       *        The value to set the `Player`'s aspect ratio to.
24013       *
24014       * @return {string|undefined}
24015       *         - The current aspect ratio of the `Player` when getting.
24016       *         - undefined when setting
24017       */
24018      ;
24019  
24020      _proto.aspectRatio = function aspectRatio(ratio) {
24021        if (ratio === undefined) {
24022          return this.aspectRatio_;
24023        } // Check for width:height format
24024  
24025  
24026        if (!/^\d+\:\d+$/.test(ratio)) {
24027          throw new Error('Improper value supplied for aspect ratio. The format should be width:height, for example 16:9.');
24028        }
24029  
24030        this.aspectRatio_ = ratio; // We're assuming if you set an aspect ratio you want fluid mode,
24031        // because in fixed mode you could calculate width and height yourself.
24032  
24033        this.fluid(true);
24034        this.updateStyleEl_();
24035      }
24036      /**
24037       * Update styles of the `Player` element (height, width and aspect ratio).
24038       *
24039       * @private
24040       * @listens Tech#loadedmetadata
24041       */
24042      ;
24043  
24044      _proto.updateStyleEl_ = function updateStyleEl_() {
24045        if (window$3.VIDEOJS_NO_DYNAMIC_STYLE === true) {
24046          var _width = typeof this.width_ === 'number' ? this.width_ : this.options_.width;
24047  
24048          var _height = typeof this.height_ === 'number' ? this.height_ : this.options_.height;
24049  
24050          var techEl = this.tech_ && this.tech_.el();
24051  
24052          if (techEl) {
24053            if (_width >= 0) {
24054              techEl.width = _width;
24055            }
24056  
24057            if (_height >= 0) {
vendor: 8,735 bytes, lines 24058-24262
24058              techEl.height = _height;
24059            }
24060          }
24061  
24062          return;
24063        }
24064  
24065        var width;
24066        var height;
24067        var aspectRatio;
24068        var idClass; // The aspect ratio is either used directly or to calculate width and height.
24069  
24070        if (this.aspectRatio_ !== undefined && this.aspectRatio_ !== 'auto') {
24071          // Use any aspectRatio that's been specifically set
24072          aspectRatio = this.aspectRatio_;
24073        } else if (this.videoWidth() > 0) {
24074          // Otherwise try to get the aspect ratio from the video metadata
24075          aspectRatio = this.videoWidth() + ':' + this.videoHeight();
24076        } else {
24077          // Or use a default. The video element's is 2:1, but 16:9 is more common.
24078          aspectRatio = '16:9';
24079        } // Get the ratio as a decimal we can use to calculate dimensions
24080  
24081  
24082        var ratioParts = aspectRatio.split(':');
24083        var ratioMultiplier = ratioParts[1] / ratioParts[0];
24084  
24085        if (this.width_ !== undefined) {
24086          // Use any width that's been specifically set
24087          width = this.width_;
24088        } else if (this.height_ !== undefined) {
24089          // Or calulate the width from the aspect ratio if a height has been set
24090          width = this.height_ / ratioMultiplier;
24091        } else {
24092          // Or use the video's metadata, or use the video el's default of 300
24093          width = this.videoWidth() || 300;
24094        }
24095  
24096        if (this.height_ !== undefined) {
24097          // Use any height that's been specifically set
24098          height = this.height_;
24099        } else {
24100          // Otherwise calculate the height from the ratio and the width
24101          height = width * ratioMultiplier;
24102        } // Ensure the CSS class is valid by starting with an alpha character
24103  
24104  
24105        if (/^[^a-zA-Z]/.test(this.id())) {
24106          idClass = 'dimensions-' + this.id();
24107        } else {
24108          idClass = this.id() + '-dimensions';
24109        } // Ensure the right class is still on the player for the style element
24110  
24111  
24112        this.addClass(idClass);
24113        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    ");
24114      }
24115      /**
24116       * Load/Create an instance of playback {@link Tech} including element
24117       * and API methods. Then append the `Tech` element in `Player` as a child.
24118       *
24119       * @param {string} techName
24120       *        name of the playback technology
24121       *
24122       * @param {string} source
24123       *        video source
24124       *
24125       * @private
24126       */
24127      ;
24128  
24129      _proto.loadTech_ = function loadTech_(techName, source) {
24130        var _this3 = this;
24131  
24132        // Pause and remove current playback technology
24133        if (this.tech_) {
24134          this.unloadTech_();
24135        }
24136  
24137        var titleTechName = toTitleCase(techName);
24138        var camelTechName = techName.charAt(0).toLowerCase() + techName.slice(1); // get rid of the HTML5 video tag as soon as we are using another tech
24139  
24140        if (titleTechName !== 'Html5' && this.tag) {
24141          Tech.getTech('Html5').disposeMediaElement(this.tag);
24142          this.tag.player = null;
24143          this.tag = null;
24144        }
24145  
24146        this.techName_ = titleTechName; // Turn off API access because we're loading a new tech that might load asynchronously
24147  
24148        this.isReady_ = false; // if autoplay is a string we pass false to the tech
24149        // because the player is going to handle autoplay on `loadstart`
24150  
24151        var autoplay = typeof this.autoplay() === 'string' ? false : this.autoplay(); // Grab tech-specific options from player options and add source and parent element to use.
24152  
24153        var techOptions = {
24154          source: source,
24155          autoplay: autoplay,
24156          'nativeControlsForTouch': this.options_.nativeControlsForTouch,
24157          'playerId': this.id(),
24158          'techId': this.id() + "_" + camelTechName + "_api",
24159          'playsinline': this.options_.playsinline,
24160          'preload': this.options_.preload,
24161          'loop': this.options_.loop,
24162          'muted': this.options_.muted,
24163          'poster': this.poster(),
24164          'language': this.language(),
24165          'playerElIngest': this.playerElIngest_ || false,
24166          'vtt.js': this.options_['vtt.js'],
24167          'canOverridePoster': !!this.options_.techCanOverridePoster,
24168          'enableSourceset': this.options_.enableSourceset,
24169          'Promise': this.options_.Promise
24170        };
24171        ALL.names.forEach(function (name) {
24172          var props = ALL[name];
24173          techOptions[props.getterName] = _this3[props.privateName];
24174        });
24175        assign(techOptions, this.options_[titleTechName]);
24176        assign(techOptions, this.options_[camelTechName]);
24177        assign(techOptions, this.options_[techName.toLowerCase()]);
24178  
24179        if (this.tag) {
24180          techOptions.tag = this.tag;
24181        }
24182  
24183        if (source && source.src === this.cache_.src && this.cache_.currentTime > 0) {
24184          techOptions.startTime = this.cache_.currentTime;
24185        } // Initialize tech instance
24186  
24187  
24188        var TechClass = Tech.getTech(techName);
24189  
24190        if (!TechClass) {
24191          throw new Error("No Tech named '" + titleTechName + "' exists! '" + titleTechName + "' should be registered using videojs.registerTech()'");
24192        }
24193  
24194        this.tech_ = new TechClass(techOptions); // player.triggerReady is always async, so don't need this to be async
24195  
24196        this.tech_.ready(bind(this, this.handleTechReady_), true);
24197        textTrackConverter.jsonToTextTracks(this.textTracksJson_ || [], this.tech_); // Listen to all HTML5-defined events and trigger them on the player
24198  
24199        TECH_EVENTS_RETRIGGER.forEach(function (event) {
24200          _this3.on(_this3.tech_, event, _this3["handleTech" + toTitleCase(event) + "_"]);
24201        });
24202        Object.keys(TECH_EVENTS_QUEUE).forEach(function (event) {
24203          _this3.on(_this3.tech_, event, function (eventObj) {
24204            if (_this3.tech_.playbackRate() === 0 && _this3.tech_.seeking()) {
24205              _this3.queuedCallbacks_.push({
24206                callback: _this3["handleTech" + TECH_EVENTS_QUEUE[event] + "_"].bind(_this3),
24207                event: eventObj
24208              });
24209  
24210              return;
24211            }
24212  
24213            _this3["handleTech" + TECH_EVENTS_QUEUE[event] + "_"](eventObj);
24214          });
24215        });
24216        this.on(this.tech_, 'loadstart', this.handleTechLoadStart_);
24217        this.on(this.tech_, 'sourceset', this.handleTechSourceset_);
24218        this.on(this.tech_, 'waiting', this.handleTechWaiting_);
24219        this.on(this.tech_, 'ended', this.handleTechEnded_);
24220        this.on(this.tech_, 'seeking', this.handleTechSeeking_);
24221        this.on(this.tech_, 'play', this.handleTechPlay_);
24222        this.on(this.tech_, 'firstplay', this.handleTechFirstPlay_);
24223        this.on(this.tech_, 'pause', this.handleTechPause_);
24224        this.on(this.tech_, 'durationchange', this.handleTechDurationChange_);
24225        this.on(this.tech_, 'fullscreenchange', this.handleTechFullscreenChange_);
24226        this.on(this.tech_, 'fullscreenerror', this.handleTechFullscreenError_);
24227        this.on(this.tech_, 'enterpictureinpicture', this.handleTechEnterPictureInPicture_);
24228        this.on(this.tech_, 'leavepictureinpicture', this.handleTechLeavePictureInPicture_);
24229        this.on(this.tech_, 'error', this.handleTechError_);
24230        this.on(this.tech_, 'loadedmetadata', this.updateStyleEl_);
24231        this.on(this.tech_, 'posterchange', this.handleTechPosterChange_);
24232        this.on(this.tech_, 'textdata', this.handleTechTextData_);
24233        this.on(this.tech_, 'ratechange', this.handleTechRateChange_);
24234        this.usingNativeControls(this.techGet_('controls'));
24235  
24236        if (this.controls() && !this.usingNativeControls()) {
24237          this.addTechControlsListeners_();
24238        } // Add the tech element in the DOM if it was not already there
24239        // Make sure to not insert the original video element if using Html5
24240  
24241  
24242        if (this.tech_.el().parentNode !== this.el() && (titleTechName !== 'Html5' || !this.tag)) {
24243          prependTo(this.tech_.el(), this.el());
24244        } // Get rid of the original video tag reference after the first tech is loaded
24245  
24246  
24247        if (this.tag) {
24248          this.tag.player = null;
24249          this.tag = null;
24250        }
24251      }
24252      /**
24253       * Unload and dispose of the current playback {@link Tech}.
24254       *
24255       * @private
24256       */
24257      ;
24258  
24259      _proto.unloadTech_ = function unloadTech_() {
24260        var _this4 = this;
24261  
24262        // Save the current text tracks so that we can reuse the same text tracks with the next tech
24263        ALL.names.forEach(function (name) {
24264          var props = ALL[name];
24265          _this4[props.privateName] = _this4[props.getterName]();
24266        });
24267        this.textTracksJson_ = textTrackConverter.textTracksToJson(this.tech_);
24268        this.isReady_ = false;
24269        this.tech_.dispose();
24270        this.tech_ = false;
24271  
24272        if (this.isPosterFromTech_) {
24273          this.poster_ = '';
24274          this.trigger('posterchange');
24275        }
24276  
24277        this.isPosterFromTech_ = false;
24278      }
24279      /**
24280       * Return a reference to the current {@link Tech}.
24281       * It will print a warning by default about the danger of using the tech directly
24282       * but any argument that is passed in will silence the warning.
24283       *
24284       * @param {*} [safety]
24285       *        Anything passed in to silence the warning
24286       *
24287       * @return {Tech}
24288       *         The Tech
24289       */
24290      ;
24291  
24292      _proto.tech = function tech(safety) {
24293        if (safety === undefined) {
24294          log.warn('Using the tech directly can be dangerous. I hope you know 
24294what you\'re doing.\n' + 'See https://github.com/videojs/video.js/issues/2617 for more info.\n');
24295        }
24296  
24297        return this.tech_;
24298      }
24299      /**
24300       * Set up click and touch listeners for the playback element
24301       *
24302       * - On desktops: a click on the video itself will toggle playback
24303       * - On mobile devices: a click on the video toggles controls
24304       *   which is done by toggling the user state between active and
24305       *   inactive
24306       * - A tap can signal that a user has become active or has become inactive
24307       *   e.g. a quick tap on an iPhone movie should reveal the controls. Another
24308       *   quick tap should hide them again (signaling the user is in an inactive
24309       *   viewing state)
24310       * - In addition to this, we still want the user to be considered inactive after
24311       *   a few seconds of inactivity.
24312       *
24313       * > Note: the only part of iOS interaction we can't mimic with this setup
24314       * is a touch and hold on the video element counting as activity in order to
24315       * keep the controls showing, but that shouldn't be an issue. A touch and hold
24316       * on any controls will still keep the user active
24317       *
24318       * @private
24319       */
24320      ;
24321  
24322      _proto.addTechControlsListeners_ = function addTechControlsListeners_() {
24323        // Make sure to remove all the previous listeners in case we are called multiple times.
24324        this.removeTechControlsListeners_(); // Some browsers (Chrome & IE) don't trigger a click on a flash swf, but do
24325        // trigger mousedown/up.
24326        // http://stackoverflow.com/questions/1444562/javascript-onclick-event-over-flash-object
24327        // Any touch events are set to block the mousedown event from happening
24328  
24329        this.on(this.tech_, 'mouseup', this.handleTechClick_);
24330        this.on(this.tech_, 'dblclick', this.handleTechDoubleClick_); // If the controls were hidden we don't want that to change without a tap event
24331        // so we'll check if the controls were already showing before reporting user
24332        // activity
24333  
24334        this.on(this.tech_, 'touchstart', this.handleTechTouchStart_);
24335        this.on(this.tech_, 'touchmove', this.handleTechTouchMove_);
24336        this.on(this.tech_, 'touchend', this.handleTechTouchEnd_); // The tap listener needs to come after the touchend listener because the tap
24337        // listener cancels out any reportedUserActivity when setting userActive(false)
24338  
24339        this.on(this.tech_, 'tap', this.handleTechTap_);
24340      }
24341      /**
24342       * Remove the listeners used for click and tap controls. This is needed for
24343       * toggling to controls disabled, where a tap/touch should do nothing.
24344       *
24345       * @private
24346       */
24347      ;
24348  
24349      _proto.removeTechControlsListeners_ = function removeTechControlsListeners_() {
24350        // We don't want to just use `this.off()` because there might be other needed
24351        // listeners added by techs that extend this.
24352        this.off(this.tech_, 'tap', this.handleTechTap_);
24353        this.off(this.tech_, 'touchstart', this.handleTechTouchStart_);
24354        this.off(this.tech_, 'touchmove', this.handleTechTouchMove_);
24355        this.off(this.tech_, 'touchend', this.handleTechTouchEnd_);
24356        this.off(this.tech_, 'mouseup', this.handleTechClick_);
24357        this.off(this.tech_, 'dblclick', this.handleTechDoubleClick_);
24358      }
24359      /**
24360       * Player waits for the tech to be ready
24361       *
24362       * @private
24363       */
24364      ;
24365  
24366      _proto.handleTechReady_ = function handleTechReady_() {
24367        this.triggerReady(); // Keep the same volume as before
24368  
24369        if (this.cache_.volume) {
24370          this.techCall_('setVolume', this.cache_.volume);
24371        } // Look if the tech found a higher resolution poster while loading
24372  
24373  
24374        this.handleTechPosterChange_(); // Update the duration if available
24375  
24376        this.handleTechDurationChange_();
24377      }
24378      /**
24379       * Retrigger the `loadstart` event that was triggered by the {@link Tech}. This
24380       * function will also trigger {@link Player#firstplay} if it is the first loadstart
24381       * for a video.
24382       *
24383       * @fires Player#loadstart
24384       * @fires Player#firstplay
24385       * @listens Tech#loadstart
24386       * @private
24387       */
24388      ;
24389  
24390      _proto.handleTechLoadStart_ = function handleTechLoadStart_() {
24391        // TODO: Update to use `emptied` event instead. See #1277.
24392        this.removeClass('vjs-ended');
24393        this.removeClass('vjs-seeking'); // reset the error state
24394  
24395        this.error(null); // Update the duration
24396  
24397        this.handleTechDurationChange_(); // If it's already playing we want to trigger a firstplay event now.
24398        // The firstplay event relies on both the play and loadstart events
24399        // which can happen in any order for a new source
24400  
24401        if (!this.paused()) {
24402          /**
24403           * Fired when the user agent begins looking for media data
24404           *
24405           * @event Player#loadstart
24406           * @type {EventTarget~Event}
24407           */
24408          this.trigger('loadstart');
24409          this.trigger('firstplay');
24410        } else {
24411          // reset the hasStarted state
24412          this.hasStarted(false);
24413          this.trigger('loadstart');
24414        } // autoplay happens after loadstart for the browser,
24415        // so we mimic that behavior
24416  
24417  
24418        this.manualAutoplay_(this.autoplay());
24419      }
24420      /**
24421       * Handle autoplay string values, rather than the typical boolean
24422       * values that should be handled by the tech. Note that this is not
24423       * part of any specification. Valid values and what they do can be
24424       * found on the autoplay getter at Player#autoplay()
24425       */
24426      ;
24427  
24428      _proto.manualAutoplay_ = function manualAutoplay_(type) {
24429        var _this5 = this;
24430  
24431        if (!this.tech_ || typeof type !== 'string') {
24432          return;
24433        }
24434  
24435        var muted = function muted() {
24436          var previouslyMuted = _this5.muted();
24437  
24438          _this5.muted(true);
24439  
24440          var restoreMuted = function restoreMuted() {
24441            _this5.muted(previouslyMuted);
24442          }; // restore muted on play terminatation
24443  
24444  
24445          _this5.playTerminatedQueue_.push(restoreMuted);
24446  
24447          var mutedPromise = _this5.play();
24448  
24449          if (!isPromise(mutedPromise)) {
24450            return;
24451          }
24452  
24453          return mutedPromise["catch"](restoreMuted);
24454        };
24455  
24456        var promise; // if muted defaults to true
24457        // the only thing we can do is call play
24458  
24459        if (type === 'any' && this.muted() !== true) {
24460          promise = this.play();
24461  
vendor: 14,193 bytes, lines 24462-24890
24462          if (isPromise(promise)) {
24463            promise = promise["catch"](muted);
24464          }
24465        } else if (type === 'muted' && this.muted() !== true) {
24466          promise = muted();
24467        } else {
24468          promise = this.play();
24469        }
24470  
24471        if (!isPromise(promise)) {
24472          return;
24473        }
24474  
24475        return promise.then(function () {
24476          _this5.trigger({
24477            type: 'autoplay-success',
24478            autoplay: type
24479          });
24480        })["catch"](function (e) {
24481          _this5.trigger({
24482            type: 'autoplay-failure',
24483            autoplay: type
24484          });
24485        });
24486      }
24487      /**
24488       * Update the internal source caches so that we return the correct source from
24489       * `src()`, `currentSource()`, and `currentSources()`.
24490       *
24491       * > Note: `currentSources` will not be updated if the source that is passed in exists
24492       *         in the current `currentSources` cache.
24493       *
24494       *
24495       * @param {Tech~SourceObject} srcObj
24496       *        A string or object source to update our caches to.
24497       */
24498      ;
24499  
24500      _proto.updateSourceCaches_ = function updateSourceCaches_(srcObj) {
24501        if (srcObj === void 0) {
24502          srcObj = '';
24503        }
24504  
24505        var src = srcObj;
24506        var type = '';
24507  
24508        if (typeof src !== 'string') {
24509          src = srcObj.src;
24510          type = srcObj.type;
24511        } // make sure all the caches are set to default values
24512        // to prevent null checking
24513  
24514  
24515        this.cache_.source = this.cache_.source || {};
24516        this.cache_.sources = this.cache_.sources || []; // try to get the type of the src that was passed in
24517  
24518        if (src && !type) {
24519          type = findMimetype(this, src);
24520        } // update `currentSource` cache always
24521  
24522  
24523        this.cache_.source = mergeOptions({}, srcObj, {
24524          src: src,
24525          type: type
24526        });
24527        var matchingSources = this.cache_.sources.filter(function (s) {
24528          return s.src && s.src === src;
24529        });
24530        var sourceElSources = [];
24531        var sourceEls = this.$$('source');
24532        var matchingSourceEls = [];
24533  
24534        for (var i = 0; i < sourceEls.length; i++) {
24535          var sourceObj = getAttributes(sourceEls[i]);
24536          sourceElSources.push(sourceObj);
24537  
24538          if (sourceObj.src && sourceObj.src === src) {
24539            matchingSourceEls.push(sourceObj.src);
24540          }
24541        } // if we have matching source els but not matching sources
24542        // the current source cache is not up to date
24543  
24544  
24545        if (matchingSourceEls.length && !matchingSources.length) {
24546          this.cache_.sources = sourceElSources; // if we don't have matching source or source els set the
24547          // sources cache to the `currentSource` cache
24548        } else if (!matchingSources.length) {
24549          this.cache_.sources = [this.cache_.source];
24550        } // update the tech `src` cache
24551  
24552  
24553        this.cache_.src = src;
24554      }
24555      /**
24556       * *EXPERIMENTAL* Fired when the source is set or changed on the {@link Tech}
24557       * causing the media element to reload.
24558       *
24559       * It will fire for the initial source and each subsequent source.
24560       * This event is a custom event from Video.js and is triggered by the {@link Tech}.
24561       *
24562       * The event object for this event contains a `src` property that will contain the source
24563       * that was available when the event was triggered. This is generally only necessary if Video.js
24564       * is switching techs while the source was being changed.
24565       *
24566       * It is also fired when `load` is called on the player (or media element)
24567       * because the {@link https://html.spec.whatwg.org/multipage/media.html#dom-media-load|specification for `load`}
24568       * says that the resource selection algorithm needs to be aborted and restarted.
24569       * In this case, it is very likely that the `src` property will be set to the
24570       * empty string `""` to indicate we do not know what the source will be but
24571       * that it is changing.
24572       *
24573       * *This event is currently still experimental and may change in minor releases.*
24574       * __To use this, pass `enableSourceset` option to the player.__
24575       *
24576       * @event Player#sourceset
24577       * @type {EventTarget~Event}
24578       * @prop {string} src
24579       *                The source url available when the `sourceset` was triggered.
24580       *                It will be an empty string if we cannot know what the source is
24581       *                but know that the source will change.
24582       */
24583  
24584      /**
24585       * Retrigger the `sourceset` event that was triggered by the {@link Tech}.
24586       *
24587       * @fires Player#sourceset
24588       * @listens Tech#sourceset
24589       * @private
24590       */
24591      ;
24592  
24593      _proto.handleTechSourceset_ = function handleTechSourceset_(event) {
24594        var _this6 = this;
24595  
24596        // only update the source cache when the source
24597        // was not updated using the player api
24598        if (!this.changingSrc_) {
24599          var updateSourceCaches = function updateSourceCaches(src) {
24600            return _this6.updateSourceCaches_(src);
24601          };
24602  
24603          var playerSrc = this.currentSource().src;
24604          var eventSrc = event.src; // if we have a playerSrc that is not a blob, and a tech src that is a blob
24605  
24606          if (playerSrc && !/^blob:/.test(playerSrc) && /^blob:/.test(eventSrc)) {
24607            // if both the tech source and the player source were updated we assume
24608            // something like @videojs/http-streaming did the sourceset and skip updating the source cache.
24609            if (!this.lastSource_ || this.lastSource_.tech !== eventSrc && this.lastSource_.player !== playerSrc) {
24610              updateSourceCaches = function updateSourceCaches() {};
24611            }
24612          } // update the source to the intial source right away
24613          // in some cases this will be empty string
24614  
24615  
24616          updateSourceCaches(eventSrc); // if the `sourceset` `src` was an empty string
24617          // wait for a `loadstart` to update the cache to `currentSrc`.
24618          // If a sourceset happens before a `loadstart`, we reset the state
24619  
24620          if (!event.src) {
24621            this.tech_.any(['sourceset', 'loadstart'], function (e) {
24622              // if a sourceset happens before a `loadstart` there
24623              // is nothing to do as this `handleTechSourceset_`
24624              // will be called again and this will be handled there.
24625              if (e.type === 'sourceset') {
24626                return;
24627              }
24628  
24629              var techSrc = _this6.techGet('currentSrc');
24630  
24631              _this6.lastSource_.tech = techSrc;
24632  
24633              _this6.updateSourceCaches_(techSrc);
24634            });
24635          }
24636        }
24637  
24638        this.lastSource_ = {
24639          player: this.currentSource().src,
24640          tech: event.src
24641        };
24642        this.trigger({
24643          src: event.src,
24644          type: 'sourceset'
24645        });
24646      }
24647      /**
24648       * Add/remove the vjs-has-started class
24649       *
24650       * @fires Player#firstplay
24651       *
24652       * @param {boolean} request
24653       *        - true: adds the class
24654       *        - false: remove the class
24655       *
24656       * @return {boolean}
24657       *         the boolean value of hasStarted_
24658       */
24659      ;
24660  
24661      _proto.hasStarted = function hasStarted(request) {
24662        if (request === undefined) {
24663          // act as getter, if we have no request to change
24664          return this.hasStarted_;
24665        }
24666  
24667        if (request === this.hasStarted_) {
24668          return;
24669        }
24670  
24671        this.hasStarted_ = request;
24672  
24673        if (this.hasStarted_) {
24674          this.addClass('vjs-has-started');
24675          this.trigger('firstplay');
24676        } else {
24677          this.removeClass('vjs-has-started');
24678        }
24679      }
24680      /**
24681       * Fired whenever the media begins or resumes playback
24682       *
24683       * @see [Spec]{@link https://html.spec.whatwg.org/multipage/embedded-content.html#dom-media-play}
24684       * @fires Player#play
24685       * @listens Tech#play
24686       * @private
24687       */
24688      ;
24689  
24690      _proto.handleTechPlay_ = function handleTechPlay_() {
24691        this.removeClass('vjs-ended');
24692        this.removeClass('vjs-paused');
24693        this.addClass('vjs-playing'); // hide the poster when the user hits play
24694  
24695        this.hasStarted(true);
24696        /**
24697         * Triggered whenever an {@link Tech#play} event happens. Indicates that
24698         * playback has started or resumed.
24699         *
24700         * @event Player#play
24701         * @type {EventTarget~Event}
24702         */
24703  
24704        this.trigger('play');
24705      }
24706      /**
24707       * Retrigger the `ratechange` event that was triggered by the {@link Tech}.
24708       *
24709       * If there were any events queued while the playback rate was zero, fire
24710       * those events now.
24711       *
24712       * @private
24713       * @method Player#handleTechRateChange_
24714       * @fires Player#ratechange
24715       * @listens Tech#ratechange
24716       */
24717      ;
24718  
24719      _proto.handleTechRateChange_ = function handleTechRateChange_() {
24720        if (this.tech_.playbackRate() > 0 && this.cache_.lastPlaybackRate === 0) {
24721          this.queuedCallbacks_.forEach(function (queued) {
24722            return queued.callback(queued.event);
24723          });
24724          this.queuedCallbacks_ = [];
24725        }
24726  
24727        this.cache_.lastPlaybackRate = this.tech_.playbackRate();
24728        /**
24729         * Fires when the playing speed of the audio/video is changed
24730         *
24731         * @event Player#ratechange
24732         * @type {event}
24733         */
24734  
24735        this.trigger('ratechange');
24736      }
24737      /**
24738       * Retrigger the `waiting` event that was triggered by the {@link Tech}.
24739       *
24740       * @fires Player#waiting
24741       * @listens Tech#waiting
24742       * @private
24743       */
24744      ;
24745  
24746      _proto.handleTechWaiting_ = function handleTechWaiting_() {
24747        var _this7 = this;
24748  
24749        this.addClass('vjs-waiting');
24750        /**
24751         * A readyState change on the DOM element has caused playback to stop.
24752         *
24753         * @event Player#waiting
24754         * @type {EventTarget~Event}
24755         */
24756  
24757        this.trigger('waiting'); // Browsers may emit a timeupdate event after a waiting event. In order to prevent
24758        // premature removal of the waiting class, wait for the time to change.
24759  
24760        var timeWhenWaiting = this.currentTime();
24761  
24762        var timeUpdateListener = function timeUpdateListener() {
24763          if (timeWhenWaiting !== _this7.currentTime()) {
24764            _this7.removeClass('vjs-waiting');
24765  
24766            _this7.off('timeupdate', timeUpdateListener);
24767          }
24768        };
24769  
24770        this.on('timeupdate', timeUpdateListener);
24771      }
24772      /**
24773       * Retrigger the `canplay` event that was triggered by the {@link Tech}.
24774       * > Note: This is not consistent between browsers. See #1351
24775       *
24776       * @fires Player#canplay
24777       * @listens Tech#canplay
24778       * @private
24779       */
24780      ;
24781  
24782      _proto.handleTechCanPlay_ = function handleTechCanPlay_() {
24783        this.removeClass('vjs-waiting');
24784        /**
24785         * The media has a readyState of HAVE_FUTURE_DATA or greater.
24786         *
24787         * @event Player#canplay
24788         * @type {EventTarget~Event}
24789         */
24790  
24791        this.trigger('canplay');
24792      }
24793      /**
24794       * Retrigger the `canplaythrough` event that was triggered by the {@link Tech}.
24795       *
24796       * @fires Player#canplaythrough
24797       * @listens Tech#canplaythrough
24798       * @private
24799       */
24800      ;
24801  
24802      _proto.handleTechCanPlayThrough_ = function handleTechCanPlayThrough_() {
24803        this.removeClass('vjs-waiting');
24804        /**
24805         * The media has a readyState of HAVE_ENOUGH_DATA or greater. This means that the
24806         * entire media file can be played without buffering.
24807         *
24808         * @event Player#canplaythrough
24809         * @type {EventTarget~Event}
24810         */
24811  
24812        this.trigger('canplaythrough');
24813      }
24814      /**
24815       * Retrigger the `playing` event that was triggered by the {@link Tech}.
24816       *
24817       * @fires Player#playing
24818       * @listens Tech#playing
24819       * @private
24820       */
24821      ;
24822  
24823      _proto.handleTechPlaying_ = function handleTechPlaying_() {
24824        this.removeClass('vjs-waiting');
24825        /**
24826         * The media is no longer blocked from playback, and has started playing.
24827         *
24828         * @event Player#playing
24829         * @type {EventTarget~Event}
24830         */
24831  
24832        this.trigger('playing');
24833      }
24834      /**
24835       * Retrigger the `seeking` event that was triggered by the {@link Tech}.
24836       *
24837       * @fires Player#seeking
24838       * @listens Tech#seeking
24839       * @private
24840       */
24841      ;
24842  
24843      _proto.handleTechSeeking_ = function handleTechSeeking_() {
24844        this.addClass('vjs-seeking');
24845        /**
24846         * Fired whenever the player is jumping to a new time
24847         *
24848         * @event Player#seeking
24849         * @type {EventTarget~Event}
24850         */
24851  
24852        this.trigger('seeking');
24853      }
24854      /**
24855       * Retrigger the `seeked` event that was triggered by the {@link Tech}.
24856       *
24857       * @fires Player#seeked
24858       * @listens Tech#seeked
24859       * @private
24860       */
24861      ;
24862  
24863      _proto.handleTechSeeked_ = function handleTechSeeked_() {
24864        this.removeClass('vjs-seeking');
24865        this.removeClass('vjs-ended');
24866        /**
24867         * Fired when the player has finished jumping to a new time
24868         *
24869         * @event Player#seeked
24870         * @type {EventTarget~Event}
24871         */
24872  
24873        this.trigger('seeked');
24874      }
24875      /**
24876       * Retrigger the `firstplay` event that was triggered by the {@link Tech}.
24877       *
24878       * @fires Player#firstplay
24879       * @listens Tech#firstplay
24880       * @deprecated As of 6.0 firstplay event is deprecated.
24881       *             As of 6.0 passing the `starttime` option to the player and the firstplay event are deprecated.
24882       * @private
24883       */
24884      ;
24885  
24886      _proto.handleTechFirstPlay_ = function handleTechFirstPlay_() {
24887        // If the first starttime attribute is specified
24888        // then we will start at the given offset in seconds
24889        if (this.options_.starttime) {
24890          log.warn('Passing the `starttime` option to the player will be deprecated 
24890in 6.0');
24891          this.currentTime(this.options_.starttime);
24892        }
24893  
24894        this.addClass('vjs-has-started');
24895        /**
24896         * Fired the first time a video is played. Not part of the HLS spec, and this is
24897         * probably not the best implementation yet, so use sparingly. If you don't have a
24898         * reason to prevent playback, use `myPlayer.one('play');` instead.
24899         *
24900         * @event Player#firstplay
24901         * @deprecated As of 6.0 firstplay event is deprecated.
24902         * @type {EventTarget~Event}
24903         */
24904  
24905        this.trigger('firstplay');
24906      }
24907      /**
24908       * Retrigger the `pause` event that was triggered by the {@link Tech}.
24909       *
24910       * @fires Player#pause
24911       * @listens Tech#pause
24912       * @private
24913       */
24914      ;
24915  
24916      _proto.handleTechPause_ = function handleTechPause_() {
24917        this.removeClass('vjs-playing');
24918        this.addClass('vjs-paused');
24919        /**
24920         * Fired whenever the media has been paused
24921         *
24922         * @event Player#pause
24923         * @type {EventTarget~Event}
24924         */
24925  
24926        this.trigger('pause');
24927      }
24928      /**
24929       * Retrigger the `ended` event that was triggered by the {@link Tech}.
24930       *
24931       * @fires Player#ended
24932       * @listens Tech#ended
24933       * @private
24934       */
24935      ;
24936  
24937      _proto.handleTechEnded_ = function handleTechEnded_() {
24938        this.addClass('vjs-ended');
24939  
24940        if (this.options_.loop) {
24941          this.currentTime(0);
24942          this.play();
24943        } else if (!this.paused()) {
24944          this.pause();
24945        }
24946        /**
24947         * Fired when the end of the media resource is reached (currentTime == duration)
24948         *
24949         * @event Player#ended
24950         * @type {EventTarget~Event}
24951         */
24952  
24953  
24954        this.trigger('ended');
24955      }
24956      /**
24957       * Fired when the duration of the media resource is first known or changed
24958       *
24959       * @listens Tech#durationchange
24960       * @private
24961       */
24962      ;
24963  
24964      _proto.handleTechDurationChange_ = function handleTechDurationChange_() {
24965        this.duration(this.techGet_('duration'));
24966      }
24967      /**
24968       * Handle a click on the media element to play/pause
24969       *
24970       * @param {EventTarget~Event} event
24971       *        the event that caused this function to trigger
24972       *
24973       * @listens Tech#mouseup
24974       * @private
24975       */
24976      ;
24977  
24978      _proto.handleTechClick_ = function handleTechClick_(event) {
24979        if (!isSingleLeftClick(event)) {
24980          return;
24981        } // When controls are disabled a click should not toggle playback because
24982        // the click is considered a control
24983  
24984  
24985        if (!this.controls_) {
24986          return;
24987        }
24988  
24989        if (this.paused()) {
24990          silencePromise(this.play());
24991        } else {
24992          this.pause();
24993        }
24994      }
24995      /**
24996       * Handle a double-click on the media element to enter/exit fullscreen
24997       *
24998       * @param {EventTarget~Event} event
24999       *        the event that caused this function to trigger
25000       *
25001       * @listens Tech#dblclick
25002       * @private
25003       */
25004      ;
25005  
25006      _proto.handleTechDoubleClick_ = function handleTechDoubleClick_(event) {
25007        if (!this.controls_) {
25008          return;
25009        } // we do not want to toggle fullscreen state
25010        // when double-clicking inside a control bar or a modal
25011  
25012  
25013        var inAllowedEls = Array.prototype.some.call(this.$$('.vjs-control-bar, .vjs-modal-dialog'), function (el) {
25014          return el.contains(event.target);
25015        });
25016  
25017        if (!inAllowedEls) {
25018          /*
25019           * options.userActions.doubleClick
25020           *
25021           * If `undefined` or `true`, double-click toggles fullscreen if controls are present
25022           * Set to `false` to disable double-click handling
25023           * Set to a function to substitute an external double-click handler
25024           */
25025          if (this.options_ === undefined || this.options_.userActions === undefined || this.options_.userActions.doubleClick === undefined || this.options_.userActions.doubleClick !== false) {
25026            if (this.options_ !== undefined && this.options_.userActions !== undefined && typeof this.options_.userActions.doubleClick === 'function') {
25027              this.options_.userActions.doubleClick.call(this, event);
25028            } else if (this.isFullscreen()) {
25029              this.exitFullscreen();
25030            } else {
25031              this.requestFullscreen();
25032            }
25033          }
25034        }
25035      }
25036      /**
25037       * Handle a tap on the media element. It will toggle the user
25038       * activity state, which hides and shows the controls.
25039       *
25040       * @listens Tech#tap
25041       * @private
25042       */
25043      ;
25044  
25045      _proto.handleTechTap_ = function handleTechTap_() {
25046        this.userActive(!this.userActive());
25047      }
25048      /**
25049       * Handle touch to start
25050       *
25051       * @listens Tech#touchstart
25052       * @private
25053       */
25054      ;
25055  
25056      _proto.handleTechTouchStart_ = function handleTechTouchStart_() {
25057        this.userWasActive = this.userActive();
25058      }
25059      /**
25060       * Handle touch to move
25061       *
25062       * @listens Tech#touchmove
25063       * @private
25064       */
25065      ;
25066  
25067      _proto.handleTechTouchMove_ = function handleTechTouchMove_() {
25068        if (this.userWasActive) {
25069          this.reportUserActivity();
25070        }
25071      }
25072      /**
25073       * Handle touch to end
25074       *
25075       * @param {EventTarget~Event} event
25076       *        the touchend event that triggered
25077       *        this function
25078       *
25079       * @listens Tech#touchend
25080       * @private
25081       */
25082      ;
25083  
25084      _proto.handleTechTouchEnd_ = function handleTechTouchEnd_(event) {
25085        // Stop the mouse events from also happening
25086        event.preventDefault();
25087      }
25088      /**
25089       * native click events on the SWF aren't triggered on IE11, Win8.1RT
25090       * use stageclick events triggered from inside the SWF instead
25091       *
25092       * @private
25093       * @listens stageclick
25094       */
25095      ;
25096  
25097      _proto.handleStageClick_ = function handleStageClick_() {
25098        this.reportUserActivity();
25099      }
25100      /**
25101       * @private
25102       */
25103      ;
25104  
25105      _proto.toggleFullscreenClass_ = function toggleFullscreenClass_() {
25106        if (this.isFullscreen()) {
25107          this.addClass('vjs-fullscreen');
25108        } else {
25109          this.removeClass('vjs-fullscreen');
25110        }
25111      }
25112      /**
25113       * when the document fschange event triggers it calls this
25114       */
25115      ;
25116  
25117      _proto.documentFullscreenChange_ = function documentFullscreenChange_(e) {
25118        var targetPlayer = e.target.player; // if another player was fullscreen
25119        // do a null check for targetPlayer because older firefox's would put document as e.target
25120  
25121        if (targetPlayer && targetPlayer !== this) {
25122          return;
25123        }
25124  
25125        var el = this.el();
25126        var isFs = document[this.fsApi_.fullscreenElement] === el;
25127  
25128        if (!isFs && el.matches) {
25129          isFs = el.matches(':' + this.fsApi_.fullscreen);
25130        } else if (!isFs && el.msMatchesSelector) {
25131          isFs = el.msMatchesSelector(':' + this.fsApi_.fullscreen);
25132        }
25133  
25134        this.isFullscreen(isFs);
25135      }
25136      /**
25137       * Handle Tech Fullscreen Change
25138       *
25139       * @param {EventTarget~Event} event
25140       *        the fullscreenchange event that triggered this function
25141       *
25142       * @param {Object} data
25143       *        the data that was sent with the event
25144       *
25145       * @private
25146       * @listens Tech#fullscreenchange
25147       * @fires Player#fullscreenchange
25148       */
25149      ;
25150  
25151      _proto.handleTechFullscreenChange_ = function handleTechFullscreenChange_(event, data) {
25152        if (data) {
25153          if (data.nativeIOSFullscreen) {
25154            this.toggleClass('vjs-ios-native-fs');
25155          }
25156  
25157          this.isFullscreen(data.isFullscreen);
25158        }
25159      };
25160  
25161      _proto.handleTechFullscreenError_ = function handleTechFullscreenError_(event, err) {
25162        this.trigger('fullscreenerror', err);
25163      }
25164      /**
25165       * @private
25166       */
25167      ;
25168  
25169      _proto.togglePictureInPictureClass_ = function togglePictureInPictureClass_() {
25170        if (this.isInPictureInPicture()) {
25171          this.addClass('vjs-picture-in-picture');
25172        } else {
25173          this.removeClass('vjs-picture-in-picture');
25174        }
25175      }
25176      /**
25177       * Handle Tech Enter Picture-in-Picture.
25178       *
25179       * @param {EventTarget~Event} event
25180       *        the enterpictureinpicture event that triggered this function
25181       *
25182       * @private
25183       * @listens Tech#enterpictureinpicture
25184       */
25185      ;
25186  
25187      _proto.handleTechEnterPictureInPicture_ = function handleTechEnterPictureInPicture_(event) {
25188        this.isInPictureInPicture(true);
25189      }
25190      /**
25191       * Handle Tech Leave Picture-in-Picture.
25192       *
25193       * @param {EventTarget~Event} event
25194       *        the leavepictureinpicture event that triggered this function
25195       *
25196       * @private
25197       * @listens Tech#leavepictureinpicture
25198       */
25199      ;
25200  
25201      _proto.handleTechLeavePictureInPicture_ = function handleTechLeavePictureInPicture_(event) {
25202        this.isInPictureInPicture(false);
25203      }
25204      /**
25205       * Fires when an error occurred during the loading of an audio/video.
25206       *
25207       * @private
25208       * @listens Tech#error
25209       */
25210      ;
25211  
25212      _proto.handleTechError_ = function handleTechError_() {
25213        var error = this.tech_.error();
25214        this.error(error);
25215      }
25216      /**
25217       * Retrigger the `textdata` event that was triggered by the {@link Tech}.
25218       *
25219       * @fires Player#textdata
25220       * @listens Tech#textdata
25221       * @private
25222       */
25223      ;
25224  
25225      _proto.handleTechTextData_ = function handleTechTextData_() {
25226        var data = null;
25227  
25228        if (arguments.length > 1) {
25229          data = arguments[1];
25230        }
25231        /**
25232         * Fires when we get a textdata event from tech
25233         *
25234         * @event Player#textdata
25235         * @type {EventTarget~Event}
25236         */
25237  
25238  
25239        this.trigger('textdata', data);
25240      }
25241      /**
25242       * Get object for cached values.
25243       *
25244       * @return {Object}
25245       *         get the current object cache
25246       */
25247      ;
25248  
25249      _proto.getCache = function getCache() {
25250        return this.cache_;
25251      }
25252      /**
25253       * Resets the internal cache object.
25254       *
25255       * Using this function outside the player constructor or reset method may
25256       * have unintended side-effects.
25257       *
25258       * @private
25259       */
25260      ;
25261  
25262      _proto.resetCache_ = function resetCache_() {
25263        this.cache_ = {
25264          // Right now, the currentTime is not _really_ cached because it is always
25265          // retrieved from the tech (see: currentTime). However, for completeness,
25266          // we set it to zero here to ensure that if we do start actually caching
25267          // it, we reset it along with everything else.
25268          currentTime: 0,
25269          initTime: 0,
25270          inactivityTimeout: this.options_.inactivityTimeout,
25271          duration: NaN,
25272          lastVolume: 1,
25273          lastPlaybackRate: this.defaultPlaybackRate(),
25274          media: null,
25275          src: '',
25276          source: {},
25277          sources: [],
25278          volume: 1
25279        };
25280      }
25281      /**
25282       * Pass values to the playback tech
25283       *
25284       * @param {string} [method]
25285       *        the method to call
25286       *
25287       * @param {Object} arg
25288       *        the argument to pass
25289       *
25290       * @private
25291       */
25292      ;
25293  
25294      _proto.techCall_ = function techCall_(method, arg) {
25295        // If it's not ready yet, call method when it is
25296        this.ready(function () {
25297          if (method in allowedSetters) {
25298            return set(this.middleware_, this.tech_, method, arg);
25299          } else if (method in allowedMediators) {
25300            return mediate(this.middleware_, this.tech_, method, arg);
25301          }
25302  
25303          try {
25304            if (this.tech_) {
25305              this.tech_[method](arg);
25306            }
25307          } catch (e) {
25308            log(e);
25309            throw e;
25310          }
25311        }, true);
25312      }
25313      /**
25314       * Get calls can't wait for the tech, and sometimes don't need to.
25315       *
25316       * @param {string} method
25317       *        Tech method
25318       *
25319       * @return {Function|undefined}
25320       *         the method or undefined
25321       *
25322       * @private
25323       */
25324      ;
25325  
25326      _proto.techGet_ = function techGet_(method) {
25327        if (!this.tech_ || !this.tech_.isReady_) {
25328          return;
25329        }
25330  
25331        if (method in allowedGetters) {
25332          return get(this.middleware_, this.tech_, method);
25333        } else if (method in allowedMediators) {
25334          return mediate(this.middleware_, this.tech_, method);
25335        } // Flash likes to die and reload when you hide or reposition it.
25336        // In these cases the object methods go away and we get errors.
25337        // When that happens we'll catch the errors and inform tech that it's not ready any more.
25338  
25339  
25340        try {
25341          return this.tech_[method]();
25342        } catch (e) {
25343          // When building additional tech libs, an expected method may not be defined yet
25344          if (this.tech_[method] === undefined) {
25345            log("Video.js: " + method + " method not defined for " + this.techName_ + " playback technology.", e);
25346            throw e;
25347          } // When a method isn't available on the object it throws a TypeError
25348  
25349  
25350          if (e.name === 'TypeError') {
25351            log("Video.js: " + method + " unavailable on " + this.techName_ + " playback technology element.", e);
25352            this.tech_.isReady_ = false;
25353            throw e;
25354          } // If error unknown, just log and throw
25355  
25356  
25357          log(e);
25358          throw e;
25359        }
25360      }
25361      /**
25362       * Attempt to begin playback at the first opportunity.
25363       *
25364       * @return {Promise|undefined}
25365       *         Returns a promise if the browser supports Promises (or one
25366       *         was passed in as an option). This promise will be resolved on
25367       *         the return value of play. If this is undefined it will fulfill the
25368       *         promise chain otherwise the promise chain will be fulfilled when
25369       *         the promise from play is fulfilled.
25370       */
25371      ;
25372  
25373      _proto.play = function play() {
25374        var _this8 = this;
25375  
25376        var PromiseClass = this.options_.Promise || window$3.Promise;
25377  
25378        if (PromiseClass) {
25379          return new PromiseClass(function (resolve) {
25380            _this8.play_(resolve);
25381          });
25382        }
25383  
25384        return this.play_();
25385      }
25386      /**
25387       * The actual logic for play, takes a callback that will be resolved on the
25388       * return value of play. This allows us to resolve to the play promise if there
25389       * is one on modern browsers.
25390       *
25391       * @private
25392       * @param {Function} [callback]
25393       *        The callback that should be called when the techs play is actually called
25394       */
25395      ;
25396  
25397      _proto.play_ = function play_(callback) {
25398        var _this9 = this;
25399  
25400        if (callback === void 0) {
25401          callback = silencePromise;
25402        }
25403  
25404        this.playCallbacks_.push(callback);
25405        var isSrcReady = Boolean(!this.changingSrc_ && (this.src() || this.currentSrc())); // treat calls to play_ somewhat like the `one` event function
25406  
25407        if (this.waitToPlay_) {
25408          this.off(['ready', 'loadstart'], this.waitToPlay_);
25409          this.waitToPlay_ = null;
25410        } // if the player/tech is not ready or the src itself is not ready
25411        // queue up a call to play on `ready` or `loadstart`
25412  
25413  
25414        if (!this.isReady_ || !isSrcReady) {
25415          this.waitToPlay_ = function (e) {
25416            _this9.play_();
25417          };
25418  
25419          this.one(['ready', 'loadstart'], this.waitToPlay_); // if we are in Safari, there is a high chance that loadstart will trigger after the gesture timeperiod
25420          // in that case, we need to prime the video element by calling load so it'll be ready in time
25421  
25422          if (!isSrcReady && (IS_ANY_SAFARI || IS_IOS)) {
25423            this.load();
25424          }
25425  
25426          return;
25427        } // If the player/tech is ready and we have a source, we can attempt playback.
25428  
25429  
25430        var val = this.techGet_('play'); // play was terminated if the returned value is null
25431  
25432        if (val === null) {
25433          this.runPlayTerminatedQueue_();
25434        } else {
25435          this.runPlayCallbacks_(val);
25436        }
25437      }
25438      /**
25439       * These functions will be run when if play is terminated. If play
25440       * runPlayCallbacks_ is run these function will not be run. This allows us
25441       * to differenciate between a terminated play and an actual call to play.
25442       */
25443      ;
25444  
25445      _proto.runPlayTerminatedQueue_ = function runPlayTerminatedQueue_() {
25446        var queue = this.playTerminatedQueue_.slice(0);
25447        this.playTerminatedQueue_ = [];
25448        queue.forEach(function (q) {
25449          q();
25450        });
25451      }
25452      /**
25453       * When a callback to play is delayed we have to run these
25454       * callbacks when play is actually called on the tech. This function
25455       * runs the callbacks that were delayed and accepts the return value
25456       * from the tech.
25457       *
25458       * @param {undefined|Promise} val
25459       *        The return value from the tech.
25460       */
25461      ;
25462  
25463      _proto.runPlayCallbacks_ = function runPlayCallbacks_(val) {
25464        var callbacks = this.playCallbacks_.slice(0);
25465        this.playCallbacks_ = []; // clear play terminatedQueue since we finished a real play
25466  
25467        this.playTerminatedQueue_ = [];
25468        callbacks.forEach(function (cb) {
25469          cb(val);
25470        });
25471      }
25472      /**
25473       * Pause the video playback
25474       *
25475       * @return {Player}
25476       *         A reference to the player object this function was called on
25477       */
25478      ;
25479  
25480      _proto.pause = function pause() {
25481        this.techCall_('pause');
25482      }
25483      /**
25484       * Check if the player is paused or has yet to play
25485       *
25486       * @return {boolean}
25487       *         - false: if the media is currently playing
25488       *         - true: if media is not currently playing
25489       */
25490      ;
25491  
25492      _proto.paused = function paused() {
25493        // The initial state of paused should be true (in Safari it's actually false)
25494        return this.techGet_('paused') === false ? false : true;
25495      }
25496      /**
25497       * Get a TimeRange object representing the current ranges of time that the user
25498       * has played.
25499       *
25500       * @return {TimeRange}
25501       *         A time range object that represents all the increments of time that have
25502       *         been played.
25503       */
25504      ;
25505  
25506      _proto.played = function played() {
25507        return this.techGet_('played') || createTimeRanges(0, 0);
25508      }
25509      /**
25510       * Returns whether or not the user is "scrubbing". Scrubbing is
25511       * when the user has clicked the progress bar handle and is
25512       * dragging it along the progress bar.
25513       *
25514       * @param {boolean} [isScrubbing]
25515       *        whether the user is or is not scrubbing
25516       *
25517       * @return {boolean}
25518       *         The value of scrubbing when getting
25519       */
25520      ;
25521  
25522      _proto.scrubbing = function scrubbing(isScrubbing) {
25523        if (typeof isScrubbing === 'undefined') {
25524          return this.scrubbing_;
25525        }
25526  
25527        this.scrubbing_ = !!isScrubbing;
25528  
25529        if (isScrubbing) {
25530          this.addClass('vjs-scrubbing');
25531        } else {
25532          this.removeClass('vjs-scrubbing');
25533        }
25534      }
25535      /**
25536       * Get or set the current time (in seconds)
25537       *
25538       * @param {number|string} [seconds]
25539       *        The time to seek to in seconds
25540       *
25541       * @return {number}
25542       *         - the current time in seconds when getting
25543       */
25544      ;
25545  
25546      _proto.currentTime = function currentTime(seconds) {
25547        if (typeof seconds !== 'undefined') {
25548          if (seconds < 0) {
25549            seconds = 0;
25550          }
25551  
25552          if (!this.isReady_ || this.changingSrc_ || !this.tech_ || !this.tech_.isReady_) {
25553            this.cache_.initTime = seconds;
25554            this.off('canplay', this.applyInitTime_);
25555            this.one('canplay', this.applyInitTime_);
25556            return;
25557          }
25558  
25559          this.techCall_('setCurrentTime', seconds);
25560          this.cache_.initTime = 0;
25561          return;
25562        } // cache last currentTime and return. default to 0 seconds
25563        //
25564        // Caching the currentTime is meant to prevent a massive amount of reads on the tech's
vendor: 10,254 bytes, lines 25565-25880
25565        // currentTime when scrubbing, but may not provide much performance benefit afterall.
25566        // Should be tested. Also something has to read the actual current time or the cache will
25567        // never get updated.
25568  
25569  
25570        this.cache_.currentTime = this.techGet_('currentTime') || 0;
25571        return this.cache_.currentTime;
25572      }
25573      /**
25574       * Apply the value of initTime stored in cache as currentTime.
25575       *
25576       * @private
25577       */
25578      ;
25579  
25580      _proto.applyInitTime_ = function applyInitTime_() {
25581        this.currentTime(this.cache_.initTime);
25582      }
25583      /**
25584       * Normally gets the length in time of the video in seconds;
25585       * in all but the rarest use cases an argument will NOT be passed to the method
25586       *
25587       * > **NOTE**: The video must have started loading before the duration can be
25588       * known, and in the case of Flash, may not be known until the video starts
25589       * playing.
25590       *
25591       * @fires Player#durationchange
25592       *
25593       * @param {number} [seconds]
25594       *        The duration of the video to set in seconds
25595       *
25596       * @return {number}
25597       *         - The duration of the video in seconds when getting
25598       */
25599      ;
25600  
25601      _proto.duration = function duration(seconds) {
25602        if (seconds === undefined) {
25603          // return NaN if the duration is not known
25604          return this.cache_.duration !== undefined ? this.cache_.duration : NaN;
25605        }
25606  
25607        seconds = parseFloat(seconds); // Standardize on Infinity for signaling video is live
25608  
25609        if (seconds < 0) {
25610          seconds = Infinity;
25611        }
25612  
25613        if (seconds !== this.cache_.duration) {
25614          // Cache the last set value for optimized scrubbing (esp. Flash)
25615          this.cache_.duration = seconds;
25616  
25617          if (seconds === Infinity) {
25618            this.addClass('vjs-live');
25619          } else {
25620            this.removeClass('vjs-live');
25621          }
25622  
25623          if (!isNaN(seconds)) {
25624            // Do not fire durationchange unless the duration value is known.
25625            // @see [Spec]{@link https://www.w3.org/TR/2011/WD-html5-20110113/video.html#media-element-load-algorithm}
25626  
25627            /**
25628             * @event Player#durationchange
25629             * @type {EventTarget~Event}
25630             */
25631            this.trigger('durationchange');
25632          }
25633        }
25634      }
25635      /**
25636       * Calculates how much time is left in the video. Not part
25637       * of the native video API.
25638       *
25639       * @return {number}
25640       *         The time remaining in seconds
25641       */
25642      ;
25643  
25644      _proto.remainingTime = function remainingTime() {
25645        return this.duration() - this.currentTime();
25646      }
25647      /**
25648       * A remaining time function that is intented to be used when
25649       * the time is to be displayed directly to the user.
25650       *
25651       * @return {number}
25652       *         The rounded time remaining in seconds
25653       */
25654      ;
25655  
25656      _proto.remainingTimeDisplay = function remainingTimeDisplay() {
25657        return Math.floor(this.duration()) - Math.floor(this.currentTime());
25658      } //
25659      // Kind of like an array of portions of the video that have been downloaded.
25660  
25661      /**
25662       * Get a TimeRange object with an array of the times of the video
25663       * that have been downloaded. If you just want the percent of the
25664       * video that's been downloaded, use bufferedPercent.
25665       *
25666       * @see [Buffered Spec]{@link http://dev.w3.org/html5/spec/video.html#dom-media-buffered}
25667       *
25668       * @return {TimeRange}
25669       *         A mock TimeRange object (following HTML spec)
25670       */
25671      ;
25672  
25673      _proto.buffered = function buffered() {
25674        var buffered = this.techGet_('buffered');
25675  
25676        if (!buffered || !buffered.length) {
25677          buffered = createTimeRanges(0, 0);
25678        }
25679  
25680        return buffered;
25681      }
25682      /**
25683       * Get the percent (as a decimal) of the video that's been downloaded.
25684       * This method is not a part of the native HTML video API.
25685       *
25686       * @return {number}
25687       *         A decimal between 0 and 1 representing the percent
25688       *         that is buffered 0 being 0% and 1 being 100%
25689       */
25690      ;
25691  
25692      _proto.bufferedPercent = function bufferedPercent$1() {
25693        return bufferedPercent(this.buffered(), this.duration());
25694      }
25695      /**
25696       * Get the ending time of the last buffered time range
25697       * This is used in the progress bar to encapsulate all time ranges.
25698       *
25699       * @return {number}
25700       *         The end of the last buffered time range
25701       */
25702      ;
25703  
25704      _proto.bufferedEnd = function bufferedEnd() {
25705        var buffered = this.buffered();
25706        var duration = this.duration();
25707        var end = buffered.end(buffered.length - 1);
25708  
25709        if (end > duration) {
25710          end = duration;
25711        }
25712  
25713        return end;
25714      }
25715      /**
25716       * Get or set the current volume of the media
25717       *
25718       * @param  {number} [percentAsDecimal]
25719       *         The new volume as a decimal percent:
25720       *         - 0 is muted/0%/off
25721       *         - 1.0 is 100%/full
25722       *         - 0.5 is half volume or 50%
25723       *
25724       * @return {number}
25725       *         The current volume as a percent when getting
25726       */
25727      ;
25728  
25729      _proto.volume = function volume(percentAsDecimal) {
25730        var vol;
25731  
25732        if (percentAsDecimal !== undefined) {
25733          // Force value to between 0 and 1
25734          vol = Math.max(0, Math.min(1, parseFloat(percentAsDecimal)));
25735          this.cache_.volume = vol;
25736          this.techCall_('setVolume', vol);
25737  
25738          if (vol > 0) {
25739            this.lastVolume_(vol);
25740          }
25741  
25742          return;
25743        } // Default to 1 when returning current volume.
25744  
25745  
25746        vol = parseFloat(this.techGet_('volume'));
25747        return isNaN(vol) ? 1 : vol;
25748      }
25749      /**
25750       * Get the current muted state, or turn mute on or off
25751       *
25752       * @param {boolean} [muted]
25753       *        - true to mute
25754       *        - false to unmute
25755       *
25756       * @return {boolean}
25757       *         - true if mute is on and getting
25758       *         - false if mute is off and getting
25759       */
25760      ;
25761  
25762      _proto.muted = function muted(_muted) {
25763        if (_muted !== undefined) {
25764          this.techCall_('setMuted', _muted);
25765          return;
25766        }
25767  
25768        return this.techGet_('muted') || false;
25769      }
25770      /**
25771       * Get the current defaultMuted state, or turn defaultMuted on or off. defaultMuted
25772       * indicates the state of muted on initial playback.
25773       *
25774       * ```js
25775       *   var myPlayer = videojs('some-player-id');
25776       *
25777       *   myPlayer.src("http://www.example.com/path/to/video.mp4");
25778       *
25779       *   // get, should be false
25780       *   console.log(myPlayer.defaultMuted());
25781       *   // set to true
25782       *   myPlayer.defaultMuted(true);
25783       *   // get should be true
25784       *   console.log(myPlayer.defaultMuted());
25785       * ```
25786       *
25787       * @param {boolean} [defaultMuted]
25788       *        - true to mute
25789       *        - false to unmute
25790       *
25791       * @return {boolean|Player}
25792       *         - true if defaultMuted is on and getting
25793       *         - false if defaultMuted is off and getting
25794       *         - A reference to the current player when setting
25795       */
25796      ;
25797  
25798      _proto.defaultMuted = function defaultMuted(_defaultMuted) {
25799        if (_defaultMuted !== undefined) {
25800          return this.techCall_('setDefaultMuted', _defaultMuted);
25801        }
25802  
25803        return this.techGet_('defaultMuted') || false;
25804      }
25805      /**
25806       * Get the last volume, or set it
25807       *
25808       * @param  {number} [percentAsDecimal]
25809       *         The new last volume as a decimal percent:
25810       *         - 0 is muted/0%/off
25811       *         - 1.0 is 100%/full
25812       *         - 0.5 is half volume or 50%
25813       *
25814       * @return {number}
25815       *         the current value of lastVolume as a percent when getting
25816       *
25817       * @private
25818       */
25819      ;
25820  
25821      _proto.lastVolume_ = function lastVolume_(percentAsDecimal) {
25822        if (percentAsDecimal !== undefined && percentAsDecimal !== 0) {
25823          this.cache_.lastVolume = percentAsDecimal;
25824          return;
25825        }
25826  
25827        return this.cache_.lastVolume;
25828      }
25829      /**
25830       * Check if current tech can support native fullscreen
25831       * (e.g. with built in controls like iOS, so not our flash swf)
25832       *
25833       * @return {boolean}
25834       *         if native fullscreen is supported
25835       */
25836      ;
25837  
25838      _proto.supportsFullScreen = function supportsFullScreen() {
25839        return this.techGet_('supportsFullScreen') || false;
25840      }
25841      /**
25842       * Check if the player is in fullscreen mode or tell the player that it
25843       * is or is not in fullscreen mode.
25844       *
25845       * > NOTE: As of the latest HTML5 spec, isFullscreen is no longer an official
25846       * property and instead document.fullscreenElement is used. But isFullscreen is
25847       * still a valuable property for internal player workings.
25848       *
25849       * @param  {boolean} [isFS]
25850       *         Set the players current fullscreen state
25851       *
25852       * @return {boolean}
25853       *         - true if fullscreen is on and getting
25854       *         - false if fullscreen is off and getting
25855       */
25856      ;
25857  
25858      _proto.isFullscreen = function isFullscreen(isFS) {
25859        if (isFS !== undefined) {
25860          var oldValue = this.isFullscreen_;
25861          this.isFullscreen_ = Boolean(isFS); // if we changed fullscreen state and we're in prefixed mode, trigger fullscreenchange
25862          // this is the only place where we trigger fullscreenchange events for older browsers
25863          // fullWindow mode is treated as a prefixed event and will get a fullscreenchange event as well
25864  
25865          if (this.isFullscreen_ !== oldValue && this.fsApi_.prefixed) {
25866            /**
25867               * @event Player#fullscreenchange
25868               * @type {EventTarget~Event}
25869               */
25870            this.trigger('fullscreenchange');
25871          }
25872  
25873          this.toggleFullscreenClass_();
25874          return;
25875        }
25876  
25877        return this.isFullscreen_;
25878      }
25879      /**
25880       * Increase the size of the video to full screen
25881       * In some browsers, full screen is not supported natively, so it enters
25882       * "full window mode", where the video fills the browser window.
25883       * In browsers and devices that support native full screen, sometimes the
25884       * browser's default controls will be shown, and not the Video.js custom skin.
25885       * This includes most mobile devices (iOS, Android) and older versions of
25886       * Safari.
25887       *
25888       * @param  {Object} [fullscreenOptions]
25889       *         Override the player fullscreen options
25890       *
25891       * @fires Player#fullscreenchange
25892       */
25893      ;
25894  
25895      _proto.requestFullscreen = function requestFullscreen(fullscreenOptions) {
25896        var PromiseClass = this.options_.Promise || window$3.Promise;
25897  
25898        if (PromiseClass) {
25899          var self = this;
25900          return new PromiseClass(function (resolve, reject) {
25901            function offHandler() {
25902              self.off('fullscreenerror', errorHandler);
25903              self.off('fullscreenchange', changeHandler);
25904            }
25905  
25906            function changeHandler() {
25907              offHandler();
25908              resolve();
25909            }
25910  
25911            function errorHandler(e, err) {
25912              offHandler();
25913              reject(err);
25914            }
25915  
25916            self.one('fullscreenchange', changeHandler);
25917            self.one('fullscreenerror', errorHandler);
25918            var promise = self.requestFullscreenHelper_(fullscreenOptions);
25919  
25920            if (promise) {
25921              promise.then(offHandler, offHandler);
25922              return promise;
25923            }
25924          });
25925        }
25926  
25927        return this.requestFullscreenHelper_();
25928      };
25929  
25930      _proto.requestFullscreenHelper_ = function requestFullscreenHelper_(fullscreenOptions) {
25931        var _this10 = this;
25932  
25933        var fsOptions; // Only pass fullscreen options to requestFullscreen in spec-compliant browsers.
25934        // Use defaults or player configured option unless passed directly to this method.
25935  
25936        if (!this.fsApi_.prefixed) {
25937          fsOptions = this.options_.fullscreen && this.options_.fullscreen.options || {};
25938  
25939          if (fullscreenOptions !== undefined) {
25940            fsOptions = fullscreenOptions;
25941          }
25942        } // This method works as follows:
25943        // 1. if a fullscreen api is available, use it
25944        //   1. call requestFullscreen with potential options
25945        //   2. if we got a promise from above, use it to update isFullscreen()
25946        // 2. otherwise, if the tech supports fullscreen, call `enterFullScreen` on it.
25947        //   This is particularly used for iPhone, older iPads, and non-safari browser on iOS.
25948        // 3. otherwise, use "fullWindow" mode
25949  
25950  
25951        if (this.fsApi_.requestFullscreen) {
25952          var promise = this.el_[this.fsApi_.requestFullscreen](fsOptions);
25953  
25954          if (promise) {
25955            promise.then(function () {
25956              return _this10.isFullscreen(true);
25957            }, function () {
25958              return _this10.isFullscreen(false);
25959            });
25960          }
25961  
25962          return promise;
25963        } else if (this.tech_.supportsFullScreen()) {
25964          // we can't take the video.js controls fullscreen but we can go fullscreen
25965          // with native controls
25966          this.techCall_('enterFullScreen');
25967        } else {
25968          // fullscreen isn't supported so we'll just stretch the video element to
25969          // fill the viewport
25970          this.enterFullWindow();
25971        }
25972      }
25973      /**
25974       * Return the video to its normal size after having been in full screen mode
25975       *
25976       * @fires Player#fullscreenchange
25977       */
25978      ;
25979  
25980      _proto.exitFullscreen = function exitFullscreen() {
25981        var PromiseClass = this.options_.Promise || window$3.Promise;
25982  
25983        if (PromiseClass) {
25984          var self = this;
25985          return new PromiseClass(function (resolve, reject) {
25986            function offHandler() {
25987              self.off('fullscreenerror', errorHandler);
25988              self.off('fullscreenchange', changeHandler);
25989            }
25990  
25991            function changeHandler() {
25992              offHandler();
25993              resolve();
25994            }
25995  
25996            function errorHandler(e, err) {
25997              offHandler();
25998              reject(err);
25999            }
26000  
26001            self.one('fullscreenchange', changeHandler);
26002            self.one('fullscreenerror', errorHandler);
26003            var promise = self.exitFullscreenHelper_();
26004  
26005            if (promise) {
26006              promise.then(offHandler, offHandler);
26007              return promise;
26008            }
26009          });
26010        }
26011  
26012        return this.exitFullscreenHelper_();
26013      };
26014  
26015      _proto.exitFullscreenHelper_ = function exitFullscreenHelper_() {
26016        var _this11 = this;
26017  
26018        if (this.fsApi_.requestFullscreen) {
26019          var promise = document[this.fsApi_.exitFullscreen]();
26020  
26021          if (promise) {
26022            promise.then(function () {
26023              return _this11.isFullscreen(false);
26024            });
26025          }
26026  
26027          return promise;
26028        } else if (this.tech_.supportsFullScreen()) {
26029          this.techCall_('exitFullScreen');
26030        } else {
26031          this.exitFullWindow();
26032        }
26033      }
26034      /**
26035       * When fullscreen isn't supported we can stretch the
26036       * video container to as wide as the browser will let us.
26037       *
26038       * @fires Player#enterFullWindow
26039       */
26040      ;
26041  
26042      _proto.enterFullWindow = function enterFullWindow() {
26043        this.isFullscreen(true);
26044        this.isFullWindow = true; // Storing original doc overflow value to return to when fullscreen is off
26045  
26046        this.docOrigOverflow = document.documentElement.style.overflow; // Add listener for esc key to exit fullscreen
26047  
26048        on(document, 'keydown', this.boundFullWindowOnEscKey_); // Hide any scroll bars
26049  
26050        document.documentElement.style.overflow = 'hidden'; // Apply fullscreen styles
26051  
26052        addClass(document.body, 'vjs-full-window');
26053        /**
26054         * @event Player#enterFullWindow
26055         * @type {EventTarget~Event}
26056         */
26057  
26058        this.trigger('enterFullWindow');
26059      }
26060      /**
26061       * Check for call to either exit full window or
26062       * full screen on ESC key
26063       *
26064       * @param {string} event
26065       *        Event to check for key press
26066       */
26067      ;
26068  
26069      _proto.fullWindowOnEscKey = function fullWindowOnEscKey(event) {
26070        if (keycode.isEventKey(event, 'Esc')) {
26071          if (this.isFullscreen() === true) {
26072            this.exitFullscreen();
26073          } else {
26074            this.exitFullWindow();
26075          }
26076        }
26077      }
26078      /**
26079       * Exit full window
26080       *
26081       * @fires Player#exitFullWindow
26082       */
26083      ;
26084  
26085      _proto.exitFullWindow = function exitFullWindow() {
26086        this.isFullscreen(false);
26087        this.isFullWindow = false;
26088        off(document, 'keydown', this.boundFullWindowOnEscKey_); // Unhide scroll bars.
26089  
26090        document.documentElement.style.overflow = this.docOrigOverflow; // Remove fullscreen styles
26091  
26092        removeClass(document.body, 'vjs-full-window'); // Resize the box, controller, and poster to original sizes
26093        // this.positionAll();
26094  
26095        /**
26096         * @event Player#exitFullWindow
26097         * @type {EventTarget~Event}
26098         */
26099  
26100        this.trigger('exitFullWindow');
26101      }
26102      /**
26103       * Check if the player is in Picture-in-Picture mode or tell the player that it
26104       * is or is not in Picture-in-Picture mode.
26105       *
26106       * @param  {boolean} [isPiP]
26107       *         Set the players current Picture-in-Picture state
26108       *
26109       * @return {boolean}
26110       *         - true if Picture-in-Picture is on and getting
26111       *         - false if Picture-in-Picture is off and getting
26112       */
26113      ;
26114  
26115      _proto.isInPictureInPicture = function isInPictureInPicture(isPiP) {
26116        if (isPiP !== undefined) {
26117          this.isInPictureInPicture_ = !!isPiP;
26118          this.togglePictureInPictureClass_();
26119          return;
26120        }
26121  
26122        return !!this.isInPictureInPicture_;
26123      }
26124      /**
26125       * Create a floating video window always on top of other windows so that users may
26126       * continue consuming media while they interact with other content sites, or
26127       * applications on their device.
26128       *
26129       * @see [Spec]{@link https://wicg.github.io/picture-in-picture}
26130       *
26131       * @fires Player#enterpictureinpicture
26132       *
26133       * @return {Promise}
26134       *         A promise with a Picture-in-Picture window.
26135       */
26136      ;
26137  
26138      _proto.requestPictureInPicture = function requestPictureInPicture() {
26139        if ('pictureInPictureEnabled' in document) {
26140          /**
26141           * This event fires when the player enters picture in picture mode
26142           *
26143           * @event Player#enterpictureinpicture
26144           * @type {EventTarget~Event}
26145           */
26146          return this.techGet_('requestPictureInPicture');
26147        }
26148      }
26149      /**
26150       * Exit Picture-in-Picture mode.
26151       *
26152       * @see [Spec]{@link https://wicg.github.io/picture-in-picture}
26153       *
26154       * @fires Player#leavepictureinpicture
26155       *
26156       * @return {Promise}
26157       *         A promise.
26158       */
26159      ;
26160  
26161      _proto.exitPictureInPicture = function exitPictureInPicture() {
26162        if ('pictureInPictureEnabled' in document) {
26163          /**
26164           * This event fires when the player leaves picture in picture mode
26165           *
26166           * @event Player#leavepictureinpicture
26167           * @type {EventTarget~Event}
26168           */
26169          return document.exitPictureInPicture();
26170        }
26171      }
26172      /**
26173       * Called when this Player has focus and a key gets pressed down, or when
26174       * any Component of this player receives a key press that it doesn't handle.
26175       * This allows player-wide hotkeys (either as defined below, or optionally
26176       * by an external function).
26177       *
26178       * @param {EventTarget~Event} event
26179       *        The `keydown` event that caused this function to be called.
26180       *
26181       * @listens keydown
26182       */
26183      ;
26184  
26185      _proto.handleKeyDown = function handleKeyDown(event) {
26186        var userActions = this.options_.userActions; // Bail out if hotkeys are not configured.
26187  
26188        if (!userActions || !userActions.hotkeys) {
26189          return;
26190        } // Function that determines whether or not to exclude an element from
26191        // hotkeys handling.
26192  
26193  
26194        var excludeElement = function excludeElement(el) {
26195          var tagName = el.tagName.toLowerCase(); // The first and easiest test is for `contenteditable` elements.
26196  
26197          if (el.isContentEditable) {
26198            return true;
26199          } // Inputs matching these types will still trigger hotkey handling as
26200          // they are not text inputs.
26201  
26202  
26203          var allowedInputTypes = ['button', 'checkbox', 'hidden', 'radio', 'reset', 'submit'];
26204  
26205          if (tagName === 'input') {
26206            return allowedInputTypes.indexOf(el.type) === -1;
26207          } // The final test is by tag name. These tags will be excluded entirely.
26208  
26209  
26210          var excludedTags = ['textarea'];
26211          return excludedTags.indexOf(tagName) !== -1;
26212        }; // Bail out if the user is focused on an interactive form element.
26213  
26214  
26215        if (excludeElement(this.el_.ownerDocument.activeElement)) {
26216          return;
26217        }
26218  
26219        if (typeof userActions.hotkeys === 'function') {
26220          userActions.hotkeys.call(this, event);
26221        } else {
26222          this.handleHotkeys(event);
26223        }
26224      }
26225      /**
26226       * Called when this Player receives a hotkey keydown event.
26227       * Supported player-wide hotkeys are:
26228       *
26229       *   f          - toggle fullscreen
26230       *   m          - toggle mute
26231       *   k or Space - toggle play/pause
26232       *
26233       * @param {EventTarget~Event} event
26234       *        The `keydown` event that caused this function to be called.
26235       */
26236      ;
26237  
26238      _proto.handleHotkeys = function handleHotkeys(event) {
26239        var hotkeys = this.options_.userActions ? this.options_.userActions.hotkeys : {}; // set fullscreenKey, muteKey, playPauseKey from `hotkeys`, use defaults if not set
26240  
26241        var _hotkeys$fullscreenKe = hotkeys.fullscreenKey,
26242            fullscreenKey = _hotkeys$fullscreenKe === void 0 ? function (keydownEvent) {
26243          return keycode.isEventKey(keydownEvent, 'f');
26244        } : _hotkeys$fullscreenKe,
26245            _hotkeys$muteKey = hotkeys.muteKey,
26246            muteKey = _hotkeys$muteKey === void 0 ? function (keydownEvent) {
26247          return keycode.isEventKey(keydownEvent, 'm');
26248        } : _hotkeys$muteKey,
26249            _hotkeys$playPauseKey = hotkeys.playPauseKey,
26250            playPauseKey = _hotkeys$playPauseKey === void 0 ? function (keydownEvent) {
26251          return keycode.isEventKey(keydownEvent, 'k') || keycode.isEventKey(keydownEvent, 'Space');
26252        } : _hotkeys$playPauseKey;
26253  
26254        if (fullscreenKey.call(this, event)) {
26255          event.preventDefault();
26256          event.stopPropagation();
26257          var FSToggle = Component.getComponent('FullscreenToggle');
26258  
26259          if (document[this.fsApi_.fullscreenEnabled] !== false) {
26260            FSToggle.prototype.handleClick.call(this, event);
26261          }
26262        } else if (muteKey.call(this, event)) {
26263          event.preventDefault();
26264          event.stopPropagation();
26265          var MuteToggle = Component.getComponent('MuteToggle');
26266          MuteToggle.prototype.handleClick.call(this, event);
26267        } else if (playPauseKey.call(this, event)) {
26268          event.preventDefault();
26269          event.stopPropagation();
26270          var PlayToggle = Component.getComponent('PlayToggle');
26271          PlayToggle.prototype.handleClick.call(this, event);
26272        }
26273      }
26274      /**
26275       * Check whether the player can play a given mimetype
26276       *
26277       * @see https://www.w3.org/TR/2011/WD-html5-20110113/video.html#dom-navigator-canplaytype
26278       *
26279       * @param {string} type
26280       *        The mimetype to check
26281       *
26282       * @return {string}
26283       *         'probably', 'maybe', or '' (empty string)
26284       */
26285      ;
26286  
26287      _proto.canPlayType = function canPlayType(type) {
26288        var can; // Loop through each playback technology in the options order
26289  
26290        for (var i = 0, j = this.options_.techOrder; i < j.length; i++) {
26291          var techName = j[i];
26292          var tech = Tech.getTech(techName); // Support old behavior of techs being registered as components.
26293          // Remove once that deprecated behavior is removed.
26294  
26295          if (!tech) {
26296            tech = Component.getComponent(techName);
26297          } // Check if the current tech is defined before continuing
26298  
26299  
26300          if (!tech) {
26301            log.error("The \"" + techName + "\" tech is undefined. Skipped browser support check for that tech.");
26302            continue;
26303          } // Check if the browser supports this technology
26304  
26305  
26306          if (tech.isSupported()) {
26307            can = tech.canPlayType(type);
26308  
26309            if (can) {
26310              return can;
26311            }
26312          }
26313        }
26314  
26315        return '';
26316      }
26317      /**
26318       * Select source based on tech-order or source-order
26319       * Uses source-order selection if `options.sourceOrder` is truthy. Otherwise,
26320       * defaults to tech-order selection
26321       *
26322       * @param {Array} sources
26323       *        The sources for a media asset
26324       *
26325       * @return {Object|boolean}
26326       *         Object of source and tech order or false
26327       */
26328      ;
26329  
26330      _proto.selectSource = function selectSource(sources) {
26331        var _this12 = this;
26332  
26333        // Get only the techs specified in `techOrder` that exist and are supported by the
26334        // current platform
26335        var techs = this.options_.techOrder.map(function (techName) {
26336          return [techName, Tech.getTech(techName)];
26337        }).filter(function (_ref) {
26338          var techName = _ref[0],
26339              tech = _ref[1];
26340  
26341          // Check if the current tech is defined before continuing
26342          if (tech) {
26343            // Check if the browser supports this technology
26344            return tech.isSupported();
26345          }
26346  
26347          log.error("The \"" + techName + "\" tech is undefined. Skipped browser support check for that tech.");
26348          return false;
vendor: 4,460 bytes, lines 26349-26470
26349        }); // Iterate over each `innerArray` element once per `outerArray` element and execute
26350        // `tester` with both. If `tester` returns a non-falsy value, exit early and return
26351        // that value.
26352  
26353        var findFirstPassingTechSourcePair = function findFirstPassingTechSourcePair(outerArray, innerArray, tester) {
26354          var found;
26355          outerArray.some(function (outerChoice) {
26356            return innerArray.some(function (innerChoice) {
26357              found = tester(outerChoice, innerChoice);
26358  
26359              if (found) {
26360                return true;
26361              }
26362            });
26363          });
26364          return found;
26365        };
26366  
26367        var foundSourceAndTech;
26368  
26369        var flip = function flip(fn) {
26370          return function (a, b) {
26371            return fn(b, a);
26372          };
26373        };
26374  
26375        var finder = function finder(_ref2, source) {
26376          var techName = _ref2[0],
26377              tech = _ref2[1];
26378  
26379          if (tech.canPlaySource(source, _this12.options_[techName.toLowerCase()])) {
26380            return {
26381              source: source,
26382              tech: techName
26383            };
26384          }
26385        }; // Depending on the truthiness of `options.sourceOrder`, we swap the order of techs and sources
26386        // to select from them based on their priority.
26387  
26388  
26389        if (this.options_.sourceOrder) {
26390          // Source-first ordering
26391          foundSourceAndTech = findFirstPassingTechSourcePair(sources, techs, flip(finder));
26392        } else {
26393          // Tech-first ordering
26394          foundSourceAndTech = findFirstPassingTechSourcePair(techs, sources, finder);
26395        }
26396  
26397        return foundSourceAndTech || false;
26398      }
26399      /**
26400       * Get or set the video source.
26401       *
26402       * @param {Tech~SourceObject|Tech~SourceObject[]|string} [source]
26403       *        A SourceObject, an array of SourceObjects, or a string referencing
26404       *        a URL to a media source. It is _highly recommended_ that an object
26405       *        or array of objects is used here, so that source selection
26406       *        algorithms can take the `type` into account.
26407       *
26408       *        If not provided, this method acts as a getter.
26409       *
26410       * @return {string|undefined}
26411       *         If the `source` argument is missing, returns the current source
26412       *         URL. Otherwise, returns nothing/undefined.
26413       */
26414      ;
26415  
26416      _proto.src = function src(source) {
26417        var _this13 = this;
26418  
26419        // getter usage
26420        if (typeof source === 'undefined') {
26421          return this.cache_.src || '';
26422        } // filter out invalid sources and turn our source into
26423        // an array of source objects
26424  
26425  
26426        var sources = filterSource(source); // if a source was passed in then it is invalid because
26427        // it was filtered to a zero length Array. So we have to
26428        // show an error
26429  
26430        if (!sources.length) {
26431          this.setTimeout(function () {
26432            this.error({
26433              code: 4,
26434              message: this.localize(this.options_.notSupportedMessage)
26435            });
26436          }, 0);
26437          return;
26438        } // intial sources
26439  
26440  
26441        this.changingSrc_ = true;
26442        this.cache_.sources = sources;
26443        this.updateSourceCaches_(sources[0]); // middlewareSource is the source after it has been changed by middleware
26444  
26445        setSource(this, sources[0], function (middlewareSource, mws) {
26446          _this13.middleware_ = mws; // since sourceSet is async we have to update the cache again after we select a source since
26447          // the source that is selected could be out of order from the cache update above this callback.
26448  
26449          _this13.cache_.sources = sources;
26450  
26451          _this13.updateSourceCaches_(middlewareSource);
26452  
26453          var err = _this13.src_(middlewareSource);
26454  
26455          if (err) {
26456            if (sources.length > 1) {
26457              return _this13.src(sources.slice(1));
26458            }
26459  
26460            _this13.changingSrc_ = false; // We need to wrap this in a timeout to give folks a chance to add error event handlers
26461  
26462            _this13.setTimeout(function () {
26463              this.error({
26464                code: 4,
26465                message: this.localize(this.options_.notSupportedMessage)
26466              });
26467            }, 0); // we could not find an appropriate tech, but let's still notify the delegate that this is it
26468            // this needs a better comment about why this is needed
26469  
26470  
26471            _this13.triggerReady();
26472  
26473            return;
26474          }
26475  
26476          setTech(mws, _this13.tech_);
26477        });
26478      }
26479      /**
26480       * Set the source object on the tech, returns a boolean that indicates whether
26481       * there is a tech that can play the source or not
26482       *
26483       * @param {Tech~SourceObject} source
26484       *        The source object to set on the Tech
26485       *
26486       * @return {boolean}
26487       *         - True if there is no Tech to playback this source
26488       *         - False otherwise
26489       *
26490       * @private
26491       */
26492      ;
26493  
26494      _proto.src_ = function src_(source) {
26495        var _this14 = this;
26496  
26497        var sourceTech = this.selectSource([source]);
26498  
26499        if (!sourceTech) {
26500          return true;
26501        }
26502  
26503        if (!titleCaseEquals(sourceTech.tech, this.techName_)) {
26504          this.changingSrc_ = true; // load this technology with the chosen source
26505  
26506          this.loadTech_(sourceTech.tech, sourceTech.source);
26507          this.tech_.ready(function () {
26508            _this14.changingSrc_ = false;
26509          });
26510          return false;
26511        } // wait until the tech is ready to set the source
26512        // and set it synchronously if possible (#2326)
26513  
26514  
26515        this.ready(function () {
26516          // The setSource tech method was added with source handlers
26517          // so older techs won't support it
26518          // We need to check the direct prototype for the case where subclasses
26519          // of the tech do not support source handlers
26520          if (this.tech_.constructor.prototype.hasOwnProperty('setSource')) {
26521            this.techCall_('setSource', source);
26522          } else {
26523            this.techCall_('src', source.src);
26524          }
26525  
26526          this.changingSrc_ = false;
26527        }, true);
26528        return false;
26529      }
26530      /**
26531       * Begin loading the src data.
26532       */
26533      ;
26534  
26535      _proto.load = function load() {
26536        this.techCall_('load');
26537      }
26538      /**
26539       * Reset the player. Loads the first tech in the techOrder,
26540       * removes all the text tracks in the existing `tech`,
26541       * and calls `reset` on the `tech`.
26542       */
26543      ;
26544  
26545      _proto.reset = function reset() {
26546        var _this15 = this;
26547  
26548        var PromiseClass = this.options_.Promise || window$3.Promise;
26549  
26550        if (this.paused() || !PromiseClass) {
26551          this.doReset_();
26552        } else {
26553          var playPromise = this.play();
26554          silencePromise(playPromise.then(function () {
26555            return _this15.doReset_();
26556          }));
26557        }
26558      };
26559  
26560      _proto.doReset_ = function doReset_() {
26561        if (this.tech_) {
26562          this.tech_.clearTracks('text');
26563        }
26564  
26565        this.resetCache_();
26566        this.poster('');
26567        this.loadTech_(this.options_.techOrder[0], null);
26568        this.techCall_('reset');
26569        this.resetControlBarUI_();
26570  
26571        if (isEvented(this)) {
26572          this.trigger('playerreset');
26573        }
26574      }
26575      /**
26576       * Reset Control Bar's UI by calling sub-methods that reset
26577       * all of Control Bar's components
26578       */
26579      ;
26580  
26581      _proto.resetControlBarUI_ = function resetControlBarUI_() {
26582        this.resetProgressBar_();
26583        this.resetPlaybackRate_();
26584        this.resetVolumeBar_();
26585      }
26586      /**
26587       * Reset tech's progress so progress bar is reset in the UI
26588       */
26589      ;
26590  
26591      _proto.resetProgressBar_ = function resetProgressBar_() {
26592        this.currentTime(0);
26593        var _this$controlBar = this.controlBar,
26594            durationDisplay = _this$controlBar.durationDisplay,
26595            remainingTimeDisplay = _this$controlBar.remainingTimeDisplay;
26596  
26597        if (durationDisplay) {
26598          durationDisplay.updateContent();
26599        }
26600  
26601        if (remainingTimeDisplay) {
26602          remainingTimeDisplay.updateContent();
26603        }
26604      }
26605      /**
26606       * Reset Playback ratio
26607       */
26608      ;
26609  
26610      _proto.resetPlaybackRate_ = function resetPlaybackRate_() {
26611        this.playbackRate(this.defaultPlaybackRate());
26612        this.handleTechRateChange_();
26613      }
26614      /**
26615       * Reset Volume bar
26616       */
26617      ;
26618  
26619      _proto.resetVolumeBar_ = function resetVolumeBar_() {
26620        this.volume(1.0);
26621        this.trigger('volumechange');
26622      }
26623      /**
26624       * Returns all of the current source objects.
26625       *
26626       * @return {Tech~SourceObject[]}
26627       *         The current source objects
26628       */
26629      ;
26630  
26631      _proto.currentSources = function currentSources() {
26632        var source = this.currentSource();
26633        var sources = []; // assume `{}` or `{ src }`
26634  
26635        if (Object.keys(source).length !== 0) {
26636          sources.push(source);
26637        }
26638  
26639        return this.cache_.sources || sources;
26640      }
26641      /**
26642       * Returns the current source object.
26643       *
26644       * @return {Tech~SourceObject}
26645       *         The current source object
26646       */
26647      ;
26648  
26649      _proto.currentSource = function currentSource() {
26650        return this.cache_.source || {};
26651      }
26652      /**
26653       * Returns the fully qualified URL of the current source value e.g. http://mysite.com/video.mp4
26654       * Can be used in conjunction with `currentType` to assist in rebuilding the current source object.
26655       *
26656       * @return {string}
26657       *         The current source
26658       */
26659      ;
26660  
26661      _proto.currentSrc = function currentSrc() {
26662        return this.currentSource() && this.currentSource().src || '';
26663      }
26664      /**
26665       * Get the current source type e.g. video/mp4
26666       * This can allow you rebuild the current source object so that you could load the same
26667       * source and tech later
26668       *
26669       * @return {string}
26670       *         The source MIME type
26671       */
26672      ;
26673  
26674      _proto.currentType = function currentType() {
26675        return this.currentSource() && this.currentSource().type || '';
26676      }
26677      /**
26678       * Get or set the preload attribute
26679       *
26680       * @param {boolean} [value]
26681       *        - true means that we should preload
26682       *        - false means that we should not preload
26683       *
26684       * @return {string}
26685       *         The preload attribute value when getting
26686       */
26687      ;
26688  
26689      _proto.preload = function preload(value) {
26690        if (value !== undefined) {
26691          this.techCall_('setPreload', value);
26692          this.options_.preload = value;
26693          return;
26694        }
26695  
26696        return this.techGet_('preload');
26697      }
26698      /**
26699       * Get or set the autoplay option. When this is a boolean it will
26700       * modify the attribute on the tech. When this is a string the attribute on
26701       * the tech will be removed and `Player` will handle autoplay on loadstarts.
26702       *
26703       * @param {boolean|string} [value]
26704       *        - true: autoplay using the browser behavior
26705       *        - false: do not autoplay
26706       *        - 'play': call play() on every loadstart
26707       *        - 'muted': call muted() then play() on every loadstart
26708       *        - 'any': call play() on every loadstart. if that fails call muted() then play().
26709       *        - *: values other than those listed here will be set `autoplay` to true
26710       *
26711       * @return {boolean|string}
26712       *         The current value of autoplay when getting
26713       */
26714      ;
26715  
26716      _proto.autoplay = function autoplay(value) {
26717        // getter usage
26718        if (value === undefined) {
26719          return this.options_.autoplay || false;
26720        }
26721  
26722        var techAutoplay; // if the value is a valid string set it to that
26723  
26724        if (typeof value === 'string' && /(any|play|muted)/.test(value)) {
26725          this.options_.autoplay = value;
26726          this.manualAutoplay_(value);
26727          techAutoplay = false;
vendor: 26,217 bytes, lines 26727-27517
26727 // any falsy value sets autoplay to false in the browser,
26728          // lets do the same
26729        } else if (!value) {
26730          this.options_.autoplay = false; // any other value (ie truthy) sets autoplay to true
26731        } else {
26732          this.options_.autoplay = true;
26733        }
26734  
26735        techAutoplay = typeof techAutoplay === 'undefined' ? this.options_.autoplay : techAutoplay; // if we don't have a tech then we do not queue up
26736        // a setAutoplay call on tech ready. We do this because the
26737        // autoplay option will be passed in the constructor and we
26738        // do not need to set it twice
26739  
26740        if (this.tech_) {
26741          this.techCall_('setAutoplay', techAutoplay);
26742        }
26743      }
26744      /**
26745       * Set or unset the playsinline attribute.
26746       * Playsinline tells the browser that non-fullscreen playback is preferred.
26747       *
26748       * @param {boolean} [value]
26749       *        - true means that we should try to play inline by default
26750       *        - false means that we should use the browser's default playback mode,
26751       *          which in most cases is inline. iOS Safari is a notable exception
26752       *          and plays fullscreen by default.
26753       *
26754       * @return {string|Player}
26755       *         - the current value of playsinline
26756       *         - the player when setting
26757       *
26758       * @see [Spec]{@link https://html.spec.whatwg.org/#attr-video-playsinline}
26759       */
26760      ;
26761  
26762      _proto.playsinline = function playsinline(value) {
26763        if (value !== undefined) {
26764          this.techCall_('setPlaysinline', value);
26765          this.options_.playsinline = value;
26766          return this;
26767        }
26768  
26769        return this.techGet_('playsinline');
26770      }
26771      /**
26772       * Get or set the loop attribute on the video element.
26773       *
26774       * @param {boolean} [value]
26775       *        - true means that we should loop the video
26776       *        - false means that we should not loop the video
26777       *
26778       * @return {boolean}
26779       *         The current value of loop when getting
26780       */
26781      ;
26782  
26783      _proto.loop = function loop(value) {
26784        if (value !== undefined) {
26785          this.techCall_('setLoop', value);
26786          this.options_.loop = value;
26787          return;
26788        }
26789  
26790        return this.techGet_('loop');
26791      }
26792      /**
26793       * Get or set the poster image source url
26794       *
26795       * @fires Player#posterchange
26796       *
26797       * @param {string} [src]
26798       *        Poster image source URL
26799       *
26800       * @return {string}
26801       *         The current value of poster when getting
26802       */
26803      ;
26804  
26805      _proto.poster = function poster(src) {
26806        if (src === undefined) {
26807          return this.poster_;
26808        } // The correct way to remove a poster is to set as an empty string
26809        // other falsey values will throw errors
26810  
26811  
26812        if (!src) {
26813          src = '';
26814        }
26815  
26816        if (src === this.poster_) {
26817          return;
26818        } // update the internal poster variable
26819  
26820  
26821        this.poster_ = src; // update the tech's poster
26822  
26823        this.techCall_('setPoster', src);
26824        this.isPosterFromTech_ = false; // alert components that the poster has been set
26825  
26826        /**
26827         * This event fires when the poster image is changed on the player.
26828         *
26829         * @event Player#posterchange
26830         * @type {EventTarget~Event}
26831         */
26832  
26833        this.trigger('posterchange');
26834      }
26835      /**
26836       * Some techs (e.g. YouTube) can provide a poster source in an
26837       * asynchronous way. We want the poster component to use this
26838       * poster source so that it covers up the tech's controls.
26839       * (YouTube's play button). However we only want to use this
26840       * source if the player user hasn't set a poster through
26841       * the normal APIs.
26842       *
26843       * @fires Player#posterchange
26844       * @listens Tech#posterchange
26845       * @private
26846       */
26847      ;
26848  
26849      _proto.handleTechPosterChange_ = function handleTechPosterChange_() {
26850        if ((!this.poster_ || this.options_.techCanOverridePoster) && this.tech_ && this.tech_.poster) {
26851          var newPoster = this.tech_.poster() || '';
26852  
26853          if (newPoster !== this.poster_) {
26854            this.poster_ = newPoster;
26855            this.isPosterFromTech_ = true; // Let components know the poster has changed
26856  
26857            this.trigger('posterchange');
26858          }
26859        }
26860      }
26861      /**
26862       * Get or set whether or not the controls are showing.
26863       *
26864       * @fires Player#controlsenabled
26865       *
26866       * @param {boolean} [bool]
26867       *        - true to turn controls on
26868       *        - false to turn controls off
26869       *
26870       * @return {boolean}
26871       *         The current value of controls when getting
26872       */
26873      ;
26874  
26875      _proto.controls = function controls(bool) {
26876        if (bool === undefined) {
26877          return !!this.controls_;
26878        }
26879  
26880        bool = !!bool; // Don't trigger a change event unless it actually changed
26881  
26882        if (this.controls_ === bool) {
26883          return;
26884        }
26885  
26886        this.controls_ = bool;
26887  
26888        if (this.usingNativeControls()) {
26889          this.techCall_('setControls', bool);
26890        }
26891  
26892        if (this.controls_) {
26893          this.removeClass('vjs-controls-disabled');
26894          this.addClass('vjs-controls-enabled');
26895          /**
26896           * @event Player#controlsenabled
26897           * @type {EventTarget~Event}
26898           */
26899  
26900          this.trigger('controlsenabled');
26901  
26902          if (!this.usingNativeControls()) {
26903            this.addTechControlsListeners_();
26904          }
26905        } else {
26906          this.removeClass('vjs-controls-enabled');
26907          this.addClass('vjs-controls-disabled');
26908          /**
26909           * @event Player#controlsdisabled
26910           * @type {EventTarget~Event}
26911           */
26912  
26913          this.trigger('controlsdisabled');
26914  
26915          if (!this.usingNativeControls()) {
26916            this.removeTechControlsListeners_();
26917          }
26918        }
26919      }
26920      /**
26921       * Toggle native controls on/off. Native controls are the controls built into
26922       * devices (e.g. default iPhone controls), Flash, or other techs
26923       * (e.g. Vimeo Controls)
26924       * **This should only be set by the current tech, because only the tech knows
26925       * if it can support native controls**
26926       *
26927       * @fires Player#usingnativecontrols
26928       * @fires Player#usingcustomcontrols
26929       *
26930       * @param {boolean} [bool]
26931       *        - true to turn native controls on
26932       *        - false to turn native controls off
26933       *
26934       * @return {boolean}
26935       *         The current value of native controls when getting
26936       */
26937      ;
26938  
26939      _proto.usingNativeControls = function usingNativeControls(bool) {
26940        if (bool === undefined) {
26941          return !!this.usingNativeControls_;
26942        }
26943  
26944        bool = !!bool; // Don't trigger a change event unless it actually changed
26945  
26946        if (this.usingNativeControls_ === bool) {
26947          return;
26948        }
26949  
26950        this.usingNativeControls_ = bool;
26951  
26952        if (this.usingNativeControls_) {
26953          this.addClass('vjs-using-native-controls');
26954          /**
26955           * player is using the native device controls
26956           *
26957           * @event Player#usingnativecontrols
26958           * @type {EventTarget~Event}
26959           */
26960  
26961          this.trigger('usingnativecontrols');
26962        } else {
26963          this.removeClass('vjs-using-native-controls');
26964          /**
26965           * player is using the custom HTML controls
26966           *
26967           * @event Player#usingcustomcontrols
26968           * @type {EventTarget~Event}
26969           */
26970  
26971          this.trigger('usingcustomcontrols');
26972        }
26973      }
26974      /**
26975       * Set or get the current MediaError
26976       *
26977       * @fires Player#error
26978       *
26979       * @param  {MediaError|string|number} [err]
26980       *         A MediaError or a string/number to be turned
26981       *         into a MediaError
26982       *
26983       * @return {MediaError|null}
26984       *         The current MediaError when getting (or null)
26985       */
26986      ;
26987  
26988      _proto.error = function error(err) {
26989        if (err === undefined) {
26990          return this.error_ || null;
26991        } // Suppress the first error message for no compatible source until
26992        // user interaction
26993  
26994  
26995        if (this.options_.suppressNotSupportedError && err && err.code === 4) {
26996          var triggerSuppressedError = function triggerSuppressedError() {
26997            this.error(err);
26998          };
26999  
27000          this.options_.suppressNotSupportedError = false;
27001          this.any(['click', 'touchstart'], triggerSuppressedError);
27002          this.one('loadstart', function () {
27003            this.off(['click', 'touchstart'], triggerSuppressedError);
27004          });
27005          return;
27006        } // restoring to default
27007  
27008  
27009        if (err === null) {
27010          this.error_ = err;
27011          this.removeClass('vjs-error');
27012  
27013          if (this.errorDisplay) {
27014            this.errorDisplay.close();
27015          }
27016  
27017          return;
27018        }
27019  
27020        this.error_ = new MediaError(err); // add the vjs-error classname to the player
27021  
27022        this.addClass('vjs-error'); // log the name of the error type and any message
27023        // IE11 logs "[object object]" and required you to expand message to see error object
27024  
27025        log.error("(CODE:" + this.error_.code + " " + MediaError.errorTypes[this.error_.code] + ")", this.error_.message, this.error_);
27026        /**
27027         * @event Player#error
27028         * @type {EventTarget~Event}
27029         */
27030  
27031        this.trigger('error');
27032        return;
27033      }
27034      /**
27035       * Report user activity
27036       *
27037       * @param {Object} event
27038       *        Event object
27039       */
27040      ;
27041  
27042      _proto.reportUserActivity = function reportUserActivity(event) {
27043        this.userActivity_ = true;
27044      }
27045      /**
27046       * Get/set if user is active
27047       *
27048       * @fires Player#useractive
27049       * @fires Player#userinactive
27050       *
27051       * @param {boolean} [bool]
27052       *        - true if the user is active
27053       *        - false if the user is inactive
27054       *
27055       * @return {boolean}
27056       *         The current value of userActive when getting
27057       */
27058      ;
27059  
27060      _proto.userActive = function userActive(bool) {
27061        if (bool === undefined) {
27062          return this.userActive_;
27063        }
27064  
27065        bool = !!bool;
27066  
27067        if (bool === this.userActive_) {
27068          return;
27069        }
27070  
27071        this.userActive_ = bool;
27072  
27073        if (this.userActive_) {
27074          this.userActivity_ = true;
27075          this.removeClass('vjs-user-inactive');
27076          this.addClass('vjs-user-active');
27077          /**
27078           * @event Player#useractive
27079           * @type {EventTarget~Event}
27080           */
27081  
27082          this.trigger('useractive');
27083          return;
27084        } // Chrome/Safari/IE have bugs where when you change the cursor it can
27085        // trigger a mousemove event. This causes an issue when you're hiding
27086        // the cursor when the user is inactive, and a mousemove signals user
27087        // activity. Making it impossible to go into inactive mode. Specifically
27088        // this happens in fullscreen when we really need to hide the cursor.
27089        //
27090        // When this gets resolved in ALL browsers it can be removed
27091        // https://code.google.com/p/chromium/issues/detail?id=103041
27092  
27093  
27094        if (this.tech_) {
27095          this.tech_.one('mousemove', function (e) {
27096            e.stopPropagation();
27097            e.preventDefault();
27098          });
27099        }
27100  
27101        this.userActivity_ = false;
27102        this.removeClass('vjs-user-active');
27103        this.addClass('vjs-user-inactive');
27104        /**
27105         * @event Player#userinactive
27106         * @type {EventTarget~Event}
27107         */
27108  
27109        this.trigger('userinactive');
27110      }
27111      /**
27112       * Listen for user activity based on timeout value
27113       *
27114       * @private
27115       */
27116      ;
27117  
27118      _proto.listenForUserActivity_ = function listenForUserActivity_() {
27119        var mouseInProgress;
27120        var lastMoveX;
27121        var lastMoveY;
27122        var handleActivity = bind(this, this.reportUserActivity);
27123  
27124        var handleMouseMove = function handleMouseMove(e) {
27125          // #1068 - Prevent mousemove spamming
27126          // Chrome Bug: https://code.google.com/p/chromium/issues/detail?id=366970
27127          if (e.screenX !== lastMoveX || e.screenY !== lastMoveY) {
27128            lastMoveX = e.screenX;
27129            lastMoveY = e.screenY;
27130            handleActivity();
27131          }
27132        };
27133  
27134        var handleMouseDown = function handleMouseDown() {
27135          handleActivity(); // For as long as the they are touching the device or have their mouse down,
27136          // we consider them active even if they're not moving their finger or mouse.
27137          // So we want to continue to update that they are active
27138  
27139          this.clearInterval(mouseInProgress); // Setting userActivity=true now and setting the interval to the same time
27140          // as the activityCheck interval (250) should ensure we never miss the
27141          // next activityCheck
27142  
27143          mouseInProgress = this.setInterval(handleActivity, 250);
27144        };
27145  
27146        var handleMouseUpAndMouseLeave = function handleMouseUpAndMouseLeave(event) {
27147          handleActivity(); // Stop the interval that maintains activity if the mouse/touch is down
27148  
27149          this.clearInterval(mouseInProgress);
27150        }; // Any mouse movement will be considered user activity
27151  
27152  
27153        this.on('mousedown', handleMouseDown);
27154        this.on('mousemove', handleMouseMove);
27155        this.on('mouseup', handleMouseUpAndMouseLeave);
27156        this.on('mouseleave', handleMouseUpAndMouseLeave);
27157        var controlBar = this.getChild('controlBar'); // Fixes bug on Android & iOS where when tapping progressBar (when control bar is displayed)
27158        // controlBar would no longer be hidden by default timeout.
27159  
27160        if (controlBar && !IS_IOS && !IS_ANDROID) {
27161          controlBar.on('mouseenter', function (event) {
27162            this.player().cache_.inactivityTimeout = this.player().options_.inactivityTimeout;
27163            this.player().options_.inactivityTimeout = 0;
27164          });
27165          controlBar.on('mouseleave', function (event) {
27166            this.player().options_.inactivityTimeout = this.player().cache_.inactivityTimeout;
27167          });
27168        } // Listen for keyboard navigation
27169        // Shouldn't need to use inProgress interval because of key repeat
27170  
27171  
27172        this.on('keydown', handleActivity);
27173        this.on('keyup', handleActivity); // Run an interval every 250 milliseconds instead of stuffing everything into
27174        // the mousemove/touchmove function itself, to prevent performance degradation.
27175        // `this.reportUserActivity` simply sets this.userActivity_ to true, which
27176        // then gets picked up by this loop
27177        // http://ejohn.org/blog/learning-from-twitter/
27178  
27179        var inactivityTimeout;
27180        this.setInterval(function () {
27181          // Check to see if mouse/touch activity has happened
27182          if (!this.userActivity_) {
27183            return;
27184          } // Reset the activity tracker
27185  
27186  
27187          this.userActivity_ = false; // If the user state was inactive, set the state to active
27188  
27189          this.userActive(true); // Clear any existing inactivity timeout to start the timer over
27190  
27191          this.clearTimeout(inactivityTimeout);
27192          var timeout = this.options_.inactivityTimeout;
27193  
27194          if (timeout <= 0) {
27195            return;
27196          } // In <timeout> milliseconds, if no more activity has occurred the
27197          // user will be considered inactive
27198  
27199  
27200          inactivityTimeout = this.setTimeout(function () {
27201            // Protect against the case where the inactivityTimeout can trigger just
27202            // before the next user activity is picked up by the activity check loop
27203            // causing a flicker
27204            if (!this.userActivity_) {
27205              this.userActive(false);
27206            }
27207          }, timeout);
27208        }, 250);
27209      }
27210      /**
27211       * Gets or sets the current playback rate. A playback rate of
27212       * 1.0 represents normal speed and 0.5 would indicate half-speed
27213       * playback, for instance.
27214       *
27215       * @see https://html.spec.whatwg.org/multipage/embedded-content.html#dom-media-playbackrate
27216       *
27217       * @param {number} [rate]
27218       *       New playback rate to set.
27219       *
27220       * @return {number}
27221       *         The current playback rate when getting or 1.0
27222       */
27223      ;
27224  
27225      _proto.playbackRate = function playbackRate(rate) {
27226        if (rate !== undefined) {
27227          // NOTE: this.cache_.lastPlaybackRate is set from the tech handler
27228          // that is registered above
27229          this.techCall_('setPlaybackRate', rate);
27230          return;
27231        }
27232  
27233        if (this.tech_ && this.tech_.featuresPlaybackRate) {
27234          return this.cache_.lastPlaybackRate || this.techGet_('playbackRate');
27235        }
27236  
27237        return 1.0;
27238      }
27239      /**
27240       * Gets or sets the current default playback rate. A default playback rate of
27241       * 1.0 represents normal speed and 0.5 would indicate half-speed playback, for instance.
27242       * defaultPlaybackRate will only represent what the initial playbackRate of a video was, not
27243       * not the current playbackRate.
27244       *
27245       * @see https://html.spec.whatwg.org/multipage/embedded-content.html#dom-media-defaultplaybackrate
27246       *
27247       * @param {number} [rate]
27248       *       New default playback rate to set.
27249       *
27250       * @return {number|Player}
27251       *         - The default playback rate when getting or 1.0
27252       *         - the player when setting
27253       */
27254      ;
27255  
27256      _proto.defaultPlaybackRate = function defaultPlaybackRate(rate) {
27257        if (rate !== undefined) {
27258          return this.techCall_('setDefaultPlaybackRate', rate);
27259        }
27260  
27261        if (this.tech_ && this.tech_.featuresPlaybackRate) {
27262          return this.techGet_('defaultPlaybackRate');
27263        }
27264  
27265        return 1.0;
27266      }
27267      /**
27268       * Gets or sets the audio flag
27269       *
27270       * @param {boolean} bool
27271       *        - true signals that this is an audio player
27272       *        - false signals that this is not an audio player
27273       *
27274       * @return {boolean}
27275       *         The current value of isAudio when getting
27276       */
27277      ;
27278  
27279      _proto.isAudio = function isAudio(bool) {
27280        if (bool !== undefined) {
27281          this.isAudio_ = !!bool;
27282          return;
27283        }
27284  
27285        return !!this.isAudio_;
27286      }
27287      /**
27288       * A helper method for adding a {@link TextTrack} to our
27289       * {@link TextTrackList}.
27290       *
27291       * In addition to the W3C settings we allow adding additional info through options.
27292       *
27293       * @see http://www.w3.org/html/wg/drafts/html/master/embedded-content-0.html#dom-media-addtexttrack
27294       *
27295       * @param {string} [kind]
27296       *        the kind of TextTrack you are adding
27297       *
27298       * @param {string} [label]
27299       *        the label to give the TextTrack label
27300       *
27301       * @param {string} [language]
27302       *        the language to set on the TextTrack
27303       *
27304       * @return {TextTrack|undefined}
27305       *         the TextTrack that was added or undefined
27306       *         if there is no tech
27307       */
27308      ;
27309  
27310      _proto.addTextTrack = function addTextTrack(kind, label, language) {
27311        if (this.tech_) {
27312          return this.tech_.addTextTrack(kind, label, language);
27313        }
27314      }
27315      /**
27316       * Create a remote {@link TextTrack} and an {@link HTMLTrackElement}.
27317       * When manualCleanup is set to false, the track will be automatically removed
27318       * on source changes.
27319       *
27320       * @param {Object} options
27321       *        Options to pass to {@link HTMLTrackElement} during creation. See
27322       *        {@link HTMLTrackElement} for object properties that you should use.
27323       *
27324       * @param {boolean} [manualCleanup=true] if set to false, the TextTrack will be
27325       *                                       removed on a source change
27326       *
27327       * @return {HtmlTrackElement}
27328       *         the HTMLTrackElement that was created and added
27329       *         to the HtmlTrackElementList and the remote
27330       *         TextTrackList
27331       *
27332       * @deprecated The default value of the "manualCleanup" parameter will default
27333       *             to "false" in upcoming versions of Video.js
27334       */
27335      ;
27336  
27337      _proto.addRemoteTextTrack = function addRemoteTextTrack(options, manualCleanup) {
27338        if (this.tech_) {
27339          return this.tech_.addRemoteTextTrack(options, manualCleanup);
27340        }
27341      }
27342      /**
27343       * Remove a remote {@link TextTrack} from the respective
27344       * {@link TextTrackList} and {@link HtmlTrackElementList}.
27345       *
27346       * @param {Object} track
27347       *        Remote {@link TextTrack} to remove
27348       *
27349       * @return {undefined}
27350       *         does not return anything
27351       */
27352      ;
27353  
27354      _proto.removeRemoteTextTrack = function removeRemoteTextTrack(obj) {
27355        if (obj === void 0) {
27356          obj = {};
27357        }
27358  
27359        var _obj = obj,
27360            track = _obj.track;
27361  
27362        if (!track) {
27363          track = obj;
27364        } // destructure the input into an object with a track argument, defaulting to arguments[0]
27365        // default the whole argument to an empty object if nothing was passed in
27366  
27367  
27368        if (this.tech_) {
27369          return this.tech_.removeRemoteTextTrack(track);
27370        }
27371      }
27372      /**
27373       * Gets available media playback quality metrics as specified by the W3C's Media
27374       * Playback Quality API.
27375       *
27376       * @see [Spec]{@link https://wicg.github.io/media-playback-quality}
27377       *
27378       * @return {Object|undefined}
27379       *         An object with supported media playback quality metrics or undefined if there
27380       *         is no tech or the tech does not support it.
27381       */
27382      ;
27383  
27384      _proto.getVideoPlaybackQuality = function getVideoPlaybackQuality() {
27385        return this.techGet_('getVideoPlaybackQuality');
27386      }
27387      /**
27388       * Get video width
27389       *
27390       * @return {number}
27391       *         current video width
27392       */
27393      ;
27394  
27395      _proto.videoWidth = function videoWidth() {
27396        return this.tech_ && this.tech_.videoWidth && this.tech_.videoWidth() || 0;
27397      }
27398      /**
27399       * Get video height
27400       *
27401       * @return {number}
27402       *         current video height
27403       */
27404      ;
27405  
27406      _proto.videoHeight = function videoHeight() {
27407        return this.tech_ && this.tech_.videoHeight && this.tech_.videoHeight() || 0;
27408      }
27409      /**
27410       * The player's language code
27411       * NOTE: The language should be set in the player options if you want the
27412       * the controls to be built with a specific language. Changing the language
27413       * later will not update controls text.
27414       *
27415       * @param {string} [code]
27416       *        the language code to set the player to
27417       *
27418       * @return {string}
27419       *         The current language code when getting
27420       */
27421      ;
27422  
27423      _proto.language = function language(code) {
27424        if (code === undefined) {
27425          return this.language_;
27426        }
27427  
27428        this.language_ = String(code).toLowerCase();
27429      }
27430      /**
27431       * Get the player's language dictionary
27432       * Merge every time, because a newly added plugin might call videojs.addLanguage() at any time
27433       * Languages specified directly in the player options have precedence
27434       *
27435       * @return {Array}
27436       *         An array of of supported languages
27437       */
27438      ;
27439  
27440      _proto.languages = function languages() {
27441        return mergeOptions(Player.prototype.options_.languages, this.languages_);
27442      }
27443      /**
27444       * returns a JavaScript object reperesenting the current track
27445       * information. **DOES not return it as JSON**
27446       *
27447       * @return {Object}
27448       *         Object representing the current of track info
27449       */
27450      ;
27451  
27452      _proto.toJSON = function toJSON() {
27453        var options = mergeOptions(this.options_);
27454        var tracks = options.tracks;
27455        options.tracks = [];
27456  
27457        for (var i = 0; i < tracks.length; i++) {
27458          var track = tracks[i]; // deep merge tracks and null out player so no circular references
27459  
27460          track = mergeOptions(track);
27461          track.player = undefined;
27462          options.tracks[i] = track;
27463        }
27464  
27465        return options;
27466      }
27467      /**
27468       * Creates a simple modal dialog (an instance of the {@link ModalDialog}
27469       * component) that immediately overlays the player with arbitrary
27470       * content and removes itself when closed.
27471       *
27472       * @param {string|Function|Element|Array|null} content
27473       *        Same as {@link ModalDialog#content}'s param of the same name.
27474       *        The most straight-forward usage is to provide a string or DOM
27475       *        element.
27476       *
27477       * @param {Object} [options]
27478       *        Extra options which will be passed on to the {@link ModalDialog}.
27479       *
27480       * @return {ModalDialog}
27481       *         the {@link ModalDialog} that was created
27482       */
27483      ;
27484  
27485      _proto.createModal = function createModal(content, options) {
27486        var _this16 = this;
27487  
27488        options = options || {};
27489        options.content = content || '';
27490        var modal = new ModalDialog(this, options);
27491        this.addChild(modal);
27492        modal.on('dispose', function () {
27493          _this16.removeChild(modal);
27494        });
27495        modal.open();
27496        return modal;
27497      }
27498      /**
27499       * Change breakpoint classes when the player resizes.
27500       *
27501       * @private
27502       */
27503      ;
27504  
27505      _proto.updateCurrentBreakpoint_ = function updateCurrentBreakpoint_() {
27506        if (!this.responsive()) {
27507          return;
27508        }
27509  
27510        var currentBreakpoint = this.currentBreakpoint();
27511        var currentWidth = this.currentWidth();
27512  
27513        for (var i = 0; i < BREAKPOINT_ORDER.length; i++) {
27514          var candidateBreakpoint = BREAKPOINT_ORDER[i];
27515          var maxWidth = this.breakpoints_[candidateBreakpoint];
27516  
27517          if (currentWidth <= maxWidth) {
27518            // The current breakpoint did not change, nothing to do.
27519            if (currentBreakpoint === candidateBreakpoint) {
27520              return;
27521            } // Only remove a class if there is a current breakpoint.
27522  
27523  
27524            if (currentBreakpoint) {
27525              this.removeClass(BREAKPOINT_CLASSES[currentBreakpoint]);
27526            }
27527  
27528            this.addClass(BREAKPOINT_CLASSES[candidateBreakpoint]);
27529            this.breakpoint_ = candidateBreakpoint;
27530            break;
27531          }
27532        }
27533      }
27534      /**
27535       * Removes the current breakpoint.
27536       *
27537       * @private
27538       */
27539      ;
27540  
27541      _proto.removeCurrentBreakpoint_ = function removeCurrentBreakpoint_() {
27542        var className = this.currentBreakpointClass();
27543        this.breakpoint_ = '';
27544  
27545        if (className) {
27546          this.removeClass(className);
27547        }
27548      }
27549      /**
27550       * Get or set breakpoints on the player.
27551       *
27552       * Calling this method with an object or `true` will remove any previous
27553       * custom breakpoints and start from the defaults again.
27554       *
27555       * @param  {Object|boolean} [breakpoints]
27556       *         If an object is given, it can be used to provide custom
27557       *         breakpoints. If `true` is given, will set default breakpoints.
27558       *         If this argument is not given, will simply return the current
27559       *         breakpoints.
27560       *
27561       * @param  {number} [breakpoints.tiny]
27562       *         The maximum width for the "vjs-layout-tiny" class.
27563       *
27564       * @param  {number} [breakpoints.xsmall]
27565       *         The maximum width for the "vjs-layout-x-small" class.
27566       *
27567       * @param  {number} [breakpoints.small]
27568       *         The maximum width for the "vjs-layout-small" class.
27569       *
27570       * @param  {number} [breakpoints.medium]
27571       *         The maximum width for the "vjs-layout-medium" class.
27572       *
27573       * @param  {number} [breakpoints.large]
27574       *         The maximum width for the "vjs-layout-large" class.
27575       *
27576       * @param  {number} [breakpoints.xlarge]
27577       *         The maximum width for the "vjs-layout-x-large" class.
27578       *
27579       * @param  {number} [breakpoints.huge]
27580       *         The maximum width for the "vjs-layout-huge" class.
27581       *
27582       * @return {Object}
27583       *         An object mapping breakpoint names to maximum width values.
27584       */
27585      ;
27586  
27587      _proto.breakpoints = function breakpoints(_breakpoints) {
27588        // Used as a getter.
27589        if (_breakpoints === undefined) {
27590          return assign(this.breakpoints_);
27591        }
27592  
27593        this.breakpoint_ = '';
27594        this.breakpoints_ = assign({}, DEFAULT_BREAKPOINTS, _breakpoints); // When breakpoint definitions change, we need to update the currently
27595        // selected breakpoint.
27596  
27597        this.updateCurrentBreakpoint_(); // Clone the breakpoints before returning.
27598  
27599        return assign(this.breakpoints_);
27600      }
27601      /**
27602       * Get or set a flag indicating whether or not this player should adjust
27603       * its UI based on its dimensions.
27604       *
27605       * @param  {boolean} value
27606       *         Should be `true` if the player should adjust its UI based on its
27607       *         dimensions; otherwise, should be `false`.
27608       *
27609       * @return {boolean}
27610       *         Will be `true` if this player should adjust its UI based on its
27611       *         dimensions; otherwise, will be `false`.
27612       */
27613      ;
27614  
27615      _proto.responsive = function responsive(value) {
27616        // Used as a getter.
27617        if (value === undefined) {
27618          return this.responsive_;
27619        }
27620  
27621        value = Boolean(value);
27622        var current = this.responsive_; // Nothing changed.
27623  
27624        if (value === current) {
27625          return;
27626        } // The value actually changed, set it.
27627  
27628  
27629        this.responsive_ = value; // Start listening for breakpoints and set the initial breakpoint if the
27630        // player is now responsive.
27631  
27632        if (value) {
27633          this.on('playerresize', this.updateCurrentBreakpoint_);
27634          this.updateCurrentBreakpoint_(); // Stop listening for breakpoints if the player is no longer responsive.
27635        } else {
27636          this.off('playerresize', this.updateCurrentBreakpoint_);
27637          this.removeCurrentBreakpoint_();
27638        }
27639  
27640        return value;
27641      }
27642      /**
27643       * Get current breakpoint name, if any.
27644       *
27645       * @return {string}
27646       *         If there is currently a breakpoint set, returns a the key from the
27647       *         breakpoints object matching it. Otherwise, returns an empty string.
27648       */
27649      ;
27650  
27651      _proto.currentBreakpoint = function currentBreakpoint() {
27652        return this.breakpoint_;
27653      }
27654      /**
27655       * Get the current breakpoint class name.
27656       *
27657       * @return {string}
27658       *         The matching class name (e.g. `"vjs-layout-tiny"` or
27659       *         `"vjs-layout-large"`) for the current breakpoint. Empty string if
27660       *         there is no current breakpoint.
27661       */
27662      ;
27663  
27664      _proto.currentBreakpointClass = function currentBreakpointClass() {
27665        return BREAKPOINT_CLASSES[this.breakpoint_] || '';
27666      }
27667      /**
27668       * An object that describes a single piece of media.
27669       *
27670       * Properties that are not part of this type description will be retained; so,
27671       * this can be viewed as a generic metadata storage mechanism as well.
27672       *
27673       * @see      {@link https://wicg.github.io/mediasession/#the-mediametadata-interface}
27674       * @typedef  {Object} Player~MediaObject
27675       *
27676       * @property {string} [album]
27677       *           Unused, except if this object is passed to the `MediaSession`
27678       *           API.
27679       *
27680       * @property {string} [artist]
27681       *           Unused, except if this object is passed to the `MediaSession`
27682       *           API.
27683       *
27684       * @property {Object[]} [artwork]
27685       *           Unused, except if this object is passed to the `MediaSession`
27686       *           API. If not specified, will be populated via the `poster`, if
27687       *           available.
27688       *
27689       * @property {string} [poster]
27690       *           URL to an image that will display before playback.
27691       *
27692       * @property {Tech~SourceObject|Tech~SourceObject[]|string} [src]
27693       *           A single source object, an array of source objects, or a string
27694       *           referencing a URL to a media source. It is _highly recommended_
27695       *           that an object or array of objects is used here, so that source
27696       *           selection algorithms can take the `type` into account.
27697       *
27698       * @property {string} [title]
27699       *           Unused, except if this object is passed to the `MediaSession`
27700       *           API.
27701       *
27702       * @property {Object[]} [textTracks]
27703       *           An array of objects to be used to create text tracks, following
27704       *           the {@link https://www.w3.org/TR/html50/embedded-content-0.html#the-track-element|native track element format}.
27705       *           For ease of removal, these will be created as "remote" text
27706       *           tracks and set to automatically clean up on source changes.
27707       *
27708       *           These objects may have properties like `src`, `kind`, `label`,
27709       *           and `language`, see {@link Tech#createRemoteTextTrack}.
27710       */
27711  
27712      /**
27713       * Populate the player using a {@link Player~MediaObject|MediaObject}.
27714       *
27715       * @param  {Player~MediaObject} media
27716       *         A media object.
27717       *
27718       * @param  {Function} ready
27719       *         A callback to be called when the player is ready.
27720       */
27721      ;
27722  
27723      _proto.loadMedia = function loadMedia(media, ready) {
27724        var _this17 = this;
27725  
27726        if (!media || typeof media !== 'object') {
27727          return;
27728        }
27729  
27730        this.reset(); // Clone the media object so it cannot be mutated from outside.
27731  
27732        this.cache_.media = mergeOptions(media);
27733        var _this$cache_$media = this.cache_.media,
27734            artwork = _this$cache_$media.artwork,
27735            poster = _this$cache_$media.poster,
27736            src = _this$cache_$media.src,
27737            textTracks = _this$cache_$media.textTracks; // If `artwork` is not given, create it using `poster`.
27738  
27739        if (!artwork && poster) {
27740          this.cache_.media.artwork = [{
27741            src: poster,
27742            type: getMimetype(poster)
27743          }];
27744        }
27745  
27746        if (src) {
27747          this.src(src);
27748        }
27749  
27750        if (poster) {
27751          this.poster(poster);
27752        }
27753  
27754        if (Array.isArray(textTracks)) {
27755          textTracks.forEach(function (tt) {
27756            return _this17.addRemoteTextTrack(tt, false);
27757          });
27758        }
27759  
27760        this.ready(ready);
27761      }
27762      /**
27763       * Get a clone of the current {@link Player~MediaObject} for this player.
27764       *
27765       * If the `loadMedia` method has not been used, will attempt to return a
27766       * {@link Player~MediaObject} based on the current state of the player.
27767       *
27768       * @return {Player~MediaObject}
27769       */
27770      ;
27771  
27772      _proto.getMedia = function getMedia() {
27773        if (!this.cache_.media) {
27774          var poster = this.poster();
27775          var src = this.currentSources();
27776          var textTracks = Array.prototype.map.call(this.remoteTextTracks(), function (tt) {
27777            return {
27778              kind: tt.kind,
27779              label: tt.label,
27780              language: tt.language,
27781              src: tt.src
27782            };
27783          });
27784          var media = {
27785            src: src,
27786            textTracks: textTracks
27787          };
27788  
27789          if (poster) {
27790            media.poster = poster;
27791            media.artwork = [{
27792              src: media.poster,
27793              type: getMimetype(media.poster)
27794            }];
27795          }
27796  
27797          return media;
27798        }
27799  
27800        return mergeOptions(this.cache_.media);
27801      }
27802      /**
27803       * Gets tag settings
27804       *
27805       * @param {Element} tag
27806       *        The player tag
27807       *
27808       * @return {Object}
27809       *         An object containing all of the settings
27810       *         for a player tag
27811       */
27812      ;
27813  
27814      Player.getTagSettings = function getTagSettings(tag) {
27815        var baseOptions = {
27816          sources: [],
27817          tracks: []
27818        };
27819        var tagOptions = getAttributes(tag);
27820        var dataSetup = tagOptions['data-setup'];
27821  
27822        if (hasClass(tag, 'vjs-fill')) {
27823          tagOptions.fill = true;
27824        }
27825  
27826        if (hasClass(tag, 'vjs-fluid')) {
27827          tagOptions.fluid = true;
27828        } // Check if data-setup attr exists.
27829  
27830  
27831        if (dataSetup !== null) {
27832          // Parse options JSON
27833          // If empty string, make it a parsable json object.
27834          var _safeParseTuple = tuple(dataSetup || '{}'),
27835              err = _safeParseTuple[0],
27836              data = _safeParseTuple[1];
27837  
27838          if (err) {
27839            log.error(err);
27840          }
27841  
27842          assign(tagOptions, data);
27843        }
27844  
27845        assign(baseOptions, tagOptions); // Get tag children settings
27846  
27847        if (tag.hasChildNodes()) {
27848          var children = tag.childNodes;
27849  
27850          for (var i = 0, j = children.length; i < j; i++) {
27851            var child = children[i]; // Change case needed: http://ejohn.org/blog/nodename-case-sensitivity/
27852  
27853            var childName = child.nodeName.toLowerCase();
27854  
27855            if (childName === 'source') {
27856              baseOptions.sources.push(getAttributes(child));
27857            } else if (childName === 'track') {
27858              baseOptions.tracks.push(getAttributes(child));
27859            }
27860          }
27861        }
27862  
27863        return baseOptions;
27864      }
27865      /**
27866       * Determine whether or not flexbox is supported
27867       *
27868       * @return {boolean}
27869       *         - true if flexbox is supported
27870       *         - false if flexbox is not supported
27871       */
27872      ;
27873  
27874      _proto.flexNotSupported_ = function flexNotSupported_() {
27875        var elem = document.createElement('i'); // Note: We don't actually use flexBasis (or flexOrder), but it's one of the more
27876        // common flex features that we can rely on when checking for flex support.
27877  
27878        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
27879        'msFlexOrder' in elem.style);
27880      };
27881  
27882      return Player;
27883    }(Component);
27884    /**
27885     * Get the {@link VideoTrackList}
27886     * @link https://html.spec.whatwg.org/multipage/embedded-content.html#videotracklist
27887     *
27888     * @return {VideoTrackList}
27889     *         the current video track list
27890     *
27891     * @method Player.prototype.videoTracks
27892     */
27893  
27894    /**
27895     * Get the {@link AudioTrackList}
27896     * @link https://html.spec.whatwg.org/multipage/embedded-content.html#audiotracklist
27897     *
27898     * @return {AudioTrackList}
27899     *         the current audio track list
27900     *
27901     * @method Player.prototype.audioTracks
27902     */
27903  
27904    /**
27905     * Get the {@link TextTrackList}
27906     *
27907     * @link http://www.w3.org/html/wg/drafts/html/master/embedded-content-0.html#dom-media-texttracks
27908     *
27909     * @return {TextTrackList}
27910     *         the current text track list
27911     *
27912     * @method Player.prototype.textTracks
27913     */
27914  
27915    /**
27916     * Get the remote {@link TextTrackList}
27917     *
27918     * @return {TextTrackList}
27919     *         The current remote text track list
27920     *
27921     * @method Player.prototype.remoteTextTracks
27922     */
27923  
27924    /**
27925     * Get the remote {@link HtmlTrackElementList} tracks.
27926     *
27927     * @return {HtmlTrackElementList}
27928     *         The current remote text track element list
27929     *
27930     * @method Player.prototype.remoteTextTrackEls
27931     */
27932  
27933  
vendor: 4,629 bytes, lines 27934-28059
27934    ALL.names.forEach(function (name) {
27935      var props = ALL[name];
27936  
27937      Player.prototype[props.getterName] = function () {
27938        if (this.tech_) {
27939          return this.tech_[props.getterName]();
27940        } // if we have not yet loadTech_, we create {video,audio,text}Tracks_
27941        // these will be passed to the tech during loading
27942  
27943  
27944        this[props.privateName] = this[props.privateName] || new props.ListClass();
27945        return this[props.privateName];
27946      };
27947    });
27948    /**
27949     * Get or set the `Player`'s crossorigin option. For the HTML5 player, this
27950     * sets the `crossOrigin` property on the `<video>` tag to control the CORS
27951     * behavior.
27952     *
27953     * @see [Video Element Attributes]{@link https://developer.mozilla.org/en-US/docs/Web/HTML/Element/video#attr-crossorigin}
27954     *
27955     * @param {string} [value]
27956     *        The value to set the `Player`'s crossorigin to. If an argument is
27957     *        given, must be one of `anonymous` or `use-credentials`.
27958     *
27959     * @return {string|undefined}
27960     *         - The current crossorigin value of the `Player` when getting.
27961     *         - undefined when setting
27962     */
27963  
27964    Player.prototype.crossorigin = Player.prototype.crossOrigin;
27965    /**
27966     * Global enumeration of players.
27967     *
27968     * The keys are the player IDs and the values are either the {@link Player}
27969     * instance or `null` for disposed players.
27970     *
27971     * @type {Object}
27972     */
27973  
27974    Player.players = {};
27975    var navigator = window$3.navigator;
27976    /*
27977     * Player instance options, surfaced using options
27978     * options = Player.prototype.options_
27979     * Make changes in options, not here.
27980     *
27981     * @type {Object}
27982     * @private
27983     */
27984  
27985    Player.prototype.options_ = {
27986      // Default order of fallback technology
27987      techOrder: Tech.defaultTechOrder_,
27988      html5: {},
27989      flash: {},
27990      // default inactivity timeout
27991      inactivityTimeout: 2000,
27992      // default playback rates
27993      playbackRates: [],
27994      // Add playback rate selection by adding rates
27995      // 'playbackRates': [0.5, 1, 1.5, 2],
27996      liveui: false,
27997      // Included control sets
27998      children: ['mediaLoader', 'posterImage', 'textTrackDisplay', 'loadingSpinner', 'bigPlayButton', 'liveTracker', 'controlBar', 'errorDisplay', 'textTrackSettings', 'resizeManager'],
27999      language: navigator && (navigator.languages && navigator.languages[0] || navigator.userLanguage || navigator.language) || 'en',
28000      // locales and their language translations
28001      languages: {},
28002      // Default message to show when a video cannot be played.
28003      notSupportedMessage: 'No compatible source was found for this media.',
28004      fullscreen: {
28005        options: {
28006          navigationUI: 'hide'
28007        }
28008      },
28009      breakpoints: {},
28010      responsive: false
28011    };
28012    [
28013    /**
28014     * Returns whether or not the player is in the "ended" state.
28015     *
28016     * @return {Boolean} True if the player is in the ended state, false if not.
28017     * @method Player#ended
28018     */
28019    'ended',
28020    /**
28021     * Returns whether or not the player is in the "seeking" state.
28022     *
28023     * @return {Boolean} True if the player is in the seeking state, false if not.
28024     * @method Player#seeking
28025     */
28026    'seeking',
28027    /**
28028     * Returns the TimeRanges of the media that are currently available
28029     * for seeking to.
28030     *
28031     * @return {TimeRanges} the seekable intervals of the media timeline
28032     * @method Player#seekable
28033     */
28034    'seekable',
28035    /**
28036     * Returns the current state of network activity for the element, from
28037     * the codes in the list below.
28038     * - NETWORK_EMPTY (numeric value 0)
28039     *   The element has not yet been initialised. All attributes are in
28040     *   their initial states.
28041     * - NETWORK_IDLE (numeric value 1)
28042     *   The element's resource selection algorithm is active and has
28043     *   selected a resource, but it is not actually using the network at
28044     *   this time.
28045     * - NETWORK_LOADING (numeric value 2)
28046     *   The user agent is actively trying to download data.
28047     * - NETWORK_NO_SOURCE (numeric value 3)
28048     *   The element's resource selection algorithm is active, but it has
28049     *   not yet found a resource to use.
28050     *
28051     * @see https://html.spec.whatwg.org/multipage/embedded-content.html#network-states
28052     * @return {number} the current network activity state
28053     * @method Player#networkState
28054     */
28055    'networkState',
28056    /**
28057     * Returns a value that expresses the current state of the element
28058     * with respect to rendering the current playback position, from the
28059     * codes in the list below.
28060     * - HAVE_NOTHING (numeric value 0)
28061     *   No information regarding the media resource is available.
28062     * - HAVE_METADATA (numeric value 1)
28063     *   Enough of the resource has been obtained that the duration of the
28064     *   resource is available.
28065     * - HAVE_CURRENT_DATA (numeric value 2)
28066     *   Data for the immediate current playback position is available.
28067     * - HAVE_FUTURE_DATA (numeric value 3)
28068     *   Data for the immediate current playback position is available, as
28069     *   well as enough data for the user agent to advance the current
28070     *   playback position in the direction of playback.
28071     * - HAVE_ENOUGH_DATA (numeric value 4)
28072     *   The user agent estimates that enough data is available for
28073     *   playback to proceed uninterrupted.
28074     *
28075     * @see https://html.spec.whatwg.org/multipage/embedded-content.html#dom-media-readystate
28076     * @return {number} the current playback rendering state
28077     * @method Player#readyState
28078     */
28079    'readyState'].forEach(function (fn) {
28080      Player.prototype[fn] = function () {
28081        return this.techGet_(fn);
28082      };
28083    });
28084    TECH_EVENTS_RETRIGGER.forEach(function (event) {
28085      Player.prototype["handleTech" + toTitleCase(event) + "_"] = function () {
28086        return this.trigger(event);
28087      };
28088    });
28089    /**
28090     * Fired when the player has initial duration and dimension information
28091     *
28092     * @event Player#loadedmetadata
28093     * @type {EventTarget~Event}
28094     */
28095  
28096    /**
28097     * Fired when the player has downloaded data at the current playback position
28098     *
28099     * @event Player#loadeddata
28100     * @type {EventTarget~Event}
28101     */
28102  
28103    /**
28104     * Fired when the current playback position has changed *
28105     * During playback this is fired every 15-250 milliseconds, depending on the
28106     * playback technology in use.
28107     *
28108     * @event Player#timeupdate
28109     * @type {EventTarget~Event}
28110     */
28111  
28112    /**
28113     * Fired when the volume changes
28114     *
28115     * @event Player#volumechange
28116     * @type {EventTarget~Event}
28117     */
28118  
28119    /**
28120     * Reports whether or not a player has a plugin available.
28121     *
28122     * This does not report whether or not the plugin has ever been initialized
28123     * on this player. For that, [usingPlugin]{@link Player#usingPlugin}.
28124     *
28125     * @method Player#hasPlugin
28126     * @param  {string}  name
28127     *         The name of a plugin.
28128     *
28129     * @return {boolean}
28130     *         Whether or not this player has the requested plugin available.
28131     */
28132  
28133    /**
28134     * Reports whether or not a player is using a plugin by name.
28135     *
28136     * For basic plugins, this only reports whether the plugin has _ever_ been
28137     * initialized on this player.
28138     *
28139     * @method Player#usingPlugin
28140     * @param  {string} name
28141     *         The name of a plugin.
28142     *
28143     * @return {boolean}
28144     *         Whether or not this player is using the requested plugin.
28145     */
28146  
28147    Component.registerComponent('Player', Player);
28148  
28149    var setPrototypeOf = createCommonjsModule(function (module) {
28150      function _setPrototypeOf(o, p) {
28151        module.exports = _setPrototypeOf = Object.setPrototypeOf || function _setPrototypeOf(o, p) {
28152          o.__proto__ = p;
28153          return o;
28154        };
28155  
28156        return _setPrototypeOf(o, p);
28157      }
28158  
28159      module.exports = _setPrototypeOf;
28160    });
28161  
28162    function _isNativeReflectConstruct() {
28163      if (typeof Reflect === "undefined" || !Reflect.construct) return false;
28164      if (Reflect.construct.sham) return false;
28165      if (typeof Proxy === "function") return true;
28166  
28167      try {
28168        Date.prototype.toString.call(Reflect.construct(Date, [], function () {}));
28169        return true;
28170      } catch (e) {
28171        return false;
28172      }
28173    }
28174  
28175    var isNativeReflectConstruct = _isNativeReflectConstruct;
28176  
28177    var construct = createCommonjsModule(function (module) {
28178      function _construct(Parent, args, Class) {
28179        if (isNativeReflectConstruct()) {
28180          module.exports = _construct = Reflect.construct;
28181        } else {
28182          module.exports = _construct = function _construct(Parent, args, Class) {
28183            var a = [null];
28184            a.push.apply(a, args);
28185            var Constructor = Function.bind.apply(Parent, a);
28186            var instance = new Constructor();
28187            if (Class) setPrototypeOf(instance, Class.prototype);
28188            return instance;
28189          };
28190        }
28191  
28192        return _construct.apply(null, arguments);
28193      }
28194  
28195      module.exports = _construct;
28196    });
28197  
28198    /**
28199     * The base plugin name.
28200     *
28201     * @private
28202     * @constant
28203     * @type {string}
28204     */
28205  
vendor: 13,112 bytes, lines 28206-28610
28206    var BASE_PLUGIN_NAME = 'plugin';
28207    /**
28208     * The key on which a player's active plugins cache is stored.
28209     *
28210     * @private
28211     * @constant
28212     * @type     {string}
28213     */
28214  
28215    var PLUGIN_CACHE_KEY = 'activePlugins_';
28216    /**
28217     * Stores registered plugins in a private space.
28218     *
28219     * @private
28220     * @type    {Object}
28221     */
28222  
28223    var pluginStorage = {};
28224    /**
28225     * Reports whether or not a plugin has been registered.
28226     *
28227     * @private
28228     * @param   {string} name
28229     *          The name of a plugin.
28230     *
28231     * @return {boolean}
28232     *          Whether or not the plugin has been registered.
28233     */
28234  
28235    var pluginExists = function pluginExists(name) {
28236      return pluginStorage.hasOwnProperty(name);
28237    };
28238    /**
28239     * Get a single registered plugin by name.
28240     *
28241     * @private
28242     * @param   {string} name
28243     *          The name of a plugin.
28244     *
28245     * @return {Function|undefined}
28246     *          The plugin (or undefined).
28247     */
28248  
28249  
28250    var getPlugin = function getPlugin(name) {
28251      return pluginExists(name) ? pluginStorage[name] : undefined;
28252    };
28253    /**
28254     * Marks a plugin as "active" on a player.
28255     *
28256     * Also, ensures that the player has an object for tracking active plugins.
28257     *
28258     * @private
28259     * @param   {Player} player
28260     *          A Video.js player instance.
28261     *
28262     * @param   {string} name
28263     *          The name of a plugin.
28264     */
28265  
28266  
28267    var markPluginAsActive = function markPluginAsActive(player, name) {
28268      player[PLUGIN_CACHE_KEY] = player[PLUGIN_CACHE_KEY] || {};
28269      player[PLUGIN_CACHE_KEY][name] = true;
28270    };
28271    /**
28272     * Triggers a pair of plugin setup events.
28273     *
28274     * @private
28275     * @param  {Player} player
28276     *         A Video.js player instance.
28277     *
28278     * @param  {Plugin~PluginEventHash} hash
28279     *         A plugin event hash.
28280     *
28281     * @param  {boolean} [before]
28282     *         If true, prefixes the event name with "before". In other words,
28283     *         use this to trigger "beforepluginsetup" instead of "pluginsetup".
28284     */
28285  
28286  
28287    var triggerSetupEvent = function triggerSetupEvent(player, hash, before) {
28288      var eventName = (before ? 'before' : '') + 'pluginsetup';
28289      player.trigger(eventName, hash);
28290      player.trigger(eventName + ':' + hash.name, hash);
28291    };
28292    /**
28293     * Takes a basic plugin function and returns a wrapper function which marks
28294     * on the player that the plugin has been activated.
28295     *
28296     * @private
28297     * @param   {string} name
28298     *          The name of the plugin.
28299     *
28300     * @param   {Function} plugin
28301     *          The basic plugin.
28302     *
28303     * @return {Function}
28304     *          A wrapper function for the given plugin.
28305     */
28306  
28307  
28308    var createBasicPlugin = function createBasicPlugin(name, plugin) {
28309      var basicPluginWrapper = function basicPluginWrapper() {
28310        // We trigger the "beforepluginsetup" and "pluginsetup" events on the player
28311        // regardless, but we want the hash to be consistent with the hash provided
28312        // for advanced plugins.
28313        //
28314        // The only potentially counter-intuitive thing here is the `instance` in
28315        // the "pluginsetup" event is the value returned by the `plugin` function.
28316        triggerSetupEvent(this, {
28317          name: name,
28318          plugin: plugin,
28319          instance: null
28320        }, true);
28321        var instance = plugin.apply(this, arguments);
28322        markPluginAsActive(this, name);
28323        triggerSetupEvent(this, {
28324          name: name,
28325          plugin: plugin,
28326          instance: instance
28327        });
28328        return instance;
28329      };
28330  
28331      Object.keys(plugin).forEach(function (prop) {
28332        basicPluginWrapper[prop] = plugin[prop];
28333      });
28334      return basicPluginWrapper;
28335    };
28336    /**
28337     * Takes a plugin sub-class and returns a factory function for generating
28338     * instances of it.
28339     *
28340     * This factory function will replace itself with an instance of the requested
28341     * sub-class of Plugin.
28342     *
28343     * @private
28344     * @param   {string} name
28345     *          The name of the plugin.
28346     *
28347     * @param   {Plugin} PluginSubClass
28348     *          The advanced plugin.
28349     *
28350     * @return {Function}
28351     */
28352  
28353  
28354    var createPluginFactory = function createPluginFactory(name, PluginSubClass) {
28355      // Add a `name` property to the plugin prototype so that each plugin can
28356      // refer to itself by name.
28357      PluginSubClass.prototype.name = name;
28358      return function () {
28359        triggerSetupEvent(this, {
28360          name: name,
28361          plugin: PluginSubClass,
28362          instance: null
28363        }, true);
28364  
28365        for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) {
28366          args[_key] = arguments[_key];
28367        }
28368  
28369        var instance = construct(PluginSubClass, [this].concat(args)); // The plugin is replaced by a function that returns the current instance.
28370  
28371  
28372        this[name] = function () {
28373          return instance;
28374        };
28375  
28376        triggerSetupEvent(this, instance.getEventHash());
28377        return instance;
28378      };
28379    };
28380    /**
28381     * Parent class for all advanced plugins.
28382     *
28383     * @mixes   module:evented~EventedMixin
28384     * @mixes   module:stateful~StatefulMixin
28385     * @fires   Player#beforepluginsetup
28386     * @fires   Player#beforepluginsetup:$name
28387     * @fires   Player#pluginsetup
28388     * @fires   Player#pluginsetup:$name
28389     * @listens Player#dispose
28390     * @throws  {Error}
28391     *          If attempting to instantiate the base {@link Plugin} class
28392     *          directly instead of via a sub-class.
28393     */
28394  
28395  
28396    var Plugin = /*#__PURE__*/function () {
28397      /**
28398       * Creates an instance of this class.
28399       *
28400       * Sub-classes should call `super` to ensure plugins are properly initialized.
28401       *
28402       * @param {Player} player
28403       *        A Video.js player instance.
28404       */
28405      function Plugin(player) {
28406        if (this.constructor === Plugin) {
28407          throw new Error('Plugin must be sub-classed; not directly instantiated.');
28408        }
28409  
28410        this.player = player; // Make this object evented, but remove the added `trigger` method so we
28411        // use the prototype version instead.
28412  
28413        evented(this);
28414        delete this.trigger;
28415        stateful(this, this.constructor.defaultState);
28416        markPluginAsActive(player, this.name); // Auto-bind the dispose method so we can use it as a listener and unbind
28417        // it later easily.
28418  
28419        this.dispose = bind(this, this.dispose); // If the player is disposed, dispose the plugin.
28420  
28421        player.on('dispose', this.dispose);
28422      }
28423      /**
28424       * Get the version of the plugin that was set on <pluginName>.VERSION
28425       */
28426  
28427  
28428      var _proto = Plugin.prototype;
28429  
28430      _proto.version = function version() {
28431        return this.constructor.VERSION;
28432      }
28433      /**
28434       * Each event triggered by plugins includes a hash of additional data with
28435       * conventional properties.
28436       *
28437       * This returns that object or mutates an existing hash.
28438       *
28439       * @param   {Object} [hash={}]
28440       *          An object to be used as event an event hash.
28441       *
28442       * @return {Plugin~PluginEventHash}
28443       *          An event hash object with provided properties mixed-in.
28444       */
28445      ;
28446  
28447      _proto.getEventHash = function getEventHash(hash) {
28448        if (hash === void 0) {
28449          hash = {};
28450        }
28451  
28452        hash.name = this.name;
28453        hash.plugin = this.constructor;
28454        hash.instance = this;
28455        return hash;
28456      }
28457      /**
28458       * Triggers an event on the plugin object and overrides
28459       * {@link module:evented~EventedMixin.trigger|EventedMixin.trigger}.
28460       *
28461       * @param   {string|Object} event
28462       *          An event type or an object with a type property.
28463       *
28464       * @param   {Object} [hash={}]
28465       *          Additional data hash to merge with a
28466       *          {@link Plugin~PluginEventHash|PluginEventHash}.
28467       *
28468       * @return {boolean}
28469       *          Whether or not default was prevented.
28470       */
28471      ;
28472  
28473      _proto.trigger = function trigger$1(event, hash) {
28474        if (hash === void 0) {
28475          hash = {};
28476        }
28477  
28478        return trigger(this.eventBusEl_, event, this.getEventHash(hash));
28479      }
28480      /**
28481       * Handles "statechanged" events on the plugin. No-op by default, override by
28482       * subclassing.
28483       *
28484       * @abstract
28485       * @param    {Event} e
28486       *           An event object provided by a "statechanged" event.
28487       *
28488       * @param    {Object} e.changes
28489       *           An object describing changes that occurred with the "statechanged"
28490       *           event.
28491       */
28492      ;
28493  
28494      _proto.handleStateChanged = function handleStateChanged(e) {}
28495      /**
28496       * Disposes a plugin.
28497       *
28498       * Subclasses can override this if they want, but for the sake of safety,
28499       * it's probably best to subscribe the "dispose" event.
28500       *
28501       * @fires Plugin#dispose
28502       */
28503      ;
28504  
28505      _proto.dispose = function dispose() {
28506        var name = this.name,
28507            player = this.player;
28508        /**
28509         * Signals that a advanced plugin is about to be disposed.
28510         *
28511         * @event Plugin#dispose
28512         * @type  {EventTarget~Event}
28513         */
28514  
28515        this.trigger('dispose');
28516        this.off();
28517        player.off('dispose', this.dispose); // Eliminate any possible sources of leaking memory by clearing up
28518        // references between the player and the plugin instance and nulling out
28519        // the plugin's state and replacing methods with a function that throws.
28520  
28521        player[PLUGIN_CACHE_KEY][name] = false;
28522        this.player = this.state = null; // Finally, replace the plugin name on the player with a new factory
28523        // function, so that the plugin is ready to be set up again.
28524  
28525        player[name] = createPluginFactory(name, pluginStorage[name]);
28526      }
28527      /**
28528       * Determines if a plugin is a basic plugin (i.e. not a sub-class of `Plugin`).
28529       *
28530       * @param   {string|Function} plugin
28531       *          If a string, matches the name of a plugin. If a function, will be
28532       *          tested directly.
28533       *
28534       * @return {boolean}
28535       *          Whether or not a plugin is a basic plugin.
28536       */
28537      ;
28538  
28539      Plugin.isBasic = function isBasic(plugin) {
28540        var p = typeof plugin === 'string' ? getPlugin(plugin) : plugin;
28541        return typeof p === 'function' && !Plugin.prototype.isPrototypeOf(p.prototype);
28542      }
28543      /**
28544       * Register a Video.js plugin.
28545       *
28546       * @param   {string} name
28547       *          The name of the plugin to be registered. Must be a string and
28548       *          must not match an existing plugin or a method on the `Player`
28549       *          prototype.
28550       *
28551       * @param   {Function} plugin
28552       *          A sub-class of `Plugin` or a function for basic plugins.
28553       *
28554       * @return {Function}
28555       *          For advanced plugins, a factory function for that plugin. For
28556       *          basic plugins, a wrapper function that initializes the plugin.
28557       */
28558      ;
28559  
28560      Plugin.registerPlugin = function registerPlugin(name, plugin) {
28561        if (typeof name !== 'string') {
28562          throw new Error("Illegal plugin name, \"" + name + "\", must be a string, was " + typeof name + ".");
28563        }
28564  
28565        if (pluginExists(name)) {
28566          log.warn("A plugin named \"" + name + "\" already exists. You may want to avoid re-registering plugins!");
28567        } else if (Player.prototype.hasOwnProperty(name)) {
28568          throw new Error("Illegal plugin name, \"" + name + "\", cannot share a name with an existing player method!");
28569        }
28570  
28571        if (typeof plugin !== 'function') {
28572          throw new Error("Illegal plugin for \"" + name + "\", must be a function, was " + typeof plugin + ".");
28573        }
28574  
28575        pluginStorage[name] = plugin; // Add a player prototype method for all sub-classed plugins (but not for
28576        // the base Plugin class).
28577  
28578        if (name !== BASE_PLUGIN_NAME) {
28579          if (Plugin.isBasic(plugin)) {
28580            Player.prototype[name] = createBasicPlugin(name, plugin);
28581          } else {
28582            Player.prototype[name] = createPluginFactory(name, plugin);
28583          }
28584        }
28585  
28586        return plugin;
28587      }
28588      /**
28589       * De-register a Video.js plugin.
28590       *
28591       * @param  {string} name
28592       *         The name of the plugin to be de-registered. Must be a string that
28593       *         matches an existing plugin.
28594       *
28595       * @throws {Error}
28596       *         If an attempt is made to de-register the base plugin.
28597       */
28598      ;
28599  
28600      Plugin.deregisterPlugin = function deregisterPlugin(name) {
28601        if (name === BASE_PLUGIN_NAME) {
28602          throw new Error('Cannot de-register base plugin.');
28603        }
28604  
28605        if (pluginExists(name)) {
28606          delete pluginStorage[name];
28607          delete Player.prototype[name];
28608        }
28609      }
28610      /**
28611       * Gets an object containing multiple Video.js plugins.
28612       *
28613       * @param   {Array} [names]
28614       *          If provided, should be an array of plugin names. Defaults to _all_
28615       *          plugin names.
28616       *
28617       * @return {Object|undefined}
28618       *          An object containing plugin(s) associated with their name(s) or
28619       *          `undefined` if no matching plugins exist).
28620       */
28621      ;
28622  
28623      Plugin.getPlugins = function getPlugins(names) {
28624        if (names === void 0) {
28625          names = Object.keys(pluginStorage);
28626        }
28627  
28628        var result;
28629        names.forEach(function (name) {
28630          var plugin = getPlugin(name);
28631  
28632          if (plugin) {
28633            result = result || {};
28634            result[name] = plugin;
28635          }
28636        });
28637        return result;
28638      }
28639      /**
28640       * Gets a plugin's version, if available
28641       *
28642       * @param   {string} name
28643       *          The name of a plugin.
28644       *
28645       * @return {string}
28646       *          The plugin's version or an empty string.
28647       */
28648      ;
28649  
28650      Plugin.getPluginVersion = function getPluginVersion(name) {
28651        var plugin = getPlugin(name);
28652        return plugin && plugin.VERSION || '';
28653      };
28654  
28655      return Plugin;
28656    }();
28657    /**
28658     * Gets a plugin by name if it exists.
28659     *
28660     * @static
28661     * @method   getPlugin
28662     * @memberOf Plugin
28663     * @param    {string} name
28664     *           The name of a plugin.
28665     *
28666     * @returns  {Function|undefined}
28667     *           The plugin (or `undefined`).
28668     */
28669  
28670  
28671    Plugin.getPlugin = getPlugin;
28672    /**
28673     * The name of the base plugin class as it is registered.
28674     *
28675     * @type {string}
28676     */
28677  
28678    Plugin.BASE_PLUGIN_NAME = BASE_PLUGIN_NAME;
28679    Plugin.registerPlugin(BASE_PLUGIN_NAME, Plugin);
28680    /**
28681     * Documented in player.js
28682     *
28683     * @ignore
28684     */
28685  
28686    Player.prototype.usingPlugin = function (name) {
28687      return !!this[PLUGIN_CACHE_KEY] && this[PLUGIN_CACHE_KEY][name] === true;
28688    };
28689    /**
28690     * Documented in player.js
28691     *
28692     * @ignore
28693     */
28694  
28695  
28696    Player.prototype.hasPlugin = function (name) {
28697      return !!pluginExists(name);
28698    };
28699    /**
28700     * Signals that a plugin is about to be set up on a player.
28701     *
28702     * @event    Player#beforepluginsetup
28703     * @type     {Plugin~PluginEventHash}
28704     */
28705  
28706    /**
28707     * Signals that a plugin is about to be set up on a player - by name. The name
28708     * is the name of the plugin.
28709     *
28710     * @event    Player#beforepluginsetup:$name
28711     * @type     {Plugin~PluginEventHash}
28712     */
28713  
28714    /**
28715     * Signals that a plugin has just been set up on a player.
28716     *
28717     * @event    Player#pluginsetup
28718     * @type     {Plugin~PluginEventHash}
28719     */
28720  
28721    /**
28722     * Signals that a plugin has just been set up on a player - by name. The name
28723     * is the name of the plugin.
28724     *
28725     * @event    Player#pluginsetup:$name
28726     * @type     {Plugin~PluginEventHash}
28727     */
28728  
28729    /**
28730     * @typedef  {Object} Plugin~PluginEventHash
28731     *
28732     * @property {string} instance
28733     *           For basic plugins, the return value of the plugin function. For
28734     *           advanced plugins, the plugin instance on which the event is fired.
28735     *
28736     * @property {string} name
28737     *           The name of the plugin.
28738     *
28739     * @property {string} plugin
28740     *           For basic plugins, the plugin function. For advanced plugins, the
28741     *           plugin class/constructor.
28742     */
28743  
28744    function _inherits(subClass, superClass) {
28745      if (typeof superClass !== "function" && superClass !== null) {
28746        throw new TypeError("Super expression must either be null or a function");
28747      }
28748  
28749      subClass.prototype = Object.create(superClass && superClass.prototype, {
28750        constructor: {
28751          value: subClass,
28752          writable: true,
28753          configurable: true
28754        }
28755      });
28756      if (superClass) setPrototypeOf(subClass, superClass);
28757    }
28758  
28759    var inherits = _inherits;
28760  
28761    /**
28762     * @file extend.js
28763     * @module extend
28764     */
28765    /**
28766     * Used to subclass an existing class by emulating ES subclassing using the
28767     * `extends` keyword.
28768     *
28769     * @function
28770     * @example
28771     * var MyComponent = videojs.extend(videojs.getComponent('Component'), {
28772     *   myCustomMethod: function() {
28773     *     // Do things in my method.
28774     *   }
28775     * });
28776     *
28777     * @param    {Function} superClass
28778     *           The class to inherit from
28779     *
28780     * @param    {Object}   [subClassMethods={}]
28781     *           Methods of the new class
28782     *
28783     * @return   {Function}
28784     *           The new class with subClassMethods that inherited superClass.
28785     */
28786  
28787    var extend = function extend(superClass, subClassMethods) {
28788      if (subClassMethods === void 0) {
28789        subClassMethods = {};
28790      }
28791  
28792      var subClass = function subClass() {
28793        superClass.apply(this, arguments);
28794      };
28795  
28796      var methods = {};
28797  
28798      if (typeof subClassMethods === 'object') {
28799        if (subClassMethods.constructor !== Object.prototype.constructor) {
28800          subClass = subClassMethods.constructor;
28801        }
28802  
28803        methods = subClassMethods;
28804      } else if (typeof subClassMethods === 'function') {
28805        subClass = subClassMethods;
28806      }
28807  
28808      inherits(subClass, superClass); // this is needed for backward-compatibility and node compatibility.
28809  
28810  
28811      if (superClass) {
28812        subClass.super_ = superClass;
28813      } // Extend subObj's prototype with functions and other properties from props
28814  
28815  
28816      for (var name in methods) {
28817        if (methods.hasOwnProperty(name)) {
28818          subClass.prototype[name] = methods[name];
28819        }
28820      }
28821  
28822      return subClass;
28823    };
28824  
28825    /**
28826     * @file video.js
28827     * @module videojs
28828     */
28829    /**
28830     * Normalize an `id` value by trimming off a leading `#`
28831     *
28832     * @private
28833     * @param   {string} id
28834     *          A string, maybe with a leading `#`.
28835     *
28836     * @return {string}
28837     *          The string, without any leading `#`.
28838     */
28839  
28840    var normalizeId = function normalizeId(id) {
28841      return id.indexOf('#') === 0 ? id.slice(1) : id;
28842    };
28843    /**
28844     * The `videojs()` function doubles as the main function for users to create a
28845     * {@link Player} instance as well as the main library namespace.
28846     *
28847     * It can also be used as a getter for a pre-existing {@link Player} instance.
28848     * However, we _strongly_ recommend using `videojs.getPlayer()` for this
28849     * purpose because it avoids any potential for unintended initialization.
28850     *
28851     * Due to [limitations](https://github.com/jsdoc3/jsdoc/issues/955#issuecomment-313829149)
28852     * of our JSDoc template, we cannot properly document this as both a function
28853     * and a namespace, so its function signature is documented here.
28854     *
28855     * #### Arguments
28856     * ##### id
28857     * string|Element, **required**
28858     *
28859     * Video element or video element ID.
28860     *
28861     * ##### options
28862     * Object, optional
28863     *
28864     * Options object for providing settings.
28865     * See: [Options Guide](https://docs.videojs.com/tutorial-options.html).
28866     *
28867     * ##### ready
28868     * {@link Component~ReadyCallback}, optional
28869     *
28870     * A function to be called when the {@link Player} and {@link Tech} are ready.
28871     *
28872     * #### Return Value
28873     *
28874     * The `videojs()` function returns a {@link Player} instance.
28875     *
28876     * @namespace
28877     *
28878     * @borrows AudioTrack as AudioTrack
28879     * @borrows Component.getComponent as getComponent
28880     * @borrows module:computed-style~computedStyle as computedStyle
28881     * @borrows module:events.on as on
28882     * @borrows module:events.one as one
28883     * @borrows module:events.off as off
28884     * @borrows module:events.trigger as trigger
28885     * @borrows EventTarget as EventTarget
28886     * @borrows module:extend~extend as extend
28887     * @borrows module:fn.bind as bind
28888     * @borrows module:format-time.formatTime as formatTime
28889     * @borrows module:format-time.resetFormatTime as resetFormatTime
28890     * @borrows module:format-time.setFormatTime as setFormatTime
28891     * @borrows module:merge-options.mergeOptions as mergeOptions
28892     * @borrows module:middleware.use as use
28893     * @borrows Player.players as players
28894     * @borrows Plugin.registerPlugin as registerPlugin
28895     * @borrows Plugin.deregisterPlugin as deregisterPlugin
28896     * @borrows Plugin.getPlugins as getPlugins
28897     * @borrows Plugin.getPlugin as getPlugin
28898     * @borrows Plugin.getPluginVersion as getPluginVersion
28899     * @borrows Tech.getTech as getTech
28900     * @borrows Tech.registerTech as registerTech
28901     * @borrows TextTrack as TextTrack
28902     * @borrows module:time-ranges.createTimeRanges as createTimeRange
28903     * @borrows module:time-ranges.createTimeRanges as createTimeRanges
28904     * @borrows module:url.isCrossOrigin as isCrossOrigin
28905     * @borrows module:url.parseUrl as parseUrl
28906     * @borrows VideoTrack as VideoTrack
28907     *
28908     * @param  {string|Element}
28908 id
28909     *         Video element or video element ID.
28910     *
28911     * @param  {Object} [options]
28912     *         Options object for providing settings.
28913     *         See: [Options Guide](https://docs.videojs.com/tutorial-options.html).
28914     *
28915     * @param  {Component~ReadyCallback} [ready]
28916     *         A function to be called when the {@link Player} and {@link Tech} are
28917     *         ready.
28918     *
28919     * @return {Player}
28920     *         The `videojs()` function returns a {@link Player|Player} instance.
28921     */
28922  
28923  
28924    function videojs$1(id, options, ready) {
28925      var player = videojs$1.getPlayer(id);
28926  
28927      if (player) {
28928        if (options) {
28929          log.warn("Player \"" + id + "\" is already initialised. Options will not be applied.");
28930        }
28931  
28932        if (ready) {
28933          player.ready(ready);
28934        }
28935  
28936        return player;
28937      }
28938  
28939      var el = typeof id === 'string' ? $('#' + normalizeId(id)) : id;
28940  
28941      if (!isEl(el)) {
28942        throw new TypeError('The element or ID supplied is not valid. (videojs)');
28943      } // document.body.contains(el) will only check if el is contained within that one document.
28944      // This causes problems for elements in iframes.
28945      // Instead, use the element's ownerDocument instead of the global document.
28946      // This will make sure that the element is indeed in the dom of that document.
28947      // Additionally, check that the document in question has a default view.
28948      // If the document is no longer attached to the dom, the defaultView of the document will be null.
28949  
28950  
28951      if (!el.ownerDocument.defaultView || !el.ownerDocument.body.contains(el)) {
28952        log.warn('The element supplied is not included in the DOM');
28953      }
28954  
28955      options = options || {};
28956      videojs$1.hooks('beforesetup').forEach(function (hookFunction) {
28957        var opts = hookFunction(el, mergeOptions(options));
28958  
28959        if (!isObject(opts) || Array.isArray(opts)) {
28960          log.error('please return an object in beforesetup hooks');
28961          return;
28962        }
28963  
28964        options = mergeOptions(options, opts);
28965      }); // We get the current "Player" component here in case an integration has
28966      // replaced it with a custom player.
28967  
28968      var PlayerComponent = Component.getComponent('Player');
28969      player = new PlayerComponent(el, options, ready);
28970      videojs$1.hooks('setup').forEach(function (hookFunction) {
28971        return hookFunction(player);
28972      });
28973      return player;
28974    }
28975    /**
28976     * An Object that contains lifecycle hooks as keys which point to an array
28977     * of functions that are run when a lifecycle is triggered
28978     *
28979     * @private
28980     */
28981  
28982  
28983    videojs$1.hooks_ = {};
28984    /**
28985     * Get a list of hooks for a specific lifecycle
28986     *
28987     * @param  {string} type
28988     *         the lifecyle to get hooks from
28989     *
28990     * @param  {Function|Function[]} [fn]
28991     *         Optionally add a hook (or hooks) to the lifecycle that your are getting.
28992     *
28993     * @return {Array}
28994     *         an array of hooks, or an empty array if there are none.
28995     */
28996  
28997    videojs$1.hooks = function (type, fn) {
28998      videojs$1.hooks_[type] = videojs$1.hooks_[type] || [];
28999  
29000      if (fn) {
29001        videojs$1.hooks_[type] = videojs$1.hooks_[type].concat(fn);
29002      }
29003  
29004      return videojs$1.hooks_[type];
29005    };
29006    /**
29007     * Add a function hook to a specific videojs lifecycle.
29008     *
29009     * @param {string} type
29010     *        the lifecycle to hook the function to.
29011     *
29012     * @param {Function|Function[]}
29013     *        The function or array of functions to attach.
29014     */
29015  
29016  
29017    videojs$1.hook = function (type, fn) {
29018      videojs$1.hooks(type, fn);
29019    };
29020    /**
29021     * Add a function hook that will only run once to a specific videojs lifecycle.
29022     *
29023     * @param {string} type
29024     *        the lifecycle to hook the function to.
29025     *
29026     * @param {Function|Function[]}
29027     *        The function or array of functions to attach.
29028     */
29029  
29030  
29031    videojs$1.hookOnce = function (type, fn) {
29032      videojs$1.hooks(type, [].concat(fn).map(function (original) {
29033        var wrapper = function wrapper() {
29034          videojs$1.removeHook(type, wrapper);
29035          return original.apply(void 0, arguments);
29036        };
29037  
29038        return wrapper;
29039      }));
29040    };
29041    /**
29042     * Remove a hook from a specific videojs lifecycle.
29043     *
29044     * @param  {string} type
29045     *         the lifecycle that the function hooked to
29046     *
29047     * @param  {Function} fn
29048     *         The hooked function to remove
29049     *
29050     * @return {boolean}
29051     *         The function that was removed or undef
29052     */
29053  
29054  
vendor: 9,401 bytes, lines 29055-29351
29055    videojs$1.removeHook = function (type, fn) {
29056      var index = videojs$1.hooks(type).indexOf(fn);
29057  
29058      if (index <= -1) {
29059        return false;
29060      }
29061  
29062      videojs$1.hooks_[type] = videojs$1.hooks_[type].slice();
29063      videojs$1.hooks_[type].splice(index, 1);
29064      return true;
29065    }; // Add default styles
29066  
29067  
29068    if (window$3.VIDEOJS_NO_DYNAMIC_STYLE !== true && isReal()) {
29069      var style = $('.vjs-styles-defaults');
29070  
29071      if (!style) {
29072        style = createStyleElement('vjs-styles-defaults');
29073        var head = $('head');
29074  
29075        if (head) {
29076          head.insertBefore(style, head.firstChild);
29077        }
29078  
29079        setTextContent(style, "\n      .video-js {\n        width: 300px;\n        height: 150px;\n      }\n\n      .vjs-fluid {\n        padding-top: 56.25%\n      }\n    ");
29080      }
29081    } // Run Auto-load players
29082    // You have to wait at least once in case this script is loaded after your
29083    // video in the DOM (weird behavior only with minified version)
29084  
29085  
29086    autoSetupTimeout(1, videojs$1);
29087    /**
29088     * Current Video.js version. Follows [semantic versioning](https://semver.org/).
29089     *
29090     * @type {string}
29091     */
29092  
29093    videojs$1.VERSION = version;
29094    /**
29095     * The global options object. These are the settings that take effect
29096     * if no overrides are specified when the player is created.
29097     *
29098     * @type {Object}
29099     */
29100  
29101    videojs$1.options = Player.prototype.options_;
29102    /**
29103     * Get an object with the currently created players, keyed by player ID
29104     *
29105     * @return {Object}
29106     *         The created players
29107     */
29108  
29109    videojs$1.getPlayers = function () {
29110      return Player.players;
29111    };
29112    /**
29113     * Get a single player based on an ID or DOM element.
29114     *
29115     * This is useful if you want to check if an element or ID has an associated
29116     * Video.js player, but not create one if it doesn't.
29117     *
29118     * @param   {string|Element} id
29119     *          An HTML element - `<video>`, `<audio>`, or `<video-js>` -
29120     *          or a string matching the `id` of such an element.
29121     *
29122     * @return {Player|undefined}
29123     *          A player instance or `undefined` if there is no player instance
29124     *          matching the argument.
29125     */
29126  
29127  
29128    videojs$1.getPlayer = function (id) {
29129      var players = Player.players;
29130      var tag;
29131  
29132      if (typeof id === 'string') {
29133        var nId = normalizeId(id);
29134        var player = players[nId];
29135  
29136        if (player) {
29137          return player;
29138        }
29139  
29140        tag = $('#' + nId);
29141      } else {
29142        tag = id;
29143      }
29144  
29145      if (isEl(tag)) {
29146        var _tag = tag,
29147            _player = _tag.player,
29148            playerId = _tag.playerId; // Element may have a `player` property referring to an already created
29149        // player instance. If so, return that.
29150  
29151        if (_player || players[playerId]) {
29152          return _player || players[playerId];
29153        }
29154      }
29155    };
29156    /**
29157     * Returns an array of all current players.
29158     *
29159     * @return {Array}
29160     *         An array of all players. The array will be in the order that
29161     *         `Object.keys` provides, which could potentially vary between
29162     *         JavaScript engines.
29163     *
29164     */
29165  
29166  
29167    videojs$1.getAllPlayers = function () {
29168      return (// Disposed players leave a key with a `null` value, so we need to make sure
29169        // we filter those out.
29170        Object.keys(Player.players).map(function (k) {
29171          return Player.players[k];
29172        }).filter(Boolean)
29173      );
29174    };
29175  
29176    videojs$1.players = Player.players;
29177    videojs$1.getComponent = Component.getComponent;
29178    /**
29179     * Register a component so it can referred to by name. Used when adding to other
29180     * components, either through addChild `component.addChild('myComponent')` or through
29181     * default children options  `{ children: ['myComponent'] }`.
29182     *
29183     * > NOTE: You could also just initialize the component before adding.
29184     * `component.addChild(new MyComponent());`
29185     *
29186     * @param {string} name
29187     *        The class name of the component
29188     *
29189     * @param {Component} comp
29190     *        The component class
29191     *
29192     * @return {Component}
29193     *         The newly registered component
29194     */
29195  
29196    videojs$1.registerComponent = function (name, comp) {
29197      if (Tech.isTech(comp)) {
29198        log.warn("The " + name + " tech was registered as a component. It should instead be registered using videojs.registerTech(name, tech)");
29199      }
29200  
29201      Component.registerComponent.call(Component, name, comp);
29202    };
29203  
29204    videojs$1.getTech = Tech.getTech;
29205    videojs$1.registerTech = Tech.registerTech;
29206    videojs$1.use = use;
29207    /**
29208     * An object that can be returned by a middleware to signify
29209     * that the middleware is being terminated.
29210     *
29211     * @type {object}
29212     * @property {object} middleware.TERMINATOR
29213     */
29214  
29215    Object.defineProperty(videojs$1, 'middleware', {
29216      value: {},
29217      writeable: false,
29218      enumerable: true
29219    });
29220    Object.defineProperty(videojs$1.middleware, 'TERMINATOR', {
29221      value: TERMINATOR,
29222      writeable: false,
29223      enumerable: true
29224    });
29225    /**
29226     * A reference to the {@link module:browser|browser utility module} as an object.
29227     *
29228     * @type {Object}
29229     * @see  {@link module:browser|browser}
29230     */
29231  
29232    videojs$1.browser = browser;
29233    /**
29234     * Use {@link module:browser.TOUCH_ENABLED|browser.TOUCH_ENABLED} instead; only
29235     * included for backward-compatibility with 4.x.
29236     *
29237     * @deprecated Since version 5.0, use {@link module:browser.TOUCH_ENABLED|browser.TOUCH_ENABLED instead.
29238     * @type {boolean}
29239     */
29240  
29241    videojs$1.TOUCH_ENABLED = TOUCH_ENABLED;
29242    videojs$1.extend = extend;
29243    videojs$1.mergeOptions = mergeOptions;
29244    videojs$1.bind = bind;
29245    videojs$1.registerPlugin = Plugin.registerPlugin;
29246    videojs$1.deregisterPlugin = Plugin.deregisterPlugin;
29247    /**
29248     * Deprecated method to register a plugin with Video.js
29249     *
29250     * @deprecated videojs.plugin() is deprecated; use videojs.registerPlugin() instead
29251     *
29252     * @param {string} name
29253     *        The plugin name
29254     *
29255     * @param {Plugin|Function} plugin
29256     *         The plugin sub-class or function
29257     */
29258  
29259    videojs$1.plugin = function (name, plugin) {
29260      log.warn('videojs.plugin() is deprecated; use videojs.registerPlugin() instead');
29261      return Plugin.registerPlugin(name, plugin);
29262    };
29263  
29264    videojs$1.getPlugins = Plugin.getPlugins;
29265    videojs$1.getPlugin = Plugin.getPlugin;
29266    videojs$1.getPluginVersion = Plugin.getPluginVersion;
29267    /**
29268     * Adding languages so that they're available to all players.
29269     * Example: `videojs.addLanguage('es', { 'Hello': 'Hola' });`
29270     *
29271     * @param {string} code
29272     *        The language code or dictionary property
29273     *
29274     * @param {Object} data
29275     *        The data values to be translated
29276     *
29277     * @return {Object}
29278     *         The resulting language dictionary object
29279     */
29280  
29281    videojs$1.addLanguage = function (code, data) {
29282      var _mergeOptions;
29283  
29284      code = ('' + code).toLowerCase();
29285      videojs$1.options.languages = mergeOptions(videojs$1.options.languages, (_mergeOptions = {}, _mergeOptions[code] = data, _mergeOptions));
29286      return videojs$1.options.languages[code];
29287    };
29288    /**
29289     * A reference to the {@link module:log|log utility module} as an object.
29290     *
29291     * @type {Function}
29292     * @see  {@link module:log|log}
29293     */
29294  
29295  
29296    videojs$1.log = log;
29297    videojs$1.createLogger = createLogger$1;
29298    videojs$1.createTimeRange = videojs$1.createTimeRanges = createTimeRanges;
29299    videojs$1.formatTime = formatTime;
29300    videojs$1.setFormatTime = setFormatTime;
29301    videojs$1.resetFormatTime = resetFormatTime;
29302    videojs$1.parseUrl = parseUrl;
29303    videojs$1.isCrossOrigin = isCrossOrigin;
29304    videojs$1.EventTarget = EventTarget;
29305    videojs$1.on = on;
29306    videojs$1.one = one;
29307    videojs$1.off = off;
29308    videojs$1.trigger = trigger;
29309    /**
29310     * A cross-browser XMLHttpRequest wrapper.
29311     *
29312     * @function
29313     * @param    {Object} options
29314     *           Settings for the request.
29315     *
29316     * @return   {XMLHttpRequest|XDomainRequest}
29317     *           The request object.
29318     *
29319     * @see      https://github.com/Raynos/xhr
29320     */
29321  
29322    videojs$1.xhr = xhr;
29323    videojs$1.TextTrack = TextTrack;
29324    videojs$1.AudioTrack = AudioTrack;
29325    videojs$1.VideoTrack = VideoTrack;
29326    ['isEl', 'isTextNode', 'createEl', 'hasClass', 'addClass', 'removeClass', 'toggleClass', 'setAttributes', 'getAttributes', 'emptyEl', 'appendContent', 'insertContent'].forEach(function (k) {
29327      videojs$1[k] = function () {
29328        log.warn("videojs." + k + "() is deprecated; use videojs.dom." + k + "() instead");
29329        return Dom[k].apply(null, arguments);
29330      };
29331    });
29332    videojs$1.computedStyle = computedStyle;
29333    /**
29334     * A reference to the {@link module:dom|DOM utility module} as an object.
29335     *
29336     * @type {Object}
29337     * @see  {@link module:dom|dom}
29338     */
29339  
29340    videojs$1.dom = Dom;
29341    /**
29342     * A reference to the {@link module:url|URL utility module} as an object.
29343     *
29344     * @type {Object}
29345     * @see  {@link module:url|url}
29346     */
29347  
29348    videojs$1.url = Url;
29349    videojs$1.defineLazyProperty = defineLazyProperty;
29350  
29351    var urlToolkit = createCommonjsModule(function (module, exports
vendor: 4,856 bytes, lines 29351-29458
29351) {
29352      // see https://tools.ietf.org/html/rfc1808
29353  
29354      /* jshint ignore:start */
29355      (function (root) {
29356        /* jshint ignore:end */
29357        var URL_REGEX = /^((?:[a-zA-Z0-9+\-.]+:)?)(\/\/[^\/?#]*)?((?:[^\/\?#]*\/)*.*?)??(;.*?)?(\?.*?)?(#.*?)?$/;
29358        var FIRST_SEGMENT_REGEX = /^([^\/?#]*)(.*)$/;
29359        var SLASH_DOT_REGEX = /(?:\/|^)\.(?=\/)/g;
29360        var SLASH_DOT_DOT_REGEX = /(?:\/|^)\.\.\/(?!\.\.\/).*?(?=\/)/g;
29361        var URLToolkit = {
29362          // jshint ignore:line
29363          // If opts.alwaysNormalize is true then the path will always be normalized even when it starts with / or //
29364          // E.g
29365          // With opts.alwaysNormalize = false (default, spec compliant)
29366          // http://a.com/b/cd + /e/f/../g => http://a.com/e/f/../g
29367          // With opts.alwaysNormalize = true (not spec compliant)
29368          // http://a.com/b/cd + /e/f/../g => http://a.com/e/g
29369          buildAbsoluteURL: function buildAbsoluteURL(baseURL, relativeURL, opts) {
29370            opts = opts || {}; // remove any remaining space and CRLF
29371  
29372            baseURL = baseURL.trim();
29373            relativeURL = relativeURL.trim();
29374  
29375            if (!relativeURL) {
29376              // 2a) If the embedded URL is entirely empty, it inherits the
29377              // entire base URL (i.e., is set equal to the base URL)
29378              // and we are done.
29379              if (!opts.alwaysNormalize) {
29380                return baseURL;
29381              }
29382  
29383              var basePartsForNormalise = URLToolkit.parseURL(baseURL);
29384  
29385              if (!basePartsForNormalise) {
29386                throw new Error('Error trying to parse base URL.');
29387              }
29388  
29389              basePartsForNormalise.path = URLToolkit.normalizePath(basePartsForNormalise.path);
29390              return URLToolkit.buildURLFromParts(basePartsForNormalise);
29391            }
29392  
29393            var relativeParts = URLToolkit.parseURL(relativeURL);
29394  
29395            if (!relativeParts) {
29396              throw new Error('Error trying to parse relative URL.');
29397            }
29398  
29399            if (relativeParts.scheme) {
29400              // 2b) If the embedded URL starts with a scheme name, it is
29401              // interpreted as an absolute URL and we are done.
29402              if (!opts.alwaysNormalize) {
29403                return relativeURL;
29404              }
29405  
29406              relativeParts.path = URLToolkit.normalizePath(relativeParts.path);
29407              return URLToolkit.buildURLFromParts(relativeParts);
29408            }
29409  
29410            var baseParts = URLToolkit.parseURL(baseURL);
29411  
29412            if (!baseParts) {
29413              throw new Error('Error trying to parse base URL.');
29414            }
29415  
29416            if (!baseParts.netLoc && baseParts.path && baseParts.path[0] !== '/') {
29417              // If netLoc missing and path doesn't start with '/', assume everthing before the first '/' is the netLoc
29418              // This causes 'example.com/a' to be handled as '//example.com/a' instead of '/example.com/a'
29419              var pathParts = FIRST_SEGMENT_REGEX.exec(baseParts.path);
29420              baseParts.netLoc = pathParts[1];
29421              baseParts.path = pathParts[2];
29422            }
29423  
29424            if (baseParts.netLoc && !baseParts.path) {
29425              baseParts.path = '/';
29426            }
29427  
29428            var builtParts = {
29429              // 2c) Otherwise, the embedded URL inherits the scheme of
29430              // the base URL.
29431              scheme: baseParts.scheme,
29432              netLoc: relativeParts.netLoc,
29433              path: null,
29434              params: relativeParts.params,
29435              query: relativeParts.query,
29436              fragment: relativeParts.fragment
29437            };
29438  
29439            if (!relativeParts.netLoc) {
29440              // 3) If the embedded URL's <net_loc> is non-empty, we skip to
29441              // Step 7.  Otherwise, the embedded URL inherits the <net_loc>
29442              // (if any) of the base URL.
29443              builtParts.netLoc = baseParts.netLoc; // 4) If the embedded URL path is preceded by a slash "/", the
29444              // path is not relative and we skip to Step 7.
29445  
29446              if (relativeParts.path[0] !== '/') {
29447                if (!relativeParts.path) {
29448                  // 5) If the embedded URL path is empty (and not preceded by a
29449                  // slash), then the embedded URL inherits the base URL path
29450                  builtParts.path = baseParts.path; // 5a) if the embedded URL's <params> is non-empty, we skip to
29451                  // step 7; otherwise, it inherits the <params> of the base
29452                  // URL (if any) and
29453  
29454                  if (!relativeParts.params) {
29455                    builtParts.params = baseParts.params; // 5b) if the embedded URL's <query> is non-empty, we skip to
29456                    // step 7; otherwise, it inherits the <query> of the base
29457                    // URL (if any) and we skip to step 7.
29458  
29459                    if (!relativeParts.query) {
29460                      builtParts.query = baseParts.query;
29461                    }
29462                  }
29463                } else {
29464                  // 6) The last segment of the base URL's path (anything
29465                  // following the rightmost slash "/", or the entire path if no
29466                  // slash is present) is removed and the embedded URL's path is
29467                  // appended in its place.
29468                  var baseURLPath = baseParts.path;
29469                  var newPath = baseURLPath.substring(0, baseURLPath.lastIndexOf('/') + 1) + relativeParts.path;
29470                  builtParts.path = URLToolkit.normalizePath(newPath);
29471                }
29472              }
29473            }
29474  
29475            if (builtParts.path === null) {
29476              builtParts.path = opts.alwaysNormalize ? URLToolkit.normalizePath(relativeParts.path) : relativeParts.path;
29477            }
29478  
29479            return URLToolkit.buildURLFromParts(builtParts);
29480          },
29481          parseURL: function parseURL(url) {
29482            var parts = URL_REGEX.exec(url);
29483  
29484            if (!parts) {
29485              return null;
29486            }
29487  
29488            return {
29489              scheme: parts[1] || '',
29490              netLoc: parts[2] || '',
29491              path: parts[3] || '',
29492              params: parts[4] || '',
29493              query: parts[5] || '',
29494              fragment: parts[6] || ''
29495            };
29496          },
29497          normalizePath: function normalizePath(path) {
29498            // The following operations are
29499            // then applied, in order, to the new path:
29500            // 6a) All occurrences of "./", where "." is a complete path
29501            // segment, are removed.
29502            // 6b) If the path ends with "." as a complete path segment,
29503            // that "." is removed.
29504            path = path.split('').reverse().join('').replace(SLASH_DOT_REGEX, ''); // 6c) All occurrences of "<segment>/../", where <segment> is a
29505            // complete path segment not equal to "..", are removed.
29506            // Removal of these path segments is performed iteratively,
29507            // removing the leftmost matching pattern on each iteration,
29508            // until no matching pattern remains.
29509            // 6d) If the path ends with "<segment>/..", where <segment> is a
29510            // complete path segment not equal to "..", that
29511            // "<segment>/.." is removed.
29512  
29513            while (path.length !== (path = path.replace(SLASH_DOT_DOT_REGEX, '')).length) {} // jshint ignore:line
29514  
29515  
29516            return path.split('').reverse().join('');
29517          },
29518          buildURLFromParts: function buildURLFromParts(parts) {
29519            return parts.scheme + parts.netLoc + parts.path + parts.params + parts.query + parts.fragment;
29520          }
29521        };
29522        /* jshint ignore:start */
29523  
29524        module.exports = URLToolkit;
29525      })();
29526      /* jshint ignore:end */
29527  
29528    });
29529  
29530    /*! @name m3u8-parser @version 4.4.0 @license Apache-2.0 */
29531  
29532    function _extends() {
29533      _extends = Object.assign || function (target) {
29534        for (var i = 1; i < arguments.length; i++) {
29535          var source = arguments[i];
29536  
29537          for (var key in source) {
29538            if (Object.prototype.hasOwnProperty.call(source, key)) {
29539              target[key] = source[key];
29540            }
29541          }
29542        }
29543  
29544        return target;
29545      };
29546  
29547      return _extends.apply(this, arguments);
29548    }
29549  
29550    function _inheritsLoose$1(subClass, superClass) {
29551      subClass.prototype = Object.create(superClass.prototype);
29552      subClass.prototype.constructor = subClass;
29553      subClass.__proto__ = superClass;
29554    }
29555  
29556    function _assertThisInitialized$1(self) {
29557      if (self === void 0) {
29558        throw new ReferenceError("this hasn't been initialised - super() hasn't been called");
29559      }
29560  
29561      return self;
29562    }
29563    /**
29564     * @file stream.js
29565     */
29566  
29567    /**
29568     * A lightweight readable stream implementation that handles event dispatching.
29569     *
29570     * @class Stream
29571     */
29572  
29573  
29574    var Stream = /*#__PURE__*/function () {
29575      function Stream() {
29576        this.listeners = {};
29577      }
29578      /**
29579       * Add a listener for a specified event type.
29580       *
29581       * @param {string} type the event name
29582       * @param {Function} listener the callback to be invoked when an event of
29583       * the specified type occurs
29584       */
29585  
29586  
29587      var _proto = Stream.prototype;
29588  
29589      _proto.on = function on(type, listener) {
29590        if (!this.listeners[type]) {
29591          this.listeners[type] = [];
29592        }
29593  
29594        this.listeners[type].push(listener);
29595      }
29596      /**
29597       * Remove a listener for a specified event type.
29598       *
29599       * @param {string} type the event name
29600       * @param {Function} listener  a function previously registered for this
29601       * type of event through `on`
29602       * @return {boolean} if we could turn it off or not
29603       */
29604      ;
29605  
29606      _proto.off = function off(type, listener) {
29607        if (!this.listeners[type]) {
29608          return false;
vendor: 40,377 bytes, lines 29609-30892
29609        }
29610  
29611        var index = this.listeners[type].indexOf(listener);
29612        this.listeners[type].splice(index, 1);
29613        return index > -1;
29614      }
29615      /**
29616       * Trigger an event of the specified type on this stream. Any additional
29617       * arguments to this function are passed as parameters to event listeners.
29618       *
29619       * @param {string} type the event name
29620       */
29621      ;
29622  
29623      _proto.trigger = function trigger(type) {
29624        var callbacks = this.listeners[type];
29625        var i;
29626        var length;
29627        var args;
29628  
29629        if (!callbacks) {
29630          return;
29631        } // Slicing the arguments on every invocation of this method
29632        // can add a significant amount of overhead. Avoid the
29633        // intermediate object creation for the common case of a
29634        // single callback argument
29635  
29636  
29637        if (arguments.length === 2) {
29638          length = callbacks.length;
29639  
29640          for (i = 0; i < length; ++i) {
29641            callbacks[i].call(this, arguments[1]);
29642          }
29643        } else {
29644          args = Array.prototype.slice.call(arguments, 1);
29645          length = callbacks.length;
29646  
29647          for (i = 0; i < length; ++i) {
29648            callbacks[i].apply(this, args);
29649          }
29650        }
29651      }
29652      /**
29653       * Destroys the stream and cleans up.
29654       */
29655      ;
29656  
29657      _proto.dispose = function dispose() {
29658        this.listeners = {};
29659      }
29660      /**
29661       * Forwards all `data` events on this stream to the destination stream. The
29662       * destination stream should provide a method `push` to receive the data
29663       * events as they arrive.
29664       *
29665       * @param {Stream} destination the stream that will receive all `data` events
29666       * @see http://nodejs.org/api/stream.html#stream_readable_pipe_destination_options
29667       */
29668      ;
29669  
29670      _proto.pipe = function pipe(destination) {
29671        this.on('data', function (data) {
29672          destination.push(data);
29673        });
29674      };
29675  
29676      return Stream;
29677    }();
29678    /**
29679     * A stream that buffers string input and generates a `data` event for each
29680     * line.
29681     *
29682     * @class LineStream
29683     * @extends Stream
29684     */
29685  
29686  
29687    var LineStream = /*#__PURE__*/function (_Stream) {
29688      _inheritsLoose$1(LineStream, _Stream);
29689  
29690      function LineStream() {
29691        var _this;
29692  
29693        _this = _Stream.call(this) || this;
29694        _this.buffer = '';
29695        return _this;
29696      }
29697      /**
29698       * Add new data to be parsed.
29699       *
29700       * @param {string} data the text to process
29701       */
29702  
29703  
29704      var _proto = LineStream.prototype;
29705  
29706      _proto.push = function push(data) {
29707        var nextNewline;
29708        this.buffer += data;
29709        nextNewline = this.buffer.indexOf('\n');
29710  
29711        for (; nextNewline > -1; nextNewline = this.buffer.indexOf('\n')) {
29712          this.trigger('data', this.buffer.substring(0, nextNewline));
29713          this.buffer = this.buffer.substring(nextNewline + 1);
29714        }
29715      };
29716  
29717      return LineStream;
29718    }(Stream);
29719    /**
29720     * "forgiving" attribute list psuedo-grammar:
29721     * attributes -> keyvalue (',' keyvalue)*
29722     * keyvalue   -> key '=' value
29723     * key        -> [^=]*
29724     * value      -> '"' [^"]* '"' | [^,]*
29725     */
29726  
29727  
29728    var attributeSeparator = function attributeSeparator() {
29729      var key = '[^=]*';
29730      var value = '"[^"]*"|[^,]*';
29731      var keyvalue = '(?:' + key + ')=(?:' + value + ')';
29732      return new RegExp('(?:^|,)(' + keyvalue + ')');
29733    };
29734    /**
29735     * Parse attributes from a line given the separator
29736     *
29737     * @param {string} attributes the attribute line to parse
29738     */
29739  
29740  
29741    var parseAttributes = function parseAttributes(attributes) {
29742      // split the string using attributes as the separator
29743      var attrs = attributes.split(attributeSeparator());
29744      var result = {};
29745      var i = attrs.length;
29746      var attr;
29747  
29748      while (i--) {
29749        // filter out unmatched portions of the string
29750        if (attrs[i] === '') {
29751          continue;
29752        } // split the key and value
29753  
29754  
29755        attr = /([^=]*)=(.*)/.exec(attrs[i]).slice(1); // trim whitespace and remove optional quotes around the value
29756  
29757        attr[0] = attr[0].replace(/^\s+|\s+$/g, '');
29758        attr[1] = attr[1].replace(/^\s+|\s+$/g, '');
29759        attr[1] = attr[1].replace(/^['"](.*)['"]$/g, '$1');
29760        result[attr[0]] = attr[1];
29761      }
29762  
29763      return result;
29764    };
29765    /**
29766     * A line-level M3U8 parser event stream. It expects to receive input one
29767     * line at a time and performs a context-free parse of its contents. A stream
29768     * interpretation of a manifest can be useful if the manifest is expected to
29769     * be too large to fit comfortably into memory or the entirety of the input
29770     * is not immediately available. Otherwise, it's probably much easier to work
29771     * with a regular `Parser` object.
29772     *
29773     * Produces `data` events with an object that captures the parser's
29774     * interpretation of the input. That object has a property `tag` that is one
29775     * of `uri`, `comment`, or `tag`. URIs only have a single additional
29776     * property, `line`, which captures the entirety of the input without
29777     * interpretation. Comments similarly have a single additional property
29778     * `text` which is the input without the leading `#`.
29779     *
29780     * Tags always have a property `tagType` which is the lower-cased version of
29781     * the M3U8 directive without the `#EXT` or `#EXT-X-` prefix. For instance,
29782     * `#EXT-X-MEDIA-SEQUENCE` becomes `media-sequence` when parsed. Unrecognized
29783     * tags are given the tag type `unknown` and a single additional property
29784     * `data` with the remainder of the input.
29785     *
29786     * @class ParseStream
29787     * @extends Stream
29788     */
29789  
29790  
29791    var ParseStream = /*#__PURE__*/function (_Stream) {
29792      _inheritsLoose$1(ParseStream, _Stream);
29793  
29794      function ParseStream() {
29795        var _this;
29796  
29797        _this = _Stream.call(this) || this;
29798        _this.customParsers = [];
29799        _this.tagMappers = [];
29800        return _this;
29801      }
29802      /**
29803       * Parses an additional line of input.
29804       *
29805       * @param {string} line a single line of an M3U8 file to parse
29806       */
29807  
29808  
29809      var _proto = ParseStream.prototype;
29810  
29811      _proto.push = function push(line) {
29812        var _this2 = this;
29813  
29814        var match;
29815        var event; // strip whitespace
29816  
29817        line = line.trim();
29818  
29819        if (line.length === 0) {
29820          // ignore empty lines
29821          return;
29822        } // URIs
29823  
29824  
29825        if (line[0] !== '#') {
29826          this.trigger('data', {
29827            type: 'uri',
29828            uri: line
29829          });
29830          return;
29831        } // map tags
29832  
29833  
29834        var newLines = this.tagMappers.reduce(function (acc, mapper) {
29835          var mappedLine = mapper(line); // skip if unchanged
29836  
29837          if (mappedLine === line) {
29838            return acc;
29839          }
29840  
29841          return acc.concat([mappedLine]);
29842        }, [line]);
29843        newLines.forEach(function (newLine) {
29844          for (var i = 0; i < _this2.customParsers.length; i++) {
29845            if (_this2.customParsers[i].call(_this2, newLine)) {
29846              return;
29847            }
29848          } // Comments
29849  
29850  
29851          if (newLine.indexOf('#EXT') !== 0) {
29852            _this2.trigger('data', {
29853              type: 'comment',
29854              text: newLine.slice(1)
29855            });
29856  
29857            return;
29858          } // strip off any carriage returns here so the regex matching
29859          // doesn't have to account for them.
29860  
29861  
29862          newLine = newLine.replace('\r', ''); // Tags
29863  
29864          match = /^#EXTM3U/.exec(newLine);
29865  
29866          if (match) {
29867            _this2.trigger('data', {
29868              type: 'tag',
29869              tagType: 'm3u'
29870            });
29871  
29872            return;
29873          }
29874  
29875          match = /^#EXTINF:?([0-9\.]*)?,?(.*)?$/.exec(newLine);
29876  
29877          if (match) {
29878            event = {
29879              type: 'tag',
29880              tagType: 'inf'
29881            };
29882  
29883            if (match[1]) {
29884              event.duration = parseFloat(match[1]);
29885            }
29886  
29887            if (match[2]) {
29888              event.title = match[2];
29889            }
29890  
29891            _this2.trigger('data', event);
29892  
29893            return;
29894          }
29895  
29896          match = /^#EXT-X-TARGETDURATION:?([0-9.]*)?/.exec(newLine);
29897  
29898          if (match) {
29899            event = {
29900              type: 'tag',
29901              tagType: 'targetduration'
29902            };
29903  
29904            if (match[1]) {
29905              event.duration = parseInt(match[1], 10);
29906            }
29907  
29908            _this2.trigger('data', event);
29909  
29910            return;
29911          }
29912  
29913          match = /^#ZEN-TOTAL-DURATION:?([0-9.]*)?/.exec(newLine);
29914  
29915          if (match) {
29916            event = {
29917              type: 'tag',
29918              tagType: 'totalduration'
29919            };
29920  
29921            if (match[1]) {
29922              event.duration = parseInt(match[1], 10);
29923            }
29924  
29925            _this2.trigger('data', event);
29926  
29927            return;
29928          }
29929  
29930          match = /^#EXT-X-VERSION:?([0-9.]*)?/.exec(newLine);
29931  
29932          if (match) {
29933            event = {
29934              type: 'tag',
29935              tagType: 'version'
29936            };
29937  
29938            if (match[1]) {
29939              event.version = parseInt(match[1], 10);
29940            }
29941  
29942            _this2.trigger('data', event);
29943  
29944            return;
29945          }
29946  
29947          match = /^#EXT-X-MEDIA-SEQUENCE:?(\-?[0-9.]*)?/.exec(newLine);
29948  
29949          if (match) {
29950            event = {
29951              type: 'tag',
29952              tagType: 'media-sequence'
29953            };
29954  
29955            if (match[1]) {
29956              event.number = parseInt(match[1], 10);
29957            }
29958  
29959            _this2.trigger('data', event);
29960  
29961            return;
29962          }
29963  
29964          match = /^#EXT-X-DISCONTINUITY-SEQUENCE:?(\-?[0-9.]*)?/.exec(newLine);
29965  
29966          if (match) {
29967            event = {
29968              type: 'tag',
29969              tagType: 'discontinuity-sequence'
29970            };
29971  
29972            if (match[1]) {
29973              event.number = parseInt(match[1], 10);
29974            }
29975  
29976            _this2.trigger('data', event);
29977  
29978            return;
29979          }
29980  
29981          match = /^#EXT-X-PLAYLIST-TYPE:?(.*)?$/.exec(newLine);
29982  
29983          if (match) {
29984            event = {
29985              type: 'tag',
29986              tagType: 'playlist-type'
29987            };
29988  
29989            if (match[1]) {
29990              event.playlistType = match[1];
29991            }
29992  
29993            _this2.trigger('data', event);
29994  
29995            return;
29996          }
29997  
29998          match = /^#EXT-X-BYTERANGE:?([0-9.]*)?@?([0-9.]*)?/.exec(newLine);
29999  
30000          if (match) {
30001            event = {
30002              type: 'tag',
30003              tagType: 'byterange'
30004            };
30005  
30006            if (match[1]) {
30007              event.length = parseInt(match[1], 10);
30008            }
30009  
30010            if (match[2]) {
30011              event.offset = parseInt(match[2], 10);
30012            }
30013  
30014            _this2.trigger('data', event);
30015  
30016            return;
30017          }
30018  
30019          match = /^#EXT-X-ALLOW-CACHE:?(YES|NO)?/.exec(newLine);
30020  
30021          if (match) {
30022            event = {
30023              type: 'tag',
30024              tagType: 'allow-cache'
30025            };
30026  
30027            if (match[1]) {
30028              event.allowed = !/NO/.test(match[1]);
30029            }
30030  
30031            _this2.trigger('data', event);
30032  
30033            return;
30034          }
30035  
30036          match = /^#EXT-X-MAP:?(.*)$/.exec(newLine);
30037  
30038          if (match) {
30039            event = {
30040              type: 'tag',
30041              tagType: 'map'
30042            };
30043  
30044            if (match[1]) {
30045              var attributes = parseAttributes(match[1]);
30046  
30047              if (attributes.URI) {
30048                event.uri = attributes.URI;
30049              }
30050  
30051              if (attributes.BYTERANGE) {
30052                var _attributes$BYTERANGE = attributes.BYTERANGE.split('@'),
30053                    length = _attributes$BYTERANGE[0],
30054                    offset = _attributes$BYTERANGE[1];
30055  
30056                event.byterange = {};
30057  
30058                if (length) {
30059                  event.byterange.length = parseInt(length, 10);
30060                }
30061  
30062                if (offset) {
30063                  event.byterange.offset = parseInt(offset, 10);
30064                }
30065              }
30066            }
30067  
30068            _this2.trigger('data', event);
30069  
30070            return;
30071          }
30072  
30073          match = /^#EXT-X-STREAM-INF:?(.*)$/.exec(newLine);
30074  
30075          if (match) {
30076            event = {
30077              type: 'tag',
30078              tagType: 'stream-inf'
30079            };
30080  
30081            if (match[1]) {
30082              event.attributes = parseAttributes(match[1]);
30083  
30084              if (event.attributes.RESOLUTION) {
30085                var split = event.attributes.RESOLUTION.split('x');
30086                var resolution = {};
30087  
30088                if (split[0]) {
30089                  resolution.width = parseInt(split[0], 10);
30090                }
30091  
30092                if (split[1]) {
30093                  resolution.height = parseInt(split[1], 10);
30094                }
30095  
30096                event.attributes.RESOLUTION = resolution;
30097              }
30098  
30099              if (event.attributes.BANDWIDTH) {
30100                event.attributes.BANDWIDTH = parseInt(event.attributes.BANDWIDTH, 10);
30101              }
30102  
30103              if (event.attributes['PROGRAM-ID']) {
30104                event.attributes['PROGRAM-ID'] = parseInt(event.attributes['PROGRAM-ID'], 10);
30105              }
30106            }
30107  
30108            _this2.trigger('data', event);
30109  
30110            return;
30111          }
30112  
30113          match = /^#EXT-X-MEDIA:?(.*)$/.exec(newLine);
30114  
30115          if (match) {
30116            event = {
30117              type: 'tag',
30118              tagType: 'media'
30119            };
30120  
30121            if (match[1]) {
30122              event.attributes = parseAttributes(match[1]);
30123            }
30124  
30125            _this2.trigger('data', event);
30126  
30127            return;
30128          }
30129  
30130          match = /^#EXT-X-ENDLIST/.exec(newLine);
30131  
30132          if (match) {
30133            _this2.trigger('data', {
30134              type: 'tag',
30135              tagType: 'endlist'
30136            });
30137  
30138            return;
30139          }
30140  
30141          match = /^#EXT-X-DISCONTINUITY/.exec(newLine);
30142  
30143          if (match) {
30144            _this2.trigger('data', {
30145              type: 'tag',
30146              tagType: 'discontinuity'
30147            });
30148  
30149            return;
30150          }
30151  
30152          match = /^#EXT-X-PROGRAM-DATE-TIME:?(.*)$/.exec(newLine);
30153  
30154          if (match) {
30155            event = {
30156              type: 'tag',
30157              tagType: 'program-date-time'
30158            };
30159  
30160            if (match[1]) {
30161              event.dateTimeString = match[1];
30162              event.dateTimeObject = new Date(match[1]);
30163            }
30164  
30165            _this2.trigger('data', event);
30166  
30167            return;
30168          }
30169  
30170          match = /^#EXT-X-KEY:?(.*)$/.exec(newLine);
30171  
30172          if (match) {
30173            event = {
30174              type: 'tag',
30175              tagType: 'key'
30176            };
30177  
30178            if (match[1]) {
30179              event.attributes = parseAttributes(match[1]); // parse the IV string into a Uint32Array
30180  
30181              if (event.attributes.IV) {
30182                if (event.attributes.IV.substring(0, 2).toLowerCase() === '0x') {
30183                  event.attributes.IV = event.attributes.IV.substring(2);
30184                }
30185  
30186                event.attributes.IV = event.attributes.IV.match(/.{8}/g);
30187                event.attributes.IV[0] = parseInt(event.attributes.IV[0], 16);
30188                event.attributes.IV[1] = parseInt(event.attributes.IV[1], 16);
30189                event.attributes.IV[2] = parseInt(event.attributes.IV[2], 16);
30190                event.attributes.IV[3] = parseInt(event.attributes.IV[3], 16);
30191                event.attributes.IV = new Uint32Array(event.attributes.IV);
30192              }
30193            }
30194  
30195            _this2.trigger('data', event);
30196  
30197            return;
30198          }
30199  
30200          match = /^#EXT-X-START:?(.*)$/.exec(newLine);
30201  
30202          if (match) {
30203            event = {
30204              type: 'tag',
30205              tagType: 'start'
30206            };
30207  
30208            if (match[1]) {
30209              event.attributes = parseAttributes(match[1]);
30210              event.attributes['TIME-OFFSET'] = parseFloat(event.attributes['TIME-OFFSET']);
30211              event.attributes.PRECISE = /YES/.test(event.attributes.PRECISE);
30212            }
30213  
30214            _this2.trigger('data', event);
30215  
30216            return;
30217          }
30218  
30219          match = /^#EXT-X-CUE-OUT-CONT:?(.*)?$/.exec(newLine);
30220  
30221          if (match) {
30222            event = {
30223              type: 'tag',
30224              tagType: 'cue-out-cont'
30225            };
30226  
30227            if (match[1]) {
30228              event.data = match[1];
30229            } else {
30230              event.data = '';
30231            }
30232  
30233            _this2.trigger('data', event);
30234  
30235            return;
30236          }
30237  
30238          match = /^#EXT-X-CUE-OUT:?(.*)?$/.exec(newLine);
30239  
30240          if (match) {
30241            event = {
30242              type: 'tag',
30243              tagType: 'cue-out'
30244            };
30245  
30246            if (match[1]) {
30247              event.data = match[1];
30248            } else {
30249              event.data = '';
30250            }
30251  
30252            _this2.trigger('data', event);
30253  
30254            return;
30255          }
30256  
30257          match = /^#EXT-X-CUE-IN:?(.*)?$/.exec(newLine);
30258  
30259          if (match) {
30260            event = {
30261              type: 'tag',
30262              tagType: 'cue-in'
30263            };
30264  
30265            if (match[1]) {
30266              event.data = match[1];
30267            } else {
30268              event.data = '';
30269            }
30270  
30271            _this2.trigger('data', event);
30272  
30273            return;
30274          } // unknown tag type
30275  
30276  
30277          _this2.trigger('data', {
30278            type: 'tag',
30279            data: newLine.slice(4)
30280          });
30281        });
30282      }
30283      /**
30284       * Add a parser for custom headers
30285       *
30286       * @param {Object}   options              a map of options for the added parser
30287       * @param {RegExp}   options.expression   a regular expression to match the custom header
30288       * @param {string}   options.customType   the custom type to register to the output
30289       * @param {Function} [options.dataParser] function to parse the line into an object
30290       * @param {boolean}  [options.segment]    should tag data be attached to the segment object
30291       */
30292      ;
30293  
30294      _proto.addParser = function addParser(_ref) {
30295        var _this3 = this;
30296  
30297        var expression = _ref.expression,
30298            customType = _ref.customType,
30299            dataParser = _ref.dataParser,
30300            segment = _ref.segment;
30301  
30302        if (typeof dataParser !== 'function') {
30303          dataParser = function dataParser(line) {
30304            return line;
30305          };
30306        }
30307  
30308        this.customParsers.push(function (line) {
30309          var match = expression.exec(line);
30310  
30311          if (match) {
30312            _this3.trigger('data', {
30313              type: 'custom',
30314              data: dataParser(line),
30315              customType: customType,
30316              segment: segment
30317            });
30318  
30319            return true;
30320          }
30321        });
30322      }
30323      /**
30324       * Add a custom header mapper
30325       *
30326       * @param {Object}   options
30327       * @param {RegExp}   options.expression   a regular expression to match the custom header
30328       * @param {Function} options.map          function to translate tag into a different tag
30329       */
30330      ;
30331  
30332      _proto.addTagMapper = function addTagMapper(_ref2) {
30333        var expression = _ref2.expression,
30334            map = _ref2.map;
30335  
30336        var mapFn = function mapFn(line) {
30337          if (expression.test(line)) {
30338            return map(line);
30339          }
30340  
30341          return line;
30342        };
30343  
30344        this.tagMappers.push(mapFn);
30345      };
30346  
30347      return ParseStream;
30348    }(Stream);
30349  
30350    function decodeB64ToUint8Array(b64Text) {
30351      var decodedString = window$3.atob(b64Text || '');
30352      var array = new Uint8Array(decodedString.length);
30353  
30354      for (var i = 0; i < decodedString.length; i++) {
30355        array[i] = decodedString.charCodeAt(i);
30356      }
30357  
30358      return array;
30359    }
30360    /**
30361     * A parser for M3U8 files. The current interpretation of the input is
30362     * exposed as a property `manifest` on parser objects. It's just two lines to
30363     * create and parse a manifest once you have the contents available as a string:
30364     *
30365     * ```js
30366     * var parser = new m3u8.Parser();
30367     * parser.push(xhr.responseText);
30368     * ```
30369     *
30370     * New input can later be applied to update the manifest object by calling
30371     * `push` again.
30372     *
30373     * The parser attempts to create a usable manifest object even if the
30374     * underlying input is somewhat nonsensical. It emits `info` and `warning`
30375     * events during the parse if it encounters input that seems invalid or
30376     * requires some property of the manifest object to be defaulted.
30377     *
30378     * @class Parser
30379     * @extends Stream
30380     */
30381  
30382  
30383    var Parser = /*#__PURE__*/function (_Stream) {
30384      _inheritsLoose$1(Parser, _Stream);
30385  
30386      function Parser() {
30387        var _this;
30388  
30389        _this = _Stream.call(this) || this;
30390        _this.lineStream = new LineStream();
30391        _this.parseStream = new ParseStream();
30392  
30393        _this.lineStream.pipe(_this.parseStream);
30394        /* eslint-disable consistent-this */
30395  
30396  
30397        var self = _assertThisInitialized$1(_this);
30398        /* eslint-enable consistent-this */
30399  
30400  
30401        var uris = [];
30402        var currentUri = {}; // if specified, the active EXT-X-MAP definition
30403  
30404        var currentMap; // if specified, the active decryption key
30405  
30406        var _key;
30407  
30408        var noop = function noop() {};
30409  
30410        var defaultMediaGroups = {
30411          'AUDIO': {},
30412          'VIDEO': {},
30413          'CLOSED-CAPTIONS': {},
30414          'SUBTITLES': {}
30415        }; // This is the Widevine UUID from DASH IF IOP. The same exact string is
30416        // used in MPDs with Widevine encrypted streams.
30417  
30418        var widevineUuid = 'urn:uuid:edef8ba9-79d6-4ace-a3c8-27dcd51d21ed'; // group segments into numbered timelines delineated by discontinuities
30419  
30420        var currentTimeline = 0; // the manifest is empty until the parse stream begins delivering data
30421  
30422        _this.manifest = {
30423          allowCache: true,
30424          discontinuityStarts: [],
30425          segments: []
30426        }; // update the manifest with the m3u8 entry from the parse stream
30427  
30428        _this.parseStream.on('data', function (entry) {
30429          var mediaGroup;
30430          var rendition;
30431          ({
30432            tag: function tag() {
30433              // switch based on the tag type
30434              (({
30435                'allow-cache': function allowCache() {
30436                  this.manifest.allowCache = entry.allowed;
30437  
30438                  if (!('allowed' in entry)) {
30439                    this.trigger('info', {
30440                      message: 'defaulting allowCache to YES'
30441                    });
30442                    this.manifest.allowCache = true;
30443                  }
30444                },
30445                byterange: function byterange() {
30446                  var byterange = {};
30447  
30448                  if ('length' in entry) {
30449                    currentUri.byterange = byterange;
30450                    byterange.length = entry.length;
30451  
30452                    if (!('offset' in entry)) {
30453                      this.trigger('info', {
30454                        message: 'defaulting offset to zero'
30455                      });
30456                      entry.offset = 0;
30457                    }
30458                  }
30459  
30460                  if ('offset' in entry) {
30461                    currentUri.byterange = byterange;
30462                    byterange.offset = entry.offset;
30463                  }
30464                },
30465                endlist: function endlist() {
30466                  this.manifest.endList = true;
30467                },
30468                inf: function inf() {
30469                  if (!('mediaSequence' in this.manifest)) {
30470                    this.manifest.mediaSequence = 0;
30471                    this.trigger('info', {
30472                      message: 'defaulting media sequence to zero'
30473                    });
30474                  }
30475  
30476                  if (!('discontinuitySequence' in this.manifest)) {
30477                    this.manifest.discontinuitySequence = 0;
30478                    this.trigger('info', {
30479                      message: 'defaulting discontinuity sequence to zero'
30480                    });
30481                  }
30482  
30483                  if (entry.duration > 0) {
30484                    currentUri.duration = entry.duration;
30485                  }
30486  
30487                  if (entry.duration === 0) {
30488                    currentUri.duration = 0.01;
30489                    this.trigger('info', {
30490                      message: 'updating zero segment duration to a small value'
30491                    });
30492                  }
30493  
30494                  this.manifest.segments = uris;
30495                },
30496                key: function key() {
30497                  if (!entry.attributes) {
30498                    this.trigger('warn', {
30499                      message: 'ignoring key declaration without attribute list'
30500                    });
30501                    return;
30502                  } // clear the active encryption key
30503  
30504  
30505                  if (entry.attributes.METHOD === 'NONE') {
30506                    _key = null;
30507                    return;
30508                  }
30509  
30510                  if (!entry.attributes.URI) {
30511                    this.trigger('warn', {
30512                      message: 'ignoring key declaration without URI'
30513                    });
30514                    return;
30515                  } // check if the content is encrypted for Widevine
30516                  // Widevine/HLS spec: https://storage.googleapis.com/wvdocs/Widevine_DRM_HLS.pdf
30517  
30518  
30519                  if (entry.attributes.KEYFORMAT === widevineUuid) {
30520                    var VALID_METHODS = ['SAMPLE-AES', 'SAMPLE-AES-CTR', 'SAMPLE-AES-CENC'];
30521  
30522                    if (VALID_METHODS.indexOf(entry.attributes.METHOD) === -1) {
30523                      this.trigger('warn', {
30524                        message: 'invalid key method provided for Widevine'
30525                      });
30526                      return;
30527                    }
30528  
30529                    if (entry.attributes.METHOD === 'SAMPLE-AES-CENC') {
30530                      this.trigger('warn', {
30531                        message: 'SAMPLE-AES-CENC is deprecated, please use SAMPLE-AES-CTR instead'
30532                      });
30533                    }
30534  
30535                    if (entry.attributes.URI.substring(0, 23) !== 'data:text/plain;base64,') {
30536                      this.trigger('warn', {
30537                        message: 'invalid key URI provided for Widevine'
30538                      });
30539                      return;
30540                    }
30541  
30542                    if (!(entry.attributes.KEYID && entry.attributes.KEYID.substring(0, 2) === '0x')) {
30543                      this.trigger('warn', {
30544                        message: 'invalid key ID provided for Widevine'
30545                      });
30546                      return;
30547                    } // if Widevine key attributes are valid, store them as `contentProtection`
30548                    // on the manifest to emulate Widevine tag structure in a DASH mpd
30549  
30550  
30551                    this.manifest.contentProtection = {
30552                      'com.widevine.alpha': {
30553                        attributes: {
30554                          schemeIdUri: entry.attributes.KEYFORMAT,
30555                          // remove '0x' from the key id string
30556                          keyId: entry.attributes.KEYID.substring(2)
30557                        },
30558                        // decode the base64-encoded PSSH box
30559                        pssh: decodeB64ToUint8Array(entry.attributes.URI.split(',')[1])
30560                      }
30561                    };
30562                    return;
30563                  }
30564  
30565                  if (!entry.attributes.METHOD) {
30566                    this.trigger('warn', {
30567                      message: 'defaulting key method to AES-128'
30568                    });
30569                  } // setup an encryption key for upcoming segments
30570  
30571  
30572                  _key = {
30573                    method: entry.attributes.METHOD || 'AES-128',
30574                    uri: entry.attributes.URI
30575                  };
30576  
30577                  if (typeof entry.attributes.IV !== 'undefined') {
30578                    _key.iv = entry.attributes.IV;
30579                  }
30580                },
30581                'media-sequence': function mediaSequence() {
30582                  if (!isFinite(entry.number)) {
30583                    this.trigger('warn', {
30584                      message: 'ignoring invalid media sequence: ' + entry.number
30585                    });
30586                    return;
30587                  }
30588  
30589                  this.manifest.mediaSequence = entry.number;
30590                },
30591                'discontinuity-sequence': function discontinuitySequence() {
30592                  if (!isFinite(entry.number)) {
30593                    this.trigger('warn', {
30594                      message: 'ignoring invalid discontinuity sequence: ' + entry.number
30595                    });
30596                    return;
30597                  }
30598  
30599                  this.manifest.discontinuitySequence = entry.number;
30600                  currentTimeline = entry.number;
30601                },
30602                'playlist-type': function playlistType() {
30603                  if (!/VOD|EVENT/.test(entry.playlistType)) {
30604                    this.trigger('warn', {
30605                      message: 'ignoring unknown playlist type: ' + entry.playlist
30606                    });
30607                    return;
30608                  }
30609  
30610                  this.manifest.playlistType = entry.playlistType;
30611                },
30612                map: function map() {
30613                  currentMap = {};
30614  
30615                  if (entry.uri) {
30616                    currentMap.uri = entry.uri;
30617                  }
30618  
30619                  if (entry.byterange) {
30620                    currentMap.byterange = entry.byterange;
30621                  }
30622                },
30623                'stream-inf': function streamInf() {
30624                  this.manifest.playlists = uris;
30625                  this.manifest.mediaGroups = this.manifest.mediaGroups || defaultMediaGroups;
30626  
30627                  if (!entry.attributes) {
30628                    this.trigger('warn', {
30629                      message: 'ignoring empty stream-inf attributes'
30630                    });
30631                    return;
30632                  }
30633  
30634                  if (!currentUri.attributes) {
30635                    currentUri.attributes = {};
30636                  }
30637  
30638                  _extends(currentUri.attributes, entry.attributes);
30639                },
30640                media: function media() {
30641                  this.manifest.mediaGroups = this.manifest.mediaGroups || defaultMediaGroups;
30642  
30643                  if (!(entry.attributes && entry.attributes.TYPE && entry.attributes['GROUP-ID'] && entry.attributes.NAME)) {
30644                    this.trigger('warn', {
30645                      message: 'ignoring incomplete or missing media group'
30646                    });
30647                    return;
30648                  } // find the media group, creating defaults as necessary
30649  
30650  
30651                  var mediaGroupType = this.manifest.mediaGroups[entry.attributes.TYPE];
30652                  mediaGroupType[entry.attributes['GROUP-ID']] = mediaGroupType[entry.attributes['GROUP-ID']] || {};
30653                  mediaGroup = mediaGroupType[entry.attributes['GROUP-ID']]; // collect the rendition metadata
30654  
30655                  rendition = {
30656                    "default": /yes/i.test(entry.attributes.DEFAULT)
30657                  };
30658  
30659                  if (rendition["default"]) {
30660                    rendition.autoselect = true;
30661                  } else {
30662                    rendition.autoselect = /yes/i.test(entry.attributes.AUTOSELECT);
30663                  }
30664  
30665                  if (entry.attributes.LANGUAGE) {
30666                    rendition.language = entry.attributes.LANGUAGE;
30667                  }
30668  
30669                  if (entry.attributes.URI) {
30670                    rendition.uri = entry.attributes.URI;
30671                  }
30672  
30673                  if (entry.attributes['INSTREAM-ID']) {
30674                    rendition.instreamId = entry.attributes['INSTREAM-ID'];
30675                  }
30676  
30677                  if (entry.attributes.CHARACTERISTICS) {
30678                    rendition.characteristics = entry.attributes.CHARACTERISTICS;
30679                  }
30680  
30681                  if (entry.attributes.FORCED) {
30682                    rendition.forced = /yes/i.test(entry.attributes.FORCED);
30683                  } // insert the new rendition
30684  
30685  
30686                  mediaGroup[entry.attributes.NAME] = rendition;
30687                },
30688                discontinuity: function discontinuity() {
30689                  currentTimeline += 1;
30690                  currentUri.discontinuity = true;
30691                  this.manifest.discontinuityStarts.push(uris.length);
30692                },
30693                'program-date-time': function programDateTime() {
30694                  if (typeof this.manifest.dateTimeString === 'undefined') {
30695                    // PROGRAM-DATE-TIME is a media-segment tag, but for backwards
30696                    // compatibility, we add the first occurence of the PROGRAM-DATE-TIME tag
30697                    // to the manifest object
30698                    // TODO: Consider removing this in future major version
30699                    this.manifest.dateTimeString = entry.dateTimeString;
30700                    this.manifest.dateTimeObject = entry.dateTimeObject;
30701                  }
30702  
30703                  currentUri.dateTimeString = entry.dateTimeString;
30704                  currentUri.dateTimeObject = entry.dateTimeObject;
30705                },
30706                targetduration: function targetduration() {
30707                  if (!isFinite(entry.duration) || entry.duration < 0) {
30708                    this.trigger('warn', {
30709                      message: 'ignoring invalid target duration: ' + entry.duration
30710                    });
30711                    return;
30712                  }
30713  
30714                  this.manifest.targetDuration = entry.duration;
30715                },
30716                totalduration: function totalduration() {
30717                  if (!isFinite(entry.duration) || entry.duration < 0) {
30718                    this.trigger('warn', {
30719                      message: 'ignoring invalid total duration: ' + entry.duration
30720                    });
30721                    return;
30722                  }
30723  
30724                  this.manifest.totalDuration = entry.duration;
30725                },
30726                start: function start() {
30727                  if (!entry.attributes || isNaN(entry.attributes['TIME-OFFSET'])) {
30728                    this.trigger('warn', {
30729                      message: 'ignoring start declaration without appropriate attribute list'
30730                    });
30731                    return;
30732                  }
30733  
30734                  this.manifest.start = {
30735                    timeOffset: entry.attributes['TIME-OFFSET'],
30736                    precise: entry.attributes.PRECISE
30737                  };
30738                },
30739                'cue-out': function cueOut() {
30740                  currentUri.cueOut = entry.data;
30741                },
30742                'cue-out-cont': function cueOutCont() {
30743                  currentUri.cueOutCont = entry.data;
30744                },
30745                'cue-in': function cueIn() {
30746                  currentUri.cueIn = entry.data;
30747                }
30748              })[entry.tagType] || noop).call(self);
30749            },
30750            uri: function uri() {
30751              currentUri.uri = entry.uri;
30752              uris.push(currentUri); // if no explicit duration was declared, use the target duration
30753  
30754              if (this.manifest.targetDuration && !('duration' in currentUri)) {
30755                this.trigger('warn', {
30756                  message: 'defaulting segment duration to the target duration'
30757                });
30758                currentUri.duration = this.manifest.targetDuration;
30759              } // annotate with encryption information, if necessary
30760  
30761  
30762              if (_key) {
30763                currentUri.key = _key;
30764              }
30765  
30766              currentUri.timeline = currentTimeline; // annotate with initialization segment information, if necessary
30767  
30768              if (currentMap) {
30769                currentUri.map = currentMap;
30770              } // prepare for the next URI
30771  
30772  
30773              currentUri = {};
30774            },
30775            comment: function comment() {// comments are not important for playback
30776            },
30777            custom: function custom() {
30778              // if this is segment-level data attach the output to the segment
30779              if (entry.segment) {
30780                currentUri.custom = currentUri.custom || {};
30781                currentUri.custom[entry.customType] = entry.data; // if this is manifest-level data attach to the top level manifest object
30782              } else {
30783                this.manifest.custom = this.manifest.custom || {};
30784                this.manifest.custom[entry.customType] = entry.data;
30785              }
30786            }
30787          })[entry.type].call(self);
30788        });
30789  
30790        return _this;
30791      }
30792      /**
30793       * Parse the input string and update the manifest object.
30794       *
30795       * @param {string} chunk a potentially incomplete portion of the manifest
30796       */
30797  
30798  
30799      var _proto = Parser.prototype;
30800  
30801      _proto.push = function push(chunk) {
30802        this.lineStream.push(chunk);
30803      }
30804      /**
30805       * Flush any remaining input. This can be handy if the last line of an M3U8
30806       * manifest did not contain a trailing newline but the file has been
30807       * completely received.
30808       */
30809      ;
30810  
30811      _proto.end = function end() {
30812        // flush any buffered input
30813        this.lineStream.push('\n');
30814      }
30815      /**
30816       * Add an additional parser for non-standard tags
30817       *
30818       * @param {Object}   options              a map of options for the added parser
30819       * @param {RegExp}   options.expression   a regular expression to match the custom header
30820       * @param {string}   options.type         the type to register to the output
30821       * @param {Function} [options.dataParser] function to parse the line into an object
30822       * @param {boolean}  [options.segment]    should tag data be attached to the segment object
30823       */
30824      ;
30825  
30826      _proto.addParser = function addParser(options) {
30827        this.parseStream.addParser(options);
30828      }
30829      /**
30830       * Add a custom header mapper
30831       *
30832       * @param {Object}   options
30833       * @param {RegExp}   options.expression   a regular expression to match the custom header
30834       * @param {Function} options.map          function to translate tag into a different tag
30835       */
30836      ;
30837  
30838      _proto.addTagMapper = function addTagMapper(options) {
30839        this.parseStream.addTagMapper(options);
30840      };
30841  
30842      return Parser;
30843    }(Stream);
30844  
30845    function _interopDefault(ex) {
30846      return ex && typeof ex === 'object' && 'default' in ex ? ex['default'] : ex;
30847    }
30848  
30849    var URLToolkit = _interopDefault(urlToolkit);
30850  
30851    var window$1 = _interopDefault(window$3);
30852  
30853    var resolveUrl = function resolveUrl(baseUrl, relativeUrl) {
30854      // return early if we don't need to resolve
30855      if (/^[a-z]+:/i.test(relativeUrl)) {
30856        return relativeUrl;
30857      } // if the base URL is relative then combine with the current location
30858  
30859  
30860      if (!/\/\//i.test(baseUrl)) {
30861        baseUrl = URLToolkit.buildAbsoluteURL(window$1.location && window$1.location.href || '', baseUrl);
30862      }
30863  
30864      return URLToolkit.buildAbsoluteURL(baseUrl, relativeUrl);
30865    };
30866  
30867    var resolveUrl_1 = resolveUrl;
30868  
30869    function _interopDefault$1(ex) {
30870      return ex && typeof ex === 'object' && 'default' in ex ? ex['default'] : ex;
30871    }
30872  
30873    var window$2 = _interopDefault$1(window$3);
30874  
30875    var atob = function atob(s) {
30876      return window$2.atob ? window$2.atob(s) : Buffer.from(s, 'base64').toString('binary');
30877    };
30878  
30879    function decodeB64ToUint8Array$1(b64Text) {
30880      var decodedString = atob(b64Text);
30881      var array = new Uint8Array(decodedString.length);
30882  
30883      for (var i = 0; i < decodedString.length; i++) {
30884        array[i] = decodedString.charCodeAt(i);
30885      }
30886  
30887      return array;
30888    }
30889  
30890    var decodeB64ToUint8Array_1 = decodeB64ToUint8Array$1;
30891  
30892    //[4]   	NameStartChar	   ::=   	":" | [A-Z] | "_" | [a-z] | [#xC0-#xD6
vendor: 5,975 bytes, lines 30892-31041
30892] | [#xD8-#xF6] | [#xF8-#x2FF] | [#x370-#x37D] | [#x37F-#x1FFF] | [#x200C-#x200D] | [#x2070-#x218F] | [#x2C00-#x2FEF] | [#x3001-#xD7FF] | [#xF900-#xFDCF] | [#xFDF0-#xFFFD] | [#x10000-#xEFFFF]
30893    //[4a]   	NameChar	   ::=   	NameStartChar | "-" | "." | [0-9] | #xB7 | [#x0300-#x036F] | [#x203F-#x2040]
30894    //[5]   	Name	   ::=   	NameStartChar (NameChar)*
30895    var nameStartChar = /[A-Z_a-z\xC0-\xD6\xD8-\xF6\u00F8-\u02FF\u0370-\u037D\u037F-\u1FFF\u200C-\u200D\u2070-\u218F\u2C00-\u2FEF\u3001-\uD7FF\uF900-\uFDCF\uFDF0-\uFFFD]/; //\u10000-\uEFFFF
30896  
30897    var nameChar = new RegExp("[\\-\\.0-9" + nameStartChar.source.slice(1, -1) + "\\u00B7\\u0300-\\u036F\\u203F-\\u2040]");
30898    var tagNamePattern = new RegExp('^' + nameStartChar.source + nameChar.source + '*(?:\:' + nameStartChar.source + nameChar.source + '*)?$'); //var tagNamePattern = /^[a-zA-Z_][\w\-\.]*(?:\:[a-zA-Z_][\w\-\.]*)?$/
30899    //var handlers = 'resolveEntity,getExternalSubset,characters,endDocument,endElement,endPrefixMapping,ignorableWhitespace,processingInstruction,setDocumentLocator,skippedEntity,startDocument,startElement,startPrefixMapping,notationDecl,unparsedEntityDecl,error,fatalError,warning,attributeDecl,elementDecl,externalEntityDecl,internalEntityDecl,comment,endCDATA,endDTD,endEntity,startCDATA,startDTD,startEntity'.split(',')
30900    //S_TAG,	S_ATTR,	S_EQ,	S_ATTR_NOQUOT_VALUE
30901    //S_ATTR_SPACE,	S_ATTR_END,	S_TAG_SPACE, S_TAG_CLOSE
30902  
30903    var S_TAG = 0; //tag name offerring
30904  
30905    var S_ATTR = 1; //attr name offerring 
30906  
30907    var S_ATTR_SPACE = 2; //attr name end and space offer
30908  
30909    var S_EQ = 3; //=space?
30910  
30911    var S_ATTR_NOQUOT_VALUE = 4; //attr value(no quot value only)
30912  
30913    var S_ATTR_END = 5; //attr value end and no space(quot end)
30914  
30915    var S_TAG_SPACE = 6; //(attr value end || tag end ) && (space offer)
30916  
30917    var S_TAG_CLOSE = 7; //closed el<el />
30918  
30919    function XMLReader() {}
30920  
30921    XMLReader.prototype = {
30922      parse: function parse(source, defaultNSMap, entityMap) {
30923        var domBuilder = this.domBuilder;
30924        domBuilder.startDocument();
30925  
30926        _copy(defaultNSMap, defaultNSMap = {});
30927  
30928        _parse(source, defaultNSMap, entityMap, domBuilder, this.errorHandler);
30929  
30930        domBuilder.endDocument();
30931      }
30932    };
30933  
30934    function _parse(source, defaultNSMapCopy, entityMap, domBuilder, errorHandler) {
30935      function fixedFromCharCode(code) {
30936        // String.prototype.fromCharCode does not supports
30937        // > 2 bytes unicode chars directly
30938        if (code > 0xffff) {
30939          code -= 0x10000;
30940          var surrogate1 = 0xd800 + (code >> 10),
30941              surrogate2 = 0xdc00 + (code & 0x3ff);
30942          return String.fromCharCode(surrogate1, surrogate2);
30943        } else {
30944          return String.fromCharCode(code);
30945        }
30946      }
30947  
30948      function entityReplacer(a) {
30949        var k = a.slice(1, -1);
30950  
30951        if (k in entityMap) {
30952          return entityMap[k];
30953        } else if (k.charAt(0) === '#') {
30954          return fixedFromCharCode(parseInt(k.substr(1).replace('x', '0x')));
30955        } else {
30956          errorHandler.error('entity not found:' + a);
30957          return a;
30958        }
30959      }
30960  
30961      function appendText(end) {
30962        //has some bugs
30963        if (end > start) {
30964          var xt = source.substring(start, end).replace(/&#?\w+;/g, entityReplacer);
30965          locator && position(start);
30966          domBuilder.characters(xt, 0, end - start);
30967          start = end;
30968        }
30969      }
30970  
30971      function position(p, m) {
30972        while (p >= lineEnd && (m = linePattern.exec(source))) {
30973          lineStart = m.index;
30974          lineEnd = lineStart + m[0].length;
30975          locator.lineNumber++; //console.log('line++:',locator,startPos,endPos)
30976        }
30977  
30978        locator.columnNumber = p - lineStart + 1;
30979      }
30980  
30981      var lineStart = 0;
30982      var lineEnd = 0;
30983      var linePattern = /.*(?:\r\n?|\n)|.*$/g;
30984      var locator = domBuilder.locator;
30985      var parseStack = [{
30986        currentNSMap: defaultNSMapCopy
30987      }];
30988      var closeMap = {};
30989      var start = 0;
30990  
30991      while (true) {
30992        try {
30993          var tagStart = source.indexOf('<', start);
30994  
30995          if (tagStart < 0) {
30996            if (!source.substr(start).match(/^\s*$/)) {
30997              var doc = domBuilder.doc;
30998              var text = doc.createTextNode(source.substr(start));
30999              doc.appendChild(text);
31000              domBuilder.currentElement = text;
31001            }
31002  
31003            return;
31004          }
31005  
31006          if (tagStart > start) {
31007            appendText(tagStart);
31008          }
31009  
31010          switch (source.charAt(tagStart + 1)) {
31011            case '/':
31012              var end = source.indexOf('>', tagStart + 3);
31013              var tagName = source.substring(tagStart + 2, end);
31014              var config = parseStack.pop();
31015  
31016              if (end < 0) {
31017                tagName = source.substring(tagStart + 2).replace(/[\s<].*/, ''); //console.error('#@@@@@@'+tagName)
31018  
31019                errorHandler.error("end tag name: " + tagName + ' is not complete:' + config.tagName);
31020                end = tagStart + 1 + tagName.length;
31021              } else if (tagName.match(/\s</)) {
31022                tagName = tagName.replace(/[\s<].*/, '');
31023                errorHandler.error("end tag name: " + tagName + ' maybe not complete');
31024                end = tagStart + 1 + tagName.length;
31025              } //console.error(parseStack.length,parseStack)
31026              //console.error(config);
31027  
31028  
31029              var localNSMap = config.localNSMap;
31030              var endMatch = config.tagName == tagName;
31031              var endIgnoreCaseMach = endMatch || config.tagName && config.tagName.toLowerCase() == tagName.toLowerCase();
31032  
31033              if (endIgnoreCaseMach) {
31034                domBuilder.endElement(config.uri, config.localName, tagName);
31035  
31036                if (localNSMap) {
31037                  for (var prefix in localNSMap) {
31038                    domBuilder.endPrefixMapping(prefix);
31039                  }
31040                }
31041  
vendor: 14,537 bytes, lines 31042-31510
31042                if (!endMatch) {
31043                  errorHandler.fatalError("end tag name: " + tagName + ' is not match the current start tagName:' + config.tagName);
31044                }
31045              } else {
31046                parseStack.push(config);
31047              }
31048  
31049              end++;
31050              break;
31051            // end elment
31052  
31053            case '?':
31054              // <?...?>
31055              locator && position(tagStart);
31056              end = parseInstruction(source, tagStart, domBuilder);
31057              break;
31058  
31059            case '!':
31060              // <!doctype,<![CDATA,<!--
31061              locator && position(tagStart);
31062              end = parseDCC(source, tagStart, domBuilder, errorHandler);
31063              break;
31064  
31065            default:
31066              locator && position(tagStart);
31067              var el = new ElementAttributes();
31068              var currentNSMap = parseStack[parseStack.length - 1].currentNSMap; //elStartEnd
31069  
31070              var end = parseElementStartPart(source, tagStart, el, currentNSMap, entityReplacer, errorHandler);
31071              var len = el.length;
31072  
31073              if (!el.closed && fixSelfClosed(source, end, el.tagName, closeMap)) {
31074                el.closed = true;
31075  
31076                if (!entityMap.nbsp) {
31077                  errorHandler.warning('unclosed xml attribute');
31078                }
31079              }
31080  
31081              if (locator && len) {
31082                var locator2 = copyLocator(locator, {}); //try{//attribute position fixed
31083  
31084                for (var i = 0; i < len; i++) {
31085                  var a = el[i];
31086                  position(a.offset);
31087                  a.locator = copyLocator(locator, {});
31088                } //}catch(e){console.error('@@@@@'+e)}
31089  
31090  
31091                domBuilder.locator = locator2;
31092  
31093                if (appendElement(el, domBuilder, currentNSMap)) {
31094                  parseStack.push(el);
31095                }
31096  
31097                domBuilder.locator = locator;
31098              } else {
31099                if (appendElement(el, domBuilder, currentNSMap)) {
31100                  parseStack.push(el);
31101                }
31102              }
31103  
31104              if (el.uri === 'http://www.w3.org/1999/xhtml' && !el.closed) {
31105                end = parseHtmlSpecialContent(source, end, el.tagName, entityReplacer, domBuilder);
31106              } else {
31107                end++;
31108              }
31109  
31110          }
31111        } catch (e) {
31112          errorHandler.error('element parse error: ' + e); //errorHandler.error('element parse error: '+e);
31113  
31114          end = -1; //throw e;
31115        }
31116  
31117        if (end > start) {
31118          start = end;
31119        } else {
31120          //TODO: 这里有可能sax回退,有位置错误风险
31121          appendText(Math.max(tagStart, start) + 1);
31122        }
31123      }
31124    }
31125  
31126    function copyLocator(f, t) {
31127      t.lineNumber = f.lineNumber;
31128      t.columnNumber = f.columnNumber;
31129      return t;
31130    }
31131    /**
31132     * @see #appendElement(source,elStartEnd,el,selfClosed,entityReplacer,domBuilder,parseStack);
31133     * @return end of the elementStartPart(end of elementEndPart for selfClosed el)
31134     */
31135  
31136  
31137    function parseElementStartPart(source, start, el, currentNSMap, entityReplacer, errorHandler) {
31138      var attrName;
31139      var value;
31140      var p = ++start;
31141      var s = S_TAG; //status
31142  
31143      while (true) {
31144        var c = source.charAt(p);
31145  
31146        switch (c) {
31147          case '=':
31148            if (s === S_ATTR) {
31149              //attrName
31150              attrName = source.slice(start, p);
31151              s = S_EQ;
31152            } else if (s === S_ATTR_SPACE) {
31153              s = S_EQ;
31154            } else {
31155              //fatalError: equal must after attrName or space after attrName
31156              throw new Error('attribute equal must after attrName');
31157            }
31158  
31159            break;
31160  
31161          case '\'':
31162          case '"':
31163            if (s === S_EQ || s === S_ATTR //|| s == S_ATTR_SPACE
31164            ) {
31165                //equal
31166                if (s === S_ATTR) {
31167                  errorHandler.warning('attribute value must after "="');
31168                  attrName = source.slice(start, p);
31169                }
31170  
31171                start = p + 1;
31172                p = source.indexOf(c, start);
31173  
31174                if (p > 0) {
31175                  value = source.slice(start, p).replace(/&#?\w+;/g, entityReplacer);
31176                  el.add(attrName, value, start - 1);
31177                  s = S_ATTR_END;
31178                } else {
31179                  //fatalError: no end quot match
31180                  throw new Error('attribute value no end \'' + c + '\' match');
31181                }
31182              } else if (s == S_ATTR_NOQUOT_VALUE) {
31183              value = source.slice(start, p).replace(/&#?\w+;/g, entityReplacer); //console.log(attrName,value,start,p)
31184  
31185              el.add(attrName, value, start); //console.dir(el)
31186  
31187              errorHandler.warning('attribute "' + attrName + '" missed start quot(' + c + ')!!');
31188              start = p + 1;
31189              s = S_ATTR_END;
31190            } else {
31191              //fatalError: no equal before
31192              throw new Error('attribute value must after "="');
31193            }
31194  
31195            break;
31196  
31197          case '/':
31198            switch (s) {
31199              case S_TAG:
31200                el.setTagName(source.slice(start, p));
31201  
31202              case S_ATTR_END:
31203              case S_TAG_SPACE:
31204              case S_TAG_CLOSE:
31205                s = S_TAG_CLOSE;
31206                el.closed = true;
31207  
31208              case S_ATTR_NOQUOT_VALUE:
31209              case S_ATTR:
31210              case S_ATTR_SPACE:
31211                break;
31212              //case S_EQ:
31213  
31214              default:
31215                throw new Error("attribute invalid close char('/')");
31216            }
31217  
31218            break;
31219  
31220          case '':
31221            //end document
31222            //throw new Error('unexpected end of input')
31223            errorHandler.error('unexpected end of input');
31224  
31225            if (s == S_TAG) {
31226              el.setTagName(source.slice(start, p));
31227            }
31228  
31229            return p;
31230  
31231          case '>':
31232            switch (s) {
31233              case S_TAG:
31234                el.setTagName(source.slice(start, p));
31235  
31236              case S_ATTR_END:
31237              case S_TAG_SPACE:
31238              case S_TAG_CLOSE:
31239                break;
31240              //normal
31241  
31242              case S_ATTR_NOQUOT_VALUE: //Compatible state
31243  
31244              case S_ATTR:
31245                value = source.slice(start, p);
31246  
31247                if (value.slice(-1) === '/') {
31248                  el.closed = true;
31249                  value = value.slice(0, -1);
31250                }
31251  
31252              case S_ATTR_SPACE:
31253                if (s === S_ATTR_SPACE) {
31254                  value = attrName;
31255                }
31256  
31257                if (s == S_ATTR_NOQUOT_VALUE) {
31258                  errorHandler.warning('attribute "' + value + '" missed quot(")!!');
31259                  el.add(attrName, value.replace(/&#?\w+;/g, entityReplacer), start);
31260                } else {
31261                  if (currentNSMap[''] !== 'http://www.w3.org/1999/xhtml' || !value.match(/^(?:disabled|checked|selected)$/i)) {
31262                    errorHandler.warning('attribute "' + value + '" missed value!! "' + value + '" instead!!');
31263                  }
31264  
31265                  el.add(value, value, start);
31266                }
31267  
31268                break;
31269  
31270              case S_EQ:
31271                throw new Error('attribute value missed!!');
31272            } //			console.log(tagName,tagNamePattern,tagNamePattern.test(tagName))
31273  
31274  
31275            return p;
31276  
31277          /*xml space '\x20' | #x9 | #xD | #xA; */
31278  
31279          case "\x80":
31280            c = ' ';
31281  
31282          default:
31283            if (c <= ' ') {
31284              //space
31285              switch (s) {
31286                case S_TAG:
31287                  el.setTagName(source.slice(start, p)); //tagName
31288  
31289                  s = S_TAG_SPACE;
31290                  break;
31291  
31292                case S_ATTR:
31293                  attrName = source.slice(start, p);
31294                  s = S_ATTR_SPACE;
31295                  break;
31296  
31297                case S_ATTR_NOQUOT_VALUE:
31298                  var value = source.slice(start, p).replace(/&#?\w+;/g, entityReplacer);
31299                  errorHandler.warning('attribute "' + value + '" missed quot(")!!');
31300                  el.add(attrName, value, start);
31301  
31302                case S_ATTR_END:
31303                  s = S_TAG_SPACE;
31304                  break;
31305                //case S_TAG_SPACE:
31306                //case S_EQ:
31307                //case S_ATTR_SPACE:
31308                //	void();break;
31309                //case S_TAG_CLOSE:
31310                //ignore warning
31311              }
31312            } else {
31313              //not space
31314              //S_TAG,	S_ATTR,	S_EQ,	S_ATTR_NOQUOT_VALUE
31315              //S_ATTR_SPACE,	S_ATTR_END,	S_TAG_SPACE, S_TAG_CLOSE
31316              switch (s) {
31317                //case S_TAG:void();break;
31318                //case S_ATTR:void();break;
31319                //case S_ATTR_NOQUOT_VALUE:void();break;
31320                case S_ATTR_SPACE:
31321                  var tagName = el.tagName;
31322  
31323                  if (currentNSMap[''] !== 'http://www.w3.org/1999/xhtml' || !attrName.match(/^(?:disabled|checked|selected)$/i)) {
31324                    errorHandler.warning('attribute "' + attrName + '" missed value!! "' + attrName + '" instead2!!');
31325                  }
31326  
31327                  el.add(attrName, attrName, start);
31328                  start = p;
31329                  s = S_ATTR;
31330                  break;
31331  
31332                case S_ATTR_END:
31333                  errorHandler.warning('attribute space is required"' + attrName + '"!!');
31334  
31335                case S_TAG_SPACE:
31336                  s = S_ATTR;
31337                  start = p;
31338                  break;
31339  
31340                case S_EQ:
31341                  s = S_ATTR_NOQUOT_VALUE;
31342                  start = p;
31343                  break;
31344  
31345                case S_TAG_CLOSE:
31346                  throw new Error("elements closed character '/' and '>' must be connected to");
31347              }
31348            }
31349  
31350        } //end outer switch
31351        //console.log('p++',p)
31352  
31353  
31354        p++;
31355      }
31356    }
31357    /**
31358     * @return true if has new namespace define
31359     */
31360  
31361  
31362    function appendElement(el, domBuilder, currentNSMap) {
31363      var tagName = el.tagName;
31364      var localNSMap = null; //var currentNSMap = parseStack[parseStack.length-1].currentNSMap;
31365  
31366      var i = el.length;
31367  
31368      while (i--) {
31369        var a = el[i];
31370        var qName = a.qName;
31371        var value = a.value;
31372        var nsp = qName.indexOf(':');
31373  
31374        if (nsp > 0) {
31375          var prefix = a.prefix = qName.slice(0, nsp);
31376          var localName = qName.slice(nsp + 1);
31377          var nsPrefix = prefix === 'xmlns' && localName;
31378        } else {
31379          localName = qName;
31380          prefix = null;
31381          nsPrefix = qName === 'xmlns' && '';
31382        } //can not set prefix,because prefix !== ''
31383  
31384  
31385        a.localName = localName; //prefix == null for no ns prefix attribute 
31386  
31387        if (nsPrefix !== false) {
31388          //hack!!
31389          if (localNSMap == null) {
31390            localNSMap = {}; //console.log(currentNSMap,0)
31391  
31392            _copy(currentNSMap, currentNSMap = {}); //console.log(currentNSMap,1)
31393  
31394          }
31395  
31396          currentNSMap[nsPrefix] = localNSMap[nsPrefix] = value;
31397          a.uri = 'http://www.w3.org/2000/xmlns/';
31398          domBuilder.startPrefixMapping(nsPrefix, value);
31399        }
31400      }
31401  
31402      var i = el.length;
31403  
31404      while (i--) {
31405        a = el[i];
31406        var prefix = a.prefix;
31407  
31408        if (prefix) {
31409          //no prefix attribute has no namespace
31410          if (prefix === 'xml') {
31411            a.uri = 'http://www.w3.org/XML/1998/namespace';
31412          }
31413  
31414          if (prefix !== 'xmlns') {
31415            a.uri = currentNSMap[prefix || '']; //{console.log('###'+a.qName,domBuilder.locator.systemId+'',currentNSMap,a.uri)}
31416          }
31417        }
31418      }
31419  
31420      var nsp = tagName.indexOf(':');
31421  
31422      if (nsp > 0) {
31423        prefix = el.prefix = tagName.slice(0, nsp);
31424        localName = el.localName = tagName.slice(nsp + 1);
31425      } else {
31426        prefix = null; //important!!
31427  
31428        localName = el.localName = tagName;
31429      } //no prefix element has default namespace
31430  
31431  
31432      var ns = el.uri = currentNSMap[prefix || ''];
31433      domBuilder.startElement(ns, localName, tagName, el); //endPrefixMapping and startPrefixMapping have not any help for dom builder
31434      //localNSMap = null
31435  
31436      if (el.closed) {
31437        domBuilder.endElement(ns, localName, tagName);
31438  
31439        if (localNSMap) {
31440          for (prefix in localNSMap) {
31441            domBuilder.endPrefixMapping(prefix);
31442          }
31443        }
31444      } else {
31445        el.currentNSMap = currentNSMap;
31446        el.localNSMap = localNSMap; //parseStack.push(el);
31447  
31448        return true;
31449      }
31450    }
31451  
31452    function parseHtmlSpecialContent(source, elStartEnd, tagName, entityReplacer, domBuilder) {
31453      if (/^(?:script|textarea)$/i.test(tagName)) {
31454        var elEndStart = source.indexOf('</' + tagName + '>', elStartEnd);
31455        var text = source.substring(elStartEnd + 1, elEndStart);
31456  
31457        if (/[&<]/.test(text)) {
31458          if (/^script$/i.test(tagName)) {
31459            //if(!/\]\]>/.test(text)){
31460            //lexHandler.startCDATA();
31461            domBuilder.characters(text, 0, text.length); //lexHandler.endCDATA();
31462  
31463            return elEndStart; //}
31464          } //}else{//text area
31465  
31466  
31467          text = text.replace(/&#?\w+;/g, entityReplacer);
31468          domBuilder.characters(text, 0, text.length);
31469          return elEndStart; //}
31470        }
31471      }
31472  
31473      return elStartEnd + 1;
31474    }
31475  
31476    function fixSelfClosed(source, elStartEnd, tagName, closeMap) {
31477      //if(tagName in closeMap){
31478      var pos = closeMap[tagName];
31479  
31480      if (pos == null) {
31481        //console.log(tagName)
31482        pos = source.lastIndexOf('</' + tagName + '>');
31483  
31484        if (pos < elStartEnd) {
31485          //忘记闭合
31486          pos = source.lastIndexOf('</' + tagName);
31487        }
31488  
31489        closeMap[tagName] = pos;
31490      }
31491  
31492      return pos < elStartEnd; //} 
31493    }
31494  
31495    function _copy(source, target) {
31496      for (var n in source) {
31497        target[n] = source[n];
31498      }
31499    }
31500  
31501    function parseDCC(source, start, domBuilder, errorHandler) {
31502      //sure start with '<!'
31503      var next = source.charAt(start + 2);
31504  
31505      switch (next) {
31506        case '-':
31507          if (source.charAt(start + 3) === '-') {
31508            var end = source.indexOf('-->', start + 4); //append comment source.substring(4,end)//<!--
31509  
31510            if (end > start) {
31511              domBuilder.comment(source, start + 4, end - start - 4);
31512              return end + 3;
31513            } else {
31514              errorHandler.error("Unclosed comment");
31515              return -1;
31516            }
31517          } else {
31518            //error
31519            return -1;
31520          }
31521  
31522        default:
31523          if (source.substr(start + 3, 6) == 'CDATA[') {
31524            var end = source.indexOf(']]>', start + 9);
31525            domBuilder.startCDATA();
31526            domBuilder.characters(source, start + 9, end - start - 9);
31527            domBuilder.endCDATA();
31528            return end + 3;
31529          } //<!DOCTYPE
31530          //startDTD(java.lang.String name, java.lang.String publicId, java.lang.String systemId) 
31531  
31532  
31533          var matchs = split(source, start);
31534          var len = matchs.length;
31535  
31536          if (len > 1 && /!doctype/i.test(matchs[0][0])) {
31537            var name = matchs[1][0];
31538            var pubid = len > 3 && /^public$/i.test(matchs[2][0]) && matchs[3][0];
31539            var sysid = len > 4 && matchs[4][0];
31540            var lastMatch = matchs[len - 1];
31541            domBuilder.startDTD(name, pubid && pubid.replace(/^(['"])(.*?)\1$/, '$2'), sysid && sysid.replace(/^(['"])(.*?)\1$/, '$2'));
31542            domBuilder.endDTD();
31543            return lastMatch.index + lastMatch[0].length;
31544          }
31545  
31546      }
31547  
31548      return -1;
31549    }
31550  
31551    function parseInstruction(source, start, domBuilder) {
31552      var end = source.indexOf('?>', start);
31553  
31554      if (end) {
31555        var match = source.substring(start, end).match(/^<\?(\S*)\s*([\s\S]*?)\s*$/);
31556  
31557        if (match) {
31558          var len = match[0].length;
31559          domBuilder.processingInstruction(match[1], match[2]);
31560          return end + 2;
31561        } else {
31562          //error
31563          return -1;
31564        }
31565      }
31566  
31567      return -1;
31568    }
31569    /**
31570     * @param source
31571     */
31572  
31573  
31574    function ElementAttributes(source) {}
31575  
31576    ElementAttributes.prototype = {
31577      setTagName: function setTagName(tagName) {
31578        if (!tagNamePattern.test(tagName)) {
31579          throw new Error('invalid tagName:' + tagName);
31580        }
31581  
31582        this.tagName = tagName;
31583      },
31584      add: function add(qName, value, offset) {
31585        if (!tagNamePattern.test(qName)) {
31586          throw new Error('invalid attribute:' + qName);
31587        }
31588  
31589        this[this.length++] = {
31590          qName: qName,
31591          value: value,
31592          offset: offset
31593        };
31594      },
31595      length: 0,
31596      getLocalName: function getLocalName(i) {
31597        return this[i].localName;
31598      },
31599      getLocator: function getLocator(i) {
31600        return this[i].locator;
31601      },
31602      getQName: function getQName(i) {
31603        return this[i].qName;
31604      },
31605      getURI: function getURI(i) {
31606        return this[i].uri;
31607      },
31608      getValue: function getValue(i) {
31609        return this[i].value;
31610      } //	,getIndex:function(uri, localName)){
31611      //		if(localName){
31612      //			
31613      //		}else{
31614      //			var qName = uri
31615      //		}
31616      //	},
31617      //	getValue:function(){return this.getValue(this.getIndex.apply(this,arguments))},
31618      //	getType:function(uri,localName){}
31619      //	getType:function(i){},
31620  
31621    };
31622  
31623    function _set_proto_(thiz, parent) {
31624      thiz.__proto__ = parent;
31625      return thiz;
31626    }
31627  
31628    if (!(_set_proto_({}, _set_proto_.prototype) instanceof _set_proto_)) {
31629      _set_proto_ = function _set_proto_(thiz, parent) {
31630        function p() {}
31631        p.prototype = parent;
31632        p = new p();
31633  
31634        for (parent in thiz) {
31635          p[parent] = thiz[parent];
31636        }
31637  
31638        return p;
31639      };
31640    }
31641  
31642    function split(source, start) {
31643      var match;
31644      var buf = [];
31645      var reg = /'[^']+'|"[^"]+"|[^\s<>\/=]+=?|(\/?\s*>|<)/g;
31646      reg.lastIndex = start;
31647      reg.exec(source); //skip <
31648  
31649      while (match = reg.exec(source)) {
31650        buf.push(match);
31651        if (match[1]) return buf;
31652      }
31653    }
31654  
31655    var XMLReader_1 = XMLReader;
31656    var sax = {
31657      XMLReader: XMLReader_1
31658    };
31659  
31660    /*
31661     * DOM Level 2
31662     * Object DOMException
31663     * @see http://www.w3.org/TR/REC-DOM-Level-1/ecma-script-language-binding.html
31664     * @see http://www.w3.org/TR/2000/REC-DOM-Level-2-Core-20001113/ecma-script-binding.html
31665     */
31666    function copy(src, dest) {
31667      for (var p in src) {
31668        dest[p] = src[p];
31669      }
31670    }
31671    /**
31672    ^\w+\.prototype\.([_\w]+)\s*=\s*((?:.*\{\s*?[\r\n][\s\S]*?^})|\S.*?(?=[;\r\n]));?
31673    ^\w+\.prototype\.([_\w]+)\s*=\s*(\S.*?(?=[;\r\n]));?
31674     */
31675  
31676  
31677    function _extends$1(Class, Super) {
31678      var pt = Class.prototype;
31679  
31680      if (Object.create) {
31681        var ppt = Object.create(Super.prototype);
31682        pt.__proto__ = ppt;
31683      }
31684  
31685      if (!(pt instanceof Super)) {
31686        var t = function t() {};
31687        t.prototype = Super.prototype;
31688        t = new t();
31689        copy(pt, t);
31690        Class.prototype = pt = t;
31691      }
31692  
31693      if (pt.constructor != Class) {
31694        if (typeof Class != 'function') {
31695          console.error("unknow Class:" + Class);
31696        }
31697  
31698        pt.constructor = Class;
31699      }
31700    }
31701  
31702    var htmlns = 'http://www.w3.org/1999/xhtml'; // Node Types
31703  
31704    var NodeType = {};
vendor: 7,802 bytes, lines 31705-31915
31705    var ELEMENT_NODE = NodeType.ELEMENT_NODE = 1;
31706    var ATTRIBUTE_NODE = NodeType.ATTRIBUTE_NODE = 2;
31707    var TEXT_NODE = NodeType.TEXT_NODE = 3;
31708    var CDATA_SECTION_NODE = NodeType.CDATA_SECTION_NODE = 4;
31709    var ENTITY_REFERENCE_NODE = NodeType.ENTITY_REFERENCE_NODE = 5;
31710    var ENTITY_NODE = NodeType.ENTITY_NODE = 6;
31711    var PROCESSING_INSTRUCTION_NODE = NodeType.PROCESSING_INSTRUCTION_NODE = 7;
31712    var COMMENT_NODE = NodeType.COMMENT_NODE = 8;
31713    var DOCUMENT_NODE = NodeType.DOCUMENT_NODE = 9;
31714    var DOCUMENT_TYPE_NODE = NodeType.DOCUMENT_TYPE_NODE = 10;
31715    var DOCUMENT_FRAGMENT_NODE = NodeType.DOCUMENT_FRAGMENT_NODE = 11;
31716    var NOTATION_NODE = NodeType.NOTATION_NODE = 12; // ExceptionCode
31717  
31718    var ExceptionCode = {};
31719    var ExceptionMessage = {};
31720    var INDEX_SIZE_ERR = ExceptionCode.INDEX_SIZE_ERR = (ExceptionMessage[1] = "Index size error", 1);
31721    var DOMSTRING_SIZE_ERR = ExceptionCode.DOMSTRING_SIZE_ERR = (ExceptionMessage[2] = "DOMString size error", 2);
31722    var HIERARCHY_REQUEST_ERR = ExceptionCode.HIERARCHY_REQUEST_ERR = (ExceptionMessage[3] = "Hierarchy request error", 3);
31723    var WRONG_DOCUMENT_ERR = ExceptionCode.WRONG_DOCUMENT_ERR = (ExceptionMessage[4] = "Wrong document", 4);
31724    var INVALID_CHARACTER_ERR = ExceptionCode.INVALID_CHARACTER_ERR = (ExceptionMessage[5] = "Invalid character", 5);
31725    var NO_DATA_ALLOWED_ERR = ExceptionCode.NO_DATA_ALLOWED_ERR = (ExceptionMessage[6] = "No data allowed", 6);
31726    var NO_MODIFICATION_ALLOWED_ERR = ExceptionCode.NO_MODIFICATION_ALLOWED_ERR = (ExceptionMessage[7] = "No modification allowed", 7);
31727    var NOT_FOUND_ERR = ExceptionCode.NOT_FOUND_ERR = (ExceptionMessage[8] = "Not found", 8);
31728    var NOT_SUPPORTED_ERR = ExceptionCode.NOT_SUPPORTED_ERR = (ExceptionMessage[9] = "Not supported", 9);
31729    var INUSE_ATTRIBUTE_ERR = ExceptionCode.INUSE_ATTRIBUTE_ERR = (ExceptionMessage[10] = "Attribute in use", 10); //level2
31730  
31731    var INVALID_STATE_ERR = ExceptionCode.INVALID_STATE_ERR = (ExceptionMessage[11] = "Invalid state", 11);
31732    var SYNTAX_ERR = ExceptionCode.SYNTAX_ERR = (ExceptionMessage[12] = "Syntax error", 12);
31733    var INVALID_MODIFICATION_ERR = ExceptionCode.INVALID_MODIFICATION_ERR = (ExceptionMessage[13] = "Invalid modification", 13);
31734    var NAMESPACE_ERR = ExceptionCode.NAMESPACE_ERR = (ExceptionMessage[14] = "Invalid namespace", 14);
31735    var INVALID_ACCESS_ERR = ExceptionCode.INVALID_ACCESS_ERR = (ExceptionMessage[15] = "Invalid access", 15);
31736  
31737    function DOMException(code, message) {
31738      if (message instanceof Error) {
31739        var error = message;
31740      } else {
31741        error = this;
31742        Error.call(this, ExceptionMessage[code]);
31743        this.message = ExceptionMessage[code];
31744        if (Error.captureStackTrace) Error.captureStackTrace(this, DOMException);
31745      }
31746  
31747      error.code = code;
31748      if (message) this.message = this.message + ": " + message;
31749      return error;
31750    }
31751    DOMException.prototype = Error.prototype;
31752    copy(ExceptionCode, DOMException);
31753    /**
31754     * @see http://www.w3.org/TR/2000/REC-DOM-Level-2-Core-20001113/core.html#ID-536297177
31755     * The NodeList interface provides the abstraction of an ordered collection of nodes, without defining or constraining how this collection is implemented. NodeList objects in the DOM are live.
31756     * The items in the NodeList are accessible via an integral index, starting from 0.
31757     */
31758  
31759    function NodeList() {}
31760    NodeList.prototype = {
31761      /**
31762       * The number of nodes in the list. The range of valid child node indices is 0 to length-1 inclusive.
31763       * @standard level1
31764       */
31765      length: 0,
31766  
31767      /**
31768       * Returns the indexth item in the collection. If index is greater than or equal to the number of nodes in the list, this returns null.
31769       * @standard level1
31770       * @param index  unsigned long 
31771       *   Index into the collection.
31772       * @return Node
31773       * 	The node at the indexth position in the NodeList, or null if that is not a valid index. 
31774       */
31775      item: function item(index) {
31776        return this[index] || null;
31777      },
31778      toString: function toString(isHTML, nodeFilter) {
31779        for (var buf = [], i = 0; i < this.length; i++) {
31780          serializeToString(this[i], buf, isHTML, nodeFilter);
31781        }
31782  
31783        return buf.join('');
31784      }
31785    };
31786  
31787    function LiveNodeList(node, refresh) {
31788      this._node = node;
31789      this._refresh = refresh;
31790  
31791      _updateLiveList(this);
31792    }
31793  
31794    function _updateLiveList(list) {
31795      var inc = list._node._inc || list._node.ownerDocument._inc;
31796  
31797      if (list._inc != inc) {
31798        var ls = list._refresh(list._node); //console.log(ls.length)
31799  
31800  
31801        __set__(list, 'length', ls.length);
31802  
31803        copy(ls, list);
31804        list._inc = inc;
31805      }
31806    }
31807  
31808    LiveNodeList.prototype.item = function (i) {
31809      _updateLiveList(this);
31810  
31811      return this[i];
31812    };
31813  
31814    _extends$1(LiveNodeList, NodeList);
31815    /**
31816     * 
31817     * Objects implementing the NamedNodeMap interface are used to represent collections of nodes that can be accessed by name. Note that NamedNodeMap does not inherit from NodeList; NamedNodeMaps are not maintained in any particular order. Objects contained in an object implementing NamedNodeMap may also be accessed by an ordinal index, but this is simply to allow convenient enumeration of the contents of a NamedNodeMap, and does not imply that the DOM specifies an order to these Nodes.
31818     * NamedNodeMap objects in the DOM are live.
31819     * used for attributes or DocumentType entities 
31820     */
31821  
31822  
31823    function NamedNodeMap() {}
31824  
31825    function _findNodeIndex(list, node) {
31826      var i = list.length;
31827  
31828      while (i--) {
31829        if (list[i] === node) {
31830          return i;
31831        }
31832      }
31833    }
31834  
31835    function _addNamedNode(el, list, newAttr, oldAttr) {
31836      if (oldAttr) {
31837        list[_findNodeIndex(list, oldAttr)] = newAttr;
31838      } else {
31839        list[list.length++] = newAttr;
31840      }
31841  
31842      if (el) {
31843        newAttr.ownerElement = el;
31844        var doc = el.ownerDocument;
31845  
31846        if (doc) {
31847          oldAttr && _onRemoveAttribute(doc, el, oldAttr);
31848  
31849          _onAddAttribute(doc, el, newAttr);
31850        }
31851      }
31852    }
31853  
31854    function _removeNamedNode(el, list, attr) {
31855      //console.log('remove attr:'+attr)
31856      var i = _findNodeIndex(list, attr);
31857  
31858      if (i >= 0) {
31859        var lastIndex = list.length - 1;
31860  
31861        while (i < lastIndex) {
31862          list[i] = list[++i];
31863        }
31864  
31865        list.length = lastIndex;
31866  
31867        if (el) {
31868          var doc = el.ownerDocument;
31869  
31870          if (doc) {
31871            _onRemoveAttribute(doc, el, attr);
31872  
31873            attr.ownerElement = null;
31874          }
31875        }
31876      } else {
31877        throw DOMException(NOT_FOUND_ERR, new Error(el.tagName + '@' + attr));
31878      }
31879    }
31880  
31881    NamedNodeMap.prototype = {
31882      length: 0,
31883      item: NodeList.prototype.item,
31884      getNamedItem: function getNamedItem(key) {
31885        //		if(key.indexOf(':')>0 || key == 'xmlns'){
31886        //			return null;
31887        //		}
31888        //console.log()
31889        var i = this.length;
31890  
31891        while (i--) {
31892          var attr = this[i]; //console.log(attr.nodeName,key)
31893  
31894          if (attr.nodeName == key) {
31895            return attr;
31896          }
31897        }
31898      },
31899      setNamedItem: function setNamedItem(attr) {
31900        var el = attr.ownerElement;
31901  
31902        if (el && el != this._ownerElement) {
31903          throw new DOMException(INUSE_ATTRIBUTE_ERR);
31904        }
31905  
31906        var oldAttr = this.getNamedItem(attr.nodeName);
31907  
31908        _addNamedNode(this._ownerElement, this, attr, oldAttr);
31909  
31910        return oldAttr;
31911      },
31912  
31913      /* returns Node */
31914      setNamedItemNS: function setNamedItemNS(attr) {
31915        // raises: WRONG_DOCUMENT_ERR,NO_MODIFICATION_ALLOWED_ER
vendor: 20,382 bytes, lines 31915-32594
31915R,INUSE_ATTRIBUTE_ERR
31916        var el = attr.ownerElement,
31917            oldAttr;
31918  
31919        if (el && el != this._ownerElement) {
31920          throw new DOMException(INUSE_ATTRIBUTE_ERR);
31921        }
31922  
31923        oldAttr = this.getNamedItemNS(attr.namespaceURI, attr.localName);
31924  
31925        _addNamedNode(this._ownerElement, this, attr, oldAttr);
31926  
31927        return oldAttr;
31928      },
31929  
31930      /* returns Node */
31931      removeNamedItem: function removeNamedItem(key) {
31932        var attr = this.getNamedItem(key);
31933  
31934        _removeNamedNode(this._ownerElement, this, attr);
31935  
31936        return attr;
31937      },
31938      // raises: NOT_FOUND_ERR,NO_MODIFICATION_ALLOWED_ERR
31939      //for level2
31940      removeNamedItemNS: function removeNamedItemNS(namespaceURI, localName) {
31941        var attr = this.getNamedItemNS(namespaceURI, localName);
31942  
31943        _removeNamedNode(this._ownerElement, this, attr);
31944  
31945        return attr;
31946      },
31947      getNamedItemNS: function getNamedItemNS(namespaceURI, localName) {
31948        var i = this.length;
31949  
31950        while (i--) {
31951          var node = this[i];
31952  
31953          if (node.localName == localName && node.namespaceURI == namespaceURI) {
31954            return node;
31955          }
31956        }
31957  
31958        return null;
31959      }
31960    };
31961    /**
31962     * @see http://www.w3.org/TR/REC-DOM-Level-1/level-one-core.html#ID-102161490
31963     */
31964  
31965    function DOMImplementation(
31966    /* Object */
31967    features) {
31968      this._features = {};
31969  
31970      if (features) {
31971        for (var feature in features) {
31972          this._features = features[feature];
31973        }
31974      }
31975    }
31976    DOMImplementation.prototype = {
31977      hasFeature: function hasFeature(
31978      /* string */
31979      feature,
31980      /* string */
31981      version) {
31982        var versions = this._features[feature.toLowerCase()];
31983  
31984        if (versions && (!version || version in versions)) {
31985          return true;
31986        } else {
31987          return false;
31988        }
31989      },
31990      // Introduced in DOM Level 2:
31991      createDocument: function createDocument(namespaceURI, qualifiedName, doctype) {
31992        // raises:INVALID_CHARACTER_ERR,NAMESPACE_ERR,WRONG_DOCUMENT_ERR
31993        var doc = new Document();
31994        doc.implementation = this;
31995        doc.childNodes = new NodeList();
31996        doc.doctype = doctype;
31997  
31998        if (doctype) {
31999          doc.appendChild(doctype);
32000        }
32001  
32002        if (qualifiedName) {
32003          var root = doc.createElementNS(namespaceURI, qualifiedName);
32004          doc.appendChild(root);
32005        }
32006  
32007        return doc;
32008      },
32009      // Introduced in DOM Level 2:
32010      createDocumentType: function createDocumentType(qualifiedName, publicId, systemId) {
32011        // raises:INVALID_CHARACTER_ERR,NAMESPACE_ERR
32012        var node = new DocumentType();
32013        node.name = qualifiedName;
32014        node.nodeName = qualifiedName;
32015        node.publicId = publicId;
32016        node.systemId = systemId; // Introduced in DOM Level 2:
32017        //readonly attribute DOMString        internalSubset;
32018        //TODO:..
32019        //  readonly attribute NamedNodeMap     entities;
32020        //  readonly attribute NamedNodeMap     notations;
32021  
32022        return node;
32023      }
32024    };
32025    /**
32026     * @see http://www.w3.org/TR/2000/REC-DOM-Level-2-Core-20001113/core.html#ID-1950641247
32027     */
32028  
32029    function Node() {}
32030    Node.prototype = {
32031      firstChild: null,
32032      lastChild: null,
32033      previousSibling: null,
32034      nextSibling: null,
32035      attributes: null,
32036      parentNode: null,
32037      childNodes: null,
32038      ownerDocument: null,
32039      nodeValue: null,
32040      namespaceURI: null,
32041      prefix: null,
32042      localName: null,
32043      // Modified in DOM Level 2:
32044      insertBefore: function insertBefore(newChild, refChild) {
32045        //raises 
32046        return _insertBefore(this, newChild, refChild);
32047      },
32048      replaceChild: function replaceChild(newChild, oldChild) {
32049        //raises 
32050        this.insertBefore(newChild, oldChild);
32051  
32052        if (oldChild) {
32053          this.removeChild(oldChild);
32054        }
32055      },
32056      removeChild: function removeChild(oldChild) {
32057        return _removeChild(this, oldChild);
32058      },
32059      appendChild: function appendChild(newChild) {
32060        return this.insertBefore(newChild, null);
32061      },
32062      hasChildNodes: function hasChildNodes() {
32063        return this.firstChild != null;
32064      },
32065      cloneNode: function cloneNode(deep) {
32066        return _cloneNode(this.ownerDocument || this, this, deep);
32067      },
32068      // Modified in DOM Level 2:
32069      normalize: function normalize() {
32070        var child = this.firstChild;
32071  
32072        while (child) {
32073          var next = child.nextSibling;
32074  
32075          if (next && next.nodeType == TEXT_NODE && child.nodeType == TEXT_NODE) {
32076            this.removeChild(next);
32077            child.appendData(next.data);
32078          } else {
32079            child.normalize();
32080            child = next;
32081          }
32082        }
32083      },
32084      // Introduced in DOM Level 2:
32085      isSupported: function isSupported(feature, version) {
32086        return this.ownerDocument.implementation.hasFeature(feature, version);
32087      },
32088      // Introduced in DOM Level 2:
32089      hasAttributes: function hasAttributes() {
32090        return this.attributes.length > 0;
32091      },
32092      lookupPrefix: function lookupPrefix(namespaceURI) {
32093        var el = this;
32094  
32095        while (el) {
32096          var map = el._nsMap; //console.dir(map)
32097  
32098          if (map) {
32099            for (var n in map) {
32100              if (map[n] == namespaceURI) {
32101                return n;
32102              }
32103            }
32104          }
32105  
32106          el = el.nodeType == ATTRIBUTE_NODE ? el.ownerDocument : el.parentNode;
32107        }
32108  
32109        return null;
32110      },
32111      // Introduced in DOM Level 3:
32112      lookupNamespaceURI: function lookupNamespaceURI(prefix) {
32113        var el = this;
32114  
32115        while (el) {
32116          var map = el._nsMap; //console.dir(map)
32117  
32118          if (map) {
32119            if (prefix in map) {
32120              return map[prefix];
32121            }
32122          }
32123  
32124          el = el.nodeType == ATTRIBUTE_NODE ? el.ownerDocument : el.parentNode;
32125        }
32126  
32127        return null;
32128      },
32129      // Introduced in DOM Level 3:
32130      isDefaultNamespace: function isDefaultNamespace(namespaceURI) {
32131        var prefix = this.lookupPrefix(namespaceURI);
32132        return prefix == null;
32133      }
32134    };
32135  
32136    function _xmlEncoder(c) {
32137      return c == '<' && '&lt;' || c == '>' && '&gt;' || c == '&' && '&amp;' || c == '"' && '&quot;' || '&#' + c.charCodeAt() + ';';
32138    }
32139  
32140    copy(NodeType, Node);
32141    copy(NodeType, Node.prototype);
32142    /**
32143     * @param callback return true for continue,false for break
32144     * @return boolean true: break visit;
32145     */
32146  
32147    function _visitNode(node, callback) {
32148      if (callback(node)) {
32149        return true;
32150      }
32151  
32152      if (node = node.firstChild) {
32153        do {
32154          if (_visitNode(node, callback)) {
32155            return true;
32156          }
32157        } while (node = node.nextSibling);
32158      }
32159    }
32160  
32161    function Document() {}
32162  
32163    function _onAddAttribute(doc, el, newAttr) {
32164      doc && doc._inc++;
32165      var ns = newAttr.namespaceURI;
32166  
32167      if (ns == 'http://www.w3.org/2000/xmlns/') {
32168        //update namespace
32169        el._nsMap[newAttr.prefix ? newAttr.localName : ''] = newAttr.value;
32170      }
32171    }
32172  
32173    function _onRemoveAttribute(doc, el, newAttr, remove) {
32174      doc && doc._inc++;
32175      var ns = newAttr.namespaceURI;
32176  
32177      if (ns == 'http://www.w3.org/2000/xmlns/') {
32178        //update namespace
32179        delete el._nsMap[newAttr.prefix ? newAttr.localName : ''];
32180      }
32181    }
32182  
32183    function _onUpdateChild(doc, el, newChild) {
32184      if (doc && doc._inc) {
32185        doc._inc++; //update childNodes
32186  
32187        var cs = el.childNodes;
32188  
32189        if (newChild) {
32190          cs[cs.length++] = newChild;
32191        } else {
32192          //console.log(1)
32193          var child = el.firstChild;
32194          var i = 0;
32195  
32196          while (child) {
32197            cs[i++] = child;
32198            child = child.nextSibling;
32199          }
32200  
32201          cs.length = i;
32202        }
32203      }
32204    }
32205    /**
32206     * attributes;
32207     * children;
32208     * 
32209     * writeable properties:
32210     * nodeValue,Attr:value,CharacterData:data
32211     * prefix
32212     */
32213  
32214  
32215    function _removeChild(parentNode, child) {
32216      var previous = child.previousSibling;
32217      var next = child.nextSibling;
32218  
32219      if (previous) {
32220        previous.nextSibling = next;
32221      } else {
32222        parentNode.firstChild = next;
32223      }
32224  
32225      if (next) {
32226        next.previousSibling = previous;
32227      } else {
32228        parentNode.lastChild = previous;
32229      }
32230  
32231      _onUpdateChild(parentNode.ownerDocument, parentNode);
32232  
32233      return child;
32234    }
32235    /**
32236     * preformance key(refChild == null)
32237     */
32238  
32239  
32240    function _insertBefore(parentNode, newChild, nextChild) {
32241      var cp = newChild.parentNode;
32242  
32243      if (cp) {
32244        cp.removeChild(newChild); //remove and update
32245      }
32246  
32247      if (newChild.nodeType === DOCUMENT_FRAGMENT_NODE) {
32248        var newFirst = newChild.firstChild;
32249  
32250        if (newFirst == null) {
32251          return newChild;
32252        }
32253  
32254        var newLast = newChild.lastChild;
32255      } else {
32256        newFirst = newLast = newChild;
32257      }
32258  
32259      var pre = nextChild ? nextChild.previousSibling : parentNode.lastChild;
32260      newFirst.previousSibling = pre;
32261      newLast.nextSibling = nextChild;
32262  
32263      if (pre) {
32264        pre.nextSibling = newFirst;
32265      } else {
32266        parentNode.firstChild = newFirst;
32267      }
32268  
32269      if (nextChild == null) {
32270        parentNode.lastChild = newLast;
32271      } else {
32272        nextChild.previousSibling = newLast;
32273      }
32274  
32275      do {
32276        newFirst.parentNode = parentNode;
32277      } while (newFirst !== newLast && (newFirst = newFirst.nextSibling));
32278  
32279      _onUpdateChild(parentNode.ownerDocument || parentNode, parentNode); //console.log(parentNode.lastChild.nextSibling == null)
32280  
32281  
32282      if (newChild.nodeType == DOCUMENT_FRAGMENT_NODE) {
32283        newChild.firstChild = newChild.lastChild = null;
32284      }
32285  
32286      return newChild;
32287    }
32288  
32289    function _appendSingleChild(parentNode, newChild) {
32290      var cp = newChild.parentNode;
32291  
32292      if (cp) {
32293        var pre = parentNode.lastChild;
32294        cp.removeChild(newChild); //remove and update
32295  
32296        var pre = parentNode.lastChild;
32297      }
32298  
32299      var pre = parentNode.lastChild;
32300      newChild.parentNode = parentNode;
32301      newChild.previousSibling = pre;
32302      newChild.nextSibling = null;
32303  
32304      if (pre) {
32305        pre.nextSibling = newChild;
32306      } else {
32307        parentNode.firstChild = newChild;
32308      }
32309  
32310      parentNode.lastChild = newChild;
32311  
32312      _onUpdateChild(parentNode.ownerDocument, parentNode, newChild);
32313  
32314      return newChild; //console.log("__aa",parentNode.lastChild.nextSibling == null)
32315    }
32316  
32317    Document.prototype = {
32318      //implementation : null,
32319      nodeName: '#document',
32320      nodeType: DOCUMENT_NODE,
32321      doctype: null,
32322      documentElement: null,
32323      _inc: 1,
32324      insertBefore: function insertBefore(newChild, refChild) {
32325        //raises 
32326        if (newChild.nodeType == DOCUMENT_FRAGMENT_NODE) {
32327          var child = newChild.firstChild;
32328  
32329          while (child) {
32330            var next = child.nextSibling;
32331            this.insertBefore(child, refChild);
32332            child = next;
32333          }
32334  
32335          return newChild;
32336        }
32337  
32338        if (this.documentElement == null && newChild.nodeType == ELEMENT_NODE) {
32339          this.documentElement = newChild;
32340        }
32341  
32342        return _insertBefore(this, newChild, refChild), newChild.ownerDocument = this, newChild;
32343      },
32344      removeChild: function removeChild(oldChild) {
32345        if (this.documentElement == oldChild) {
32346          this.documentElement = null;
32347        }
32348  
32349        return _removeChild(this, oldChild);
32350      },
32351      // Introduced in DOM Level 2:
32352      importNode: function importNode(importedNode, deep) {
32353        return _importNode(this, importedNode, deep);
32354      },
32355      // Introduced in DOM Level 2:
32356      getElementById: function getElementById(id) {
32357        var rtv = null;
32358  
32359        _visitNode(this.documentElement, function (node) {
32360          if (node.nodeType == ELEMENT_NODE) {
32361            if (node.getAttribute('id') == id) {
32362              rtv = node;
32363              return true;
32364            }
32365          }
32366        });
32367  
32368        return rtv;
32369      },
32370      //document factory method:
32371      createElement: function createElement(tagName) {
32372        var node = new Element();
32373        node.ownerDocument = this;
32374        node.nodeName = tagName;
32375        node.tagName = tagName;
32376        node.childNodes = new NodeList();
32377        var attrs = node.attributes = new NamedNodeMap();
32378        attrs._ownerElement = node;
32379        return node;
32380      },
32381      createDocumentFragment: function createDocumentFragment() {
32382        var node = new DocumentFragment();
32383        node.ownerDocument = this;
32384        node.childNodes = new NodeList();
32385        return node;
32386      },
32387      createTextNode: function createTextNode(data) {
32388        var node = new Text();
32389        node.ownerDocument = this;
32390        node.appendData(data);
32391        return node;
32392      },
32393      createComment: function createComment(data) {
32394        var node = new Comment();
32395        node.ownerDocument = this;
32396        node.appendData(data);
32397        return node;
32398      },
32399      createCDATASection: function createCDATASection(data) {
32400        var node = new CDATASection();
32401        node.ownerDocument = this;
32402        node.appendData(data);
32403        return node;
32404      },
32405      createProcessingInstruction: function createProcessingInstruction(target, data) {
32406        var node = new ProcessingInstruction();
32407        node.ownerDocument = this;
32408        node.tagName = node.target = target;
32409        node.nodeValue = node.data = data;
32410        return node;
32411      },
32412      createAttribute: function createAttribute(name) {
32413        var node = new Attr();
32414        node.ownerDocument = this;
32415        node.name = name;
32416        node.nodeName = name;
32417        node.localName = name;
32418        node.specified = true;
32419        return node;
32420      },
32421      createEntityReference: function createEntityReference(name) {
32422        var node = new EntityReference();
32423        node.ownerDocument = this;
32424        node.nodeName = name;
32425        return node;
32426      },
32427      // Introduced in DOM Level 2:
32428      createElementNS: function createElementNS(namespaceURI, qualifiedName) {
32429        var node = new Element();
32430        var pl = qualifiedName.split(':');
32431        var attrs = node.attributes = new NamedNodeMap();
32432        node.childNodes = new NodeList();
32433        node.ownerDocument = this;
32434        node.nodeName = qualifiedName;
32435        node.tagName = qualifiedName;
32436        node.namespaceURI = namespaceURI;
32437  
32438        if (pl.length == 2) {
32439          node.prefix = pl[0];
32440          node.localName = pl[1];
32441        } else {
32442          //el.prefix = null;
32443          node.localName = qualifiedName;
32444        }
32445  
32446        attrs._ownerElement = node;
32447        return node;
32448      },
32449      // Introduced in DOM Level 2:
32450      createAttributeNS: function createAttributeNS(namespaceURI, qualifiedName) {
32451        var node = new Attr();
32452        var pl = qualifiedName.split(':');
32453        node.ownerDocument = this;
32454        node.nodeName = qualifiedName;
32455        node.name = qualifiedName;
32456        node.namespaceURI = namespaceURI;
32457        node.specified = true;
32458  
32459        if (pl.length == 2) {
32460          node.prefix = pl[0];
32461          node.localName = pl[1];
32462        } else {
32463          //el.prefix = null;
32464          node.localName = qualifiedName;
32465        }
32466  
32467        return node;
32468      }
32469    };
32470  
32471    _extends$1(Document, Node);
32472  
32473    function Element() {
32474      this._nsMap = {};
32475    }
32476    Element.prototype = {
32477      nodeType: ELEMENT_NODE,
32478      hasAttribute: function hasAttribute(name) {
32479        return this.getAttributeNode(name) != null;
32480      },
32481      getAttribute: function getAttribute(name) {
32482        var attr = this.getAttributeNode(name);
32483        return attr && attr.value || '';
32484      },
32485      getAttributeNode: function getAttributeNode(name) {
32486        return this.attributes.getNamedItem(name);
32487      },
32488      setAttribute: function setAttribute(name, value) {
32489        var attr = this.ownerDocument.createAttribute(name);
32490        attr.value = attr.nodeValue = "" + value;
32491        this.setAttributeNode(attr);
32492      },
32493      removeAttribute: function removeAttribute(name) {
32494        var attr = this.getAttributeNode(name);
32495        attr && this.removeAttributeNode(attr);
32496      },
32497      //four real opeartion method
32498      appendChild: function appendChild(newChild) {
32499        if (newChild.nodeType === DOCUMENT_FRAGMENT_NODE) {
32500          return this.insertBefore(newChild, null);
32501        } else {
32502          return _appendSingleChild(this, newChild);
32503        }
32504      },
32505      setAttributeNode: function setAttributeNode(newAttr) {
32506        return this.attributes.setNamedItem(newAttr);
32507      },
32508      setAttributeNodeNS: function setAttributeNodeNS(newAttr) {
32509        return this.attributes.setNamedItemNS(newAttr);
32510      },
32511      removeAttributeNode: function removeAttributeNode(oldAttr) {
32512        //console.log(this == oldAttr.ownerElement)
32513        return this.attributes.removeNamedItem(oldAttr.nodeName);
32514      },
32515      //get real attribute name,and remove it by removeAttributeNode
32516      removeAttributeNS: function removeAttributeNS(namespaceURI, localName) {
32517        var old = this.getAttributeNodeNS(namespaceURI, localName);
32518        old && this.removeAttributeNode(old);
32519      },
32520      hasAttributeNS: function hasAttributeNS(namespaceURI, localName) {
32521        return this.getAttributeNodeNS(namespaceURI, localName) != null;
32522      },
32523      getAttributeNS: function getAttributeNS(namespaceURI, localName) {
32524        var attr = this.getAttributeNodeNS(namespaceURI, localName);
32525        return attr && attr.value || '';
32526      },
32527      setAttributeNS: function setAttributeNS(namespaceURI, qualifiedName, value) {
32528        var attr = this.ownerDocument.createAttributeNS(namespaceURI, qualifiedName);
32529        attr.value = attr.nodeValue = "" + value;
32530        this.setAttributeNode(attr);
32531      },
32532      getAttributeNodeNS: function getAttributeNodeNS(namespaceURI, localName) {
32533        return this.attributes.getNamedItemNS(namespaceURI, localName);
32534      },
32535      getElementsByTagName: function getElementsByTagName(tagName) {
32536        return new LiveNodeList(this, function (base) {
32537          var ls = [];
32538  
32539          _visitNode(base, function (node) {
32540            if (node !== base && node.nodeType == ELEMENT_NODE && (tagName === '*' || node.tagName == tagName)) {
32541              ls.push(node);
32542            }
32543          });
32544  
32545          return ls;
32546        });
32547      },
32548      getElementsByTagNameNS: function getElementsByTagNameNS(namespaceURI, localName) {
32549        return new LiveNodeList(this, function (base) {
32550          var ls = [];
32551  
32552          _visitNode(base, function (node) {
32553            if (node !== base && node.nodeType === ELEMENT_NODE && (namespaceURI === '*' || node.namespaceURI === namespaceURI) && (localName === '*' || node.localName == localName)) {
32554              ls.push(node);
32555            }
32556          });
32557  
32558          return ls;
32559        });
32560      }
32561    };
32562    Document.prototype.getElementsByTagName = Element.prototype.getElementsByTagName;
32563    Document.prototype.getElementsByTagNameNS = Element.prototype.getElementsByTagNameNS;
32564  
32565    _extends$1(Element, Node);
32566  
32567    function Attr() {}
32568    Attr.prototype.nodeType = ATTRIBUTE_NODE;
32569  
32570    _extends$1(Attr, Node);
32571  
32572    function CharacterData() {}
32573    CharacterData.prototype = {
32574      data: '',
32575      substringData: function substringData(offset, count) {
32576        return this.data.substring(offset, offset + count);
32577      },
32578      appendData: function appendData(text) {
32579        text = this.data + text;
32580        this.nodeValue = this.data = text;
32581        this.length = text.length;
32582      },
32583      insertData: function insertData(offset, text) {
32584        this.replaceData(offset, 0, text);
32585      },
32586      appendChild: function appendChild(newChild) {
32587        throw new Error(ExceptionMessage[HIERARCHY_REQUEST_ERR]);
32588      },
32589      deleteData: function deleteData(offset, count) {
32590        this.replaceData(offset, count, "");
32591      },
32592      replaceData: function replaceData(offset, count, text) {
32593        var start = this.data.substring(0, offset);
32594        var end = this.data.substring(offset + count);
vendor: 9,620 bytes, lines 32595-32926
32595        text = start + text + end;
32596        this.nodeValue = this.data = text;
32597        this.length = text.length;
32598      }
32599    };
32600  
32601    _extends$1(CharacterData, Node);
32602  
32603    function Text() {}
32604    Text.prototype = {
32605      nodeName: "#text",
32606      nodeType: TEXT_NODE,
32607      splitText: function splitText(offset) {
32608        var text = this.data;
32609        var newText = text.substring(offset);
32610        text = text.substring(0, offset);
32611        this.data = this.nodeValue = text;
32612        this.length = text.length;
32613        var newNode = this.ownerDocument.createTextNode(newText);
32614  
32615        if (this.parentNode) {
32616          this.parentNode.insertBefore(newNode, this.nextSibling);
32617        }
32618  
32619        return newNode;
32620      }
32621    };
32622  
32623    _extends$1(Text, CharacterData);
32624  
32625    function Comment() {}
32626    Comment.prototype = {
32627      nodeName: "#comment",
32628      nodeType: COMMENT_NODE
32629    };
32630  
32631    _extends$1(Comment, CharacterData);
32632  
32633    function CDATASection() {}
32634    CDATASection.prototype = {
32635      nodeName: "#cdata-section",
32636      nodeType: CDATA_SECTION_NODE
32637    };
32638  
32639    _extends$1(CDATASection, CharacterData);
32640  
32641    function DocumentType() {}
32642    DocumentType.prototype.nodeType = DOCUMENT_TYPE_NODE;
32643  
32644    _extends$1(DocumentType, Node);
32645  
32646    function Notation() {}
32647    Notation.prototype.nodeType = NOTATION_NODE;
32648  
32649    _extends$1(Notation, Node);
32650  
32651    function Entity() {}
32652    Entity.prototype.nodeType = ENTITY_NODE;
32653  
32654    _extends$1(Entity, Node);
32655  
32656    function EntityReference() {}
32657    EntityReference.prototype.nodeType = ENTITY_REFERENCE_NODE;
32658  
32659    _extends$1(EntityReference, Node);
32660  
32661    function DocumentFragment() {}
32662    DocumentFragment.prototype.nodeName = "#document-fragment";
32663    DocumentFragment.prototype.nodeType = DOCUMENT_FRAGMENT_NODE;
32664  
32665    _extends$1(DocumentFragment, Node);
32666  
32667    function ProcessingInstruction() {}
32668  
32669    ProcessingInstruction.prototype.nodeType = PROCESSING_INSTRUCTION_NODE;
32670  
32671    _extends$1(ProcessingInstruction, Node);
32672  
32673    function XMLSerializer() {}
32674  
32675    XMLSerializer.prototype.serializeToString = function (node, isHtml, nodeFilter) {
32676      return nodeSerializeToString.call(node, isHtml, nodeFilter);
32677    };
32678  
32679    Node.prototype.toString = nodeSerializeToString;
32680  
32681    function nodeSerializeToString(isHtml, nodeFilter) {
32682      var buf = [];
32683      var refNode = this.nodeType == 9 ? this.documentElement : this;
32684      var prefix = refNode.prefix;
32685      var uri = refNode.namespaceURI;
32686  
32687      if (uri && prefix == null) {
32688        //console.log(prefix)
32689        var prefix = refNode.lookupPrefix(uri);
32690  
32691        if (prefix == null) {
32692          //isHTML = true;
32693          var visibleNamespaces = [{
32694            namespace: uri,
32695            prefix: null
32696          } //{namespace:uri,prefix:''}
32697          ];
32698        }
32699      }
32700  
32701      serializeToString(this, buf, isHtml, nodeFilter, visibleNamespaces); //console.log('###',this.nodeType,uri,prefix,buf.join(''))
32702  
32703      return buf.join('');
32704    }
32705  
32706    function needNamespaceDefine(node, isHTML, visibleNamespaces) {
32707      var prefix = node.prefix || '';
32708      var uri = node.namespaceURI;
32709  
32710      if (!prefix && !uri) {
32711        return false;
32712      }
32713  
32714      if (prefix === "xml" && uri === "http://www.w3.org/XML/1998/namespace" || uri == 'http://www.w3.org/2000/xmlns/') {
32715        return false;
32716      }
32717  
32718      var i = visibleNamespaces.length; //console.log('@@@@',node.tagName,prefix,uri,visibleNamespaces)
32719  
32720      while (i--) {
32721        var ns = visibleNamespaces[i]; // get namespace prefix
32722        //console.log(node.nodeType,node.tagName,ns.prefix,prefix)
32723  
32724        if (ns.prefix == prefix) {
32725          return ns.namespace != uri;
32726        }
32727      } //console.log(isHTML,uri,prefix=='')
32728      //if(isHTML && prefix ==null && uri == 'http://www.w3.org/1999/xhtml'){
32729      //	return false;
32730      //}
32731      //node.flag = '11111'
32732      //console.error(3,true,node.flag,node.prefix,node.namespaceURI)
32733  
32734  
32735      return true;
32736    }
32737  
32738    function serializeToString(node, buf, isHTML, nodeFilter, visibleNamespaces) {
32739      if (nodeFilter) {
32740        node = nodeFilter(node);
32741  
32742        if (node) {
32743          if (typeof node == 'string') {
32744            buf.push(node);
32745            return;
32746          }
32747        } else {
32748          return;
32749        } //buf.sort.apply(attrs, attributeSorter);
32750  
32751      }
32752  
32753      switch (node.nodeType) {
32754        case ELEMENT_NODE:
32755          if (!visibleNamespaces) visibleNamespaces = [];
32756          var startVisibleNamespaces = visibleNamespaces.length;
32757          var attrs = node.attributes;
32758          var len = attrs.length;
32759          var child = node.firstChild;
32760          var nodeName = node.tagName;
32761          isHTML = htmlns === node.namespaceURI || isHTML;
32762          buf.push('<', nodeName);
32763  
32764          for (var i = 0; i < len; i++) {
32765            // add namespaces for attributes
32766            var attr = attrs.item(i);
32767  
32768            if (attr.prefix == 'xmlns') {
32769              visibleNamespaces.push({
32770                prefix: attr.localName,
32771                namespace: attr.value
32772              });
32773            } else if (attr.nodeName == 'xmlns') {
32774              visibleNamespaces.push({
32775                prefix: '',
32776                namespace: attr.value
32777              });
32778            }
32779          }
32780  
32781          for (var i = 0; i < len; i++) {
32782            var attr = attrs.item(i);
32783  
32784            if (needNamespaceDefine(attr, isHTML, visibleNamespaces)) {
32785              var prefix = attr.prefix || '';
32786              var uri = attr.namespaceURI;
32787              var ns = prefix ? ' xmlns:' + prefix : " xmlns";
32788              buf.push(ns, '="', uri, '"');
32789              visibleNamespaces.push({
32790                prefix: prefix,
32791                namespace: uri
32792              });
32793            }
32794  
32795            serializeToString(attr, buf, isHTML, nodeFilter, visibleNamespaces);
32796          } // add namespace for current node		
32797  
32798  
32799          if (needNamespaceDefine(node, isHTML, visibleNamespaces)) {
32800            var prefix = node.prefix || '';
32801            var uri = node.namespaceURI;
32802            var ns = prefix ? ' xmlns:' + prefix : " xmlns";
32803            buf.push(ns, '="', uri, '"');
32804            visibleNamespaces.push({
32805              prefix: prefix,
32806              namespace: uri
32807            });
32808          }
32809  
32810          if (child || isHTML && !/^(?:meta|link|img|br|hr|input)$/i.test(nodeName)) {
32811            buf.push('>'); //if is cdata child node
32812  
32813            if (isHTML && /^script$/i.test(nodeName)) {
32814              while (child) {
32815                if (child.data) {
32816                  buf.push(child.data);
32817                } else {
32818                  serializeToString(child, buf, isHTML, nodeFilter, visibleNamespaces);
32819                }
32820  
32821                child = child.nextSibling;
32822              }
32823            } else {
32824              while (child) {
32825                serializeToString(child, buf, isHTML, nodeFilter, visibleNamespaces);
32826                child = child.nextSibling;
32827              }
32828            }
32829  
32830            buf.push('</', nodeName, '>');
32831          } else {
32832            buf.push('/>');
32833          } // remove added visible namespaces
32834          //visibleNamespaces.length = startVisibleNamespaces;
32835  
32836  
32837          return;
32838  
32839        case DOCUMENT_NODE:
32840        case DOCUMENT_FRAGMENT_NODE:
32841          var child = node.firstChild;
32842  
32843          while (child) {
32844            serializeToString(child, buf, isHTML, nodeFilter, visibleNamespaces);
32845            child = child.nextSibling;
32846          }
32847  
32848          return;
32849  
32850        case ATTRIBUTE_NODE:
32851          return buf.push(' ', node.name, '="', node.value.replace(/[<&"]/g, _xmlEncoder), '"');
32852  
32853        case TEXT_NODE:
32854          return buf.push(node.data.replace(/[<&]/g, _xmlEncoder));
32855  
32856        case CDATA_SECTION_NODE:
32857          return buf.push('<![CDATA[', node.data, ']]>');
32858  
32859        case COMMENT_NODE:
32860          return buf.push("<!--", node.data, "-->");
32861  
32862        case DOCUMENT_TYPE_NODE:
32863          var pubid = node.publicId;
32864          var sysid = node.systemId;
32865          buf.push('<!DOCTYPE ', node.name);
32866  
32867          if (pubid) {
32868            buf.push(' PUBLIC "', pubid);
32869  
32870            if (sysid && sysid != '.') {
32871              buf.push('" "', sysid);
32872            }
32873  
32874            buf.push('">');
32875          } else if (sysid && sysid != '.') {
32876            buf.push(' SYSTEM "', sysid, '">');
32877          } else {
32878            var sub = node.internalSubset;
32879  
32880            if (sub) {
32881              buf.push(" [", sub, "]");
32882            }
32883  
32884            buf.push(">");
32885          }
32886  
32887          return;
32888  
32889        case PROCESSING_INSTRUCTION_NODE:
32890          return buf.push("<?", node.target, " ", node.data, "?>");
32891  
32892        case ENTITY_REFERENCE_NODE:
32893          return buf.push('&', node.nodeName, ';');
32894        //case ENTITY_NODE:
32895        //case NOTATION_NODE:
32896  
32897        default:
32898          buf.push('??', node.nodeName);
32899      }
32900    }
32901  
32902    function _importNode(doc, node, deep) {
32903      var node2;
32904  
32905      switch (node.nodeType) {
32906        case ELEMENT_NODE:
32907          node2 = node.cloneNode(false);
32908          node2.ownerDocument = doc;
32909        //var attrs = node2.attributes;
32910        //var len = attrs.length;
32911        //for(var i=0;i<len;i++){
32912        //node2.setAttributeNodeNS(importNode(doc,attrs.item(i),deep));
32913        //}
32914  
32915        case DOCUMENT_FRAGMENT_NODE:
32916          break;
32917  
32918        case ATTRIBUTE_NODE:
32919          deep = true;
32920          break;
32921        //case ENTITY_REFERENCE_NODE:
32922        //case PROCESSING_INSTRUCTION_NODE:
32923        ////case TEXT_NODE:
32924        //case CDATA_SECTION_NODE:
32925        //case COMMENT_NODE:
32926        //	deep = false;
32927        //	break;
32928        //case DOCUMENT_NODE:
32929        //case DOCUMENT_TYPE_NODE:
32930        //cannot be imported.
32931        //case ENTITY_NODE:
32932        //case NOTATION_NODE:
32933        //can not hit in level3
32934        //default:throw e;
32935      }
32936  
32937      if (!node2) {
32938        node2 = node.cloneNode(false); //false
32939      }
32940  
32941      node2.ownerDocument = doc;
32942      node2.parentNode = null;
32943  
32944      if (deep) {
32945        var child = node.firstChild;
32946  
32947        while (child) {
32948          node2.appendChild(_importNode(doc, child, deep));
32949          child = child.nextSibling;
32950        }
32951      }
32952  
32953      return node2;
32954    } //
32955    //var _relationMap = {firstChild:1,lastChild:1,previousSibling:1,nextSibling:1,
32956    //					attributes:1,childNodes:1,parentNode:1,documentElement:1,doctype,};
32957  
32958  
32959    function _cloneNode(doc, node, deep) {
32960      var node2 = new node.constructor();
32961  
32962      for (var n in node) {
32963        var v = node[n];
32964  
32965        if (typeof v != 'object') {
32966          if (v != node2[n]) {
32967            node2[n] = v;
32968          }
32969        }
32970      }
32971  
32972      if (node.childNodes) {
32973        node2.childNodes = new NodeList();
32974      }
32975  
32976      node2.ownerDocument = doc;
32977  
32978      switch (node2.nodeType) {
32979        case ELEMENT_NODE:
32980          var attrs = node.attributes;
32981          var attrs2 = node2.attributes = new NamedNodeMap();
32982          var len = attrs.length;
32983          attrs2._ownerElement = node2;
32984  
32985          for (var i = 0; i < len; i++) {
32986            node2.setAttributeNode(_cloneNode(doc, attrs.item(i), true));
32987          }
32988  
32989          break;
32990  
32991        case ATTRIBUTE_NODE:
32992          deep = true;
32993      }
32994  
32995      if (deep) {
32996        var child = node.firstChild;
32997  
32998        while (child) {
32999          node2.appendChild(_cloneNode(doc, child, deep));
33000          child = child.nextSibling;
33001        }
33002      }
33003  
33004      return node2;
33005    }
33006  
33007    function __set__(object, key, value) {
33008      object[key] = value;
33009    } //do dynamic
33010  
33011  
33012    try {
33013      if (Object.defineProperty) {
33014        var getTextContent = function getTextContent(node) {
33015          switch (node.nodeType) {
33016            case ELEMENT_NODE:
33017            case DOCUMENT_FRAGMENT_NODE:
33018              var buf = [];
33019              node = node.firstChild;
33020  
33021              while (node) {
33022                if (node.nodeType !== 7 && node.nodeType !== 8) {
33023                  buf.push(getTextContent(node));
33024                }
33025  
33026                node = node.nextSibling;
33027              }
33028  
33029              return buf.join('');
33030  
33031            default:
33032              return node.nodeValue;
33033          }
33034        };
33035  
33036        Object.defineProperty(LiveNodeList.prototype, 'length', {
33037          get: function get() {
33038            _updateLiveList(this);
33039  
33040            return this.$$length;
33041          }
33042        });
33043        Object.defineProperty(Node.prototype, 'textContent', {
33044          get: function get() {
33045            return getTextContent(this);
33046          },
33047          set: function set(data) {
33048            switch (this.nodeType) {
33049              case ELEMENT_NODE:
33050              case DOCUMENT_FRAGMENT_NODE:
33051                while (this.firstChild) {
33052                  this.removeChild(this.firstChild);
33053                }
33054  
33055                if (data || String(data)) {
33056                  this.appendChild(this.ownerDocument.createTextNode(data));
33057                }
33058  
33059                break;
33060  
33061              default:
33062                //TODO:
33063                this.data = data;
33064                this.value = data;
33065                this.nodeValue = data;
33066            }
33067          }
33068        });
33069  
33070        __set__ = function __set__(object, key, value) {
33071          //console.log(value)
33072          object['$$' + key] = value;
33073        };
33074      }
33075    } catch (e) {} //ie8
33076    //if(typeof require == 'function'){
33077  
33078  
33079    var DOMImplementation_1 = DOMImplementation;
33080    var XMLSerializer_1 = XMLSerializer; //}
33081  
33082    var dom = {
33083      DOMImplementation: DOMImplementation_1,
33084      XMLSerializer: XMLSerializer_1
33085    };
33086  
33087    var domParser = createCommonjsModule(function (module, exports) {
33088      function DOMParser(options) {
33089        this.options = options || {
33090          locator: {}
33091        };
33092      }
33093  
33094      DOMParser.prototype.parseFromString = function (source, mimeType) {
33095        var options = this.options;
33096        var sax = new XMLReader();
33097        var domBuilder = options.domBuilder || new DOMHandler(); //contentHandler and LexicalHandler
33098  
33099        var errorHandler = options.errorHandler;
33100        var locator = options.locator;
33101        var defaultNSMap = options.xmlns || {};
33102        var entityMap = {
33103          'lt': '<',
33104          'gt': '>',
33105          'amp': '&',
33106          'quot': '"',
33107          'apos': "'"
33108        };
33109  
33110        if (locator) {
33111          domBuilder.setDocumentLocator(locator);
33112        }
33113  
33114        sax.errorHandler = buildErrorHandler(errorHandler, domBuilder, locator);
33115        sax.domBuilder = options.domBuilder || domBuilder;
33116  
33117        if (/\/x?html?$/.test(mimeType)) {
33118          entityMap.nbsp = '\xa0';
33119          entityMap.copy = '\xa9';
33120          defaultNSMap[''] = 'http://www.w3.org/1999/xhtml';
33121        }
33122  
33123        defaultNSMap.xml = defaultNSMap.xml || 'http://www.w3.org/XML/1998/namespace';
33124  
vendor: 5,143 bytes, lines 33125-33272
33125        if (source) {
33126          sax.parse(source, defaultNSMap, entityMap);
33127        } else {
33128          sax.errorHandler.error("invalid doc source");
33129        }
33130  
33131        return domBuilder.doc;
33132      };
33133  
33134      function buildErrorHandler(errorImpl, domBuilder, locator) {
33135        if (!errorImpl) {
33136          if (domBuilder instanceof DOMHandler) {
33137            return domBuilder;
33138          }
33139  
33140          errorImpl = domBuilder;
33141        }
33142  
33143        var errorHandler = {};
33144        var isCallback = errorImpl instanceof Function;
33145        locator = locator || {};
33146  
33147        function build(key) {
33148          var fn = errorImpl[key];
33149  
33150          if (!fn && isCallback) {
33151            fn = errorImpl.length == 2 ? function (msg) {
33152              errorImpl(key, msg);
33153            } : errorImpl;
33154          }
33155  
33156          errorHandler[key] = fn && function (msg) {
33157            fn('[xmldom ' + key + ']\t' + msg + _locator(locator));
33158          } || function () {};
33159        }
33160  
33161        build('warning');
33162        build('error');
33163        build('fatalError');
33164        return errorHandler;
33165      } //console.log('#\n\n\n\n\n\n\n####')
33166  
33167      /**
33168       * +ContentHandler+ErrorHandler
33169       * +LexicalHandler+EntityResolver2
33170       * -DeclHandler-DTDHandler 
33171       * 
33172       * DefaultHandler:EntityResolver, DTDHandler, ContentHandler, ErrorHandler
33173       * DefaultHandler2:DefaultHandler,LexicalHandler, DeclHandler, EntityResolver2
33174       * @link http://www.saxproject.org/apidoc/org/xml/sax/helpers/DefaultHandler.html
33175       */
33176  
33177  
33178      function DOMHandler() {
33179        this.cdata = false;
33180      }
33181  
33182      function position(locator, node) {
33183        node.lineNumber = locator.lineNumber;
33184        node.columnNumber = locator.columnNumber;
33185      }
33186      /**
33187       * @see org.xml.sax.ContentHandler#startDocument
33188       * @link http://www.saxproject.org/apidoc/org/xml/sax/ContentHandler.html
33189       */
33190  
33191  
33192      DOMHandler.prototype = {
33193        startDocument: function startDocument() {
33194          this.doc = new DOMImplementation().createDocument(null, null, null);
33195  
33196          if (this.locator) {
33197            this.doc.documentURI = this.locator.systemId;
33198          }
33199        },
33200        startElement: function startElement(namespaceURI, localName, qName, attrs) {
33201          var doc = this.doc;
33202          var el = doc.createElementNS(namespaceURI, qName || localName);
33203          var len = attrs.length;
33204          appendElement(this, el);
33205          this.currentElement = el;
33206          this.locator && position(this.locator, el);
33207  
33208          for (var i = 0; i < len; i++) {
33209            var namespaceURI = attrs.getURI(i);
33210            var value = attrs.getValue(i);
33211            var qName = attrs.getQName(i);
33212            var attr = doc.createAttributeNS(namespaceURI, qName);
33213            this.locator && position(attrs.getLocator(i), attr);
33214            attr.value = attr.nodeValue = value;
33215            el.setAttributeNode(attr);
33216          }
33217        },
33218        endElement: function endElement(namespaceURI, localName, qName) {
33219          var current = this.currentElement;
33220          var tagName = current.tagName;
33221          this.currentElement = current.parentNode;
33222        },
33223        startPrefixMapping: function startPrefixMapping(prefix, uri) {},
33224        endPrefixMapping: function endPrefixMapping(prefix) {},
33225        processingInstruction: function processingInstruction(target, data) {
33226          var ins = this.doc.createProcessingInstruction(target, data);
33227          this.locator && position(this.locator, ins);
33228          appendElement(this, ins);
33229        },
33230        ignorableWhitespace: function ignorableWhitespace(ch, start, length) {},
33231        characters: function characters(chars, start, length) {
33232          chars = _toString.apply(this, arguments); //console.log(chars)
33233  
33234          if (chars) {
33235            if (this.cdata) {
33236              var charNode = this.doc.createCDATASection(chars);
33237            } else {
33238              var charNode = this.doc.createTextNode(chars);
33239            }
33240  
33241            if (this.currentElement) {
33242              this.currentElement.appendChild(charNode);
33243            } else if (/^\s*$/.test(chars)) {
33244              this.doc.appendChild(charNode); //process xml
33245            }
33246  
33247            this.locator && position(this.locator, charNode);
33248          }
33249        },
33250        skippedEntity: function skippedEntity(name) {},
33251        endDocument: function endDocument() {
33252          this.doc.normalize();
33253        },
33254        setDocumentLocator: function setDocumentLocator(locator) {
33255          if (this.locator = locator) {
33256            // && !('lineNumber' in locator)){
33257            locator.lineNumber = 0;
33258          }
33259        },
33260        //LexicalHandler
33261        comment: function comment(chars, start, length) {
33262          chars = _toString.apply(this, arguments);
33263          var comm = this.doc.createComment(chars);
33264          this.locator && position(this.locator, comm);
33265          appendElement(this, comm);
33266        },
33267        startCDATA: function startCDATA() {
33268          //used in characters() methods
33269          this.cdata = true;
33270        },
33271        endCDATA: function endCDATA() {
33272          this.cdata = false;
vendor: 4,078 bytes, lines 33273-33376
33273        },
33274        startDTD: function startDTD(name, publicId, systemId) {
33275          var impl = this.doc.implementation;
33276  
33277          if (impl && impl.createDocumentType) {
33278            var dt = impl.createDocumentType(name, publicId, systemId);
33279            this.locator && position(this.locator, dt);
33280            appendElement(this, dt);
33281          }
33282        },
33283  
33284        /**
33285         * @see org.xml.sax.ErrorHandler
33286         * @link http://www.saxproject.org/apidoc/org/xml/sax/ErrorHandler.html
33287         */
33288        warning: function warning(error) {
33289          console.warn('[xmldom warning]\t' + error, _locator(this.locator));
33290        },
33291        error: function error(_error) {
33292          console.error('[xmldom error]\t' + _error, _locator(this.locator));
33293        },
33294        fatalError: function fatalError(error) {
33295          console.error('[xmldom fatalError]\t' + error, _locator(this.locator));
33296          throw error;
33297        }
33298      };
33299  
33300      function _locator(l) {
33301        if (l) {
33302          return '\n@' + (l.systemId || '') + '#[line:' + l.lineNumber + ',col:' + l.columnNumber + ']';
33303        }
33304      }
33305  
33306      function _toString(chars, start, length) {
33307        if (typeof chars == 'string') {
33308          return chars.substr(start, length);
33309        } else {
33310          //java sax connect width xmldom on rhino(what about: "? && !(chars instanceof String)")
33311          if (chars.length >= start + length || start) {
33312            return new java.lang.String(chars, start, length) + '';
33313          }
33314  
33315          return chars;
33316        }
33317      }
33318      /*
33319       * @link http://www.saxproject.org/apidoc/org/xml/sax/ext/LexicalHandler.html
33320       * used method of org.xml.sax.ext.LexicalHandler:
33321       *  #comment(chars, start, length)
33322       *  #startCDATA()
33323       *  #endCDATA()
33324       *  #startDTD(name, publicId, systemId)
33325       *
33326       *
33327       * IGNORED method of org.xml.sax.ext.LexicalHandler:
33328       *  #endDTD()
33329       *  #startEntity(name)
33330       *  #endEntity(name)
33331       *
33332       *
33333       * @link http://www.saxproject.org/apidoc/org/xml/sax/ext/DeclHandler.html
33334       * IGNORED method of org.xml.sax.ext.DeclHandler
33335       * 	#attributeDecl(eName, aName, type, mode, value)
33336       *  #elementDecl(name, model)
33337       *  #externalEntityDecl(name, publicId, systemId)
33338       *  #internalEntityDecl(name, value)
33339       * @link http://www.saxproject.org/apidoc/org/xml/sax/ext/EntityResolver2.html
33340       * IGNORED method of org.xml.sax.EntityResolver2
33341       *  #resolveEntity(String name,String publicId,String baseURI,String systemId)
33342       *  #resolveEntity(publicId, systemId)
33343       *  #getExternalSubset(name, baseURI)
33344       * @link http://www.saxproject.org/apidoc/org/xml/sax/DTDHandler.html
33345       * IGNORED method of org.xml.sax.DTDHandler
33346       *  #notationDecl(name, publicId, systemId) {};
33347       *  #unparsedEntityDecl(name, publicId, systemId, notationName) {};
33348       */
33349  
33350  
33351      "endDTD,startEntity,endEntity,attributeDecl,elementDecl,externalEntityDecl,internalEntityDecl,resolveEntity,getExternalSubset,notationDecl,unparsedEntityDecl".replace(/\w+/g, function (key) {
33352        DOMHandler.prototype[key] = function () {
33353          return null;
33354        };
33355      });
33356      /* Private static helpers treated below as private instance methods, so don't need to add these to the public API; we might use a Relator to also get rid of non-standard public properties */
33357  
33358      function appendElement(hander, node) {
33359        if (!hander.currentElement) {
33360          hander.doc.appendChild(node);
33361        } else {
33362          hander.currentElement.appendChild(node);
33363        }
33364      } //appendChild and setAttributeNS are preformance key
33365      //if(typeof require == 'function'){
33366  
33367  
33368      var XMLReader = sax.XMLReader;
33369      var DOMImplementation = exports.DOMImplementation = dom.DOMImplementation;
33370      exports.XMLSerializer = dom.XMLSerializer;
33371      exports.DOMParser = DOMParser; //}
33372    });
33373    var domParser_1 = domParser.DOMImplementation;
33374    var domParser_2 = domParser.XMLSerializer;
33375    var domParser_3 = domParser.DOMParser;
33376  
vendor: 6,869 bytes, lines 33377-33569
33377    /*! @name mpd-parser @version 0.10.0 @license Apache-2.0 */
33378  
33379    var isObject$1 = function isObject(obj) {
33380      return !!obj && typeof obj === 'object';
33381    };
33382  
33383    var merge = function merge() {
33384      for (var _len = arguments.length, objects = new Array(_len), _key = 0; _key < _len; _key++) {
33385        objects[_key] = arguments[_key];
33386      }
33387  
33388      return objects.reduce(function (result, source) {
33389        Object.keys(source).forEach(function (key) {
33390          if (Array.isArray(result[key]) && Array.isArray(source[key])) {
33391            result[key] = result[key].concat(source[key]);
33392          } else if (isObject$1(result[key]) && isObject$1(source[key])) {
33393            result[key] = merge(result[key], source[key]);
33394          } else {
33395            result[key] = source[key];
33396          }
33397        });
33398        return result;
33399      }, {});
33400    };
33401  
33402    var values = function values(o) {
33403      return Object.keys(o).map(function (k) {
33404        return o[k];
33405      });
33406    };
33407  
33408    var range = function range(start, end) {
33409      var result = [];
33410  
33411      for (var i = start; i < end; i++) {
33412        result.push(i);
33413      }
33414  
33415      return result;
33416    };
33417  
33418    var flatten = function flatten(lists) {
33419      return lists.reduce(function (x, y) {
33420        return x.concat(y);
33421      }, []);
33422    };
33423  
33424    var from = function from(list) {
33425      if (!list.length) {
33426        return [];
33427      }
33428  
33429      var result = [];
33430  
33431      for (var i = 0; i < list.length; i++) {
33432        result.push(list[i]);
33433      }
33434  
33435      return result;
33436    };
33437  
33438    var findIndexes = function findIndexes(l, key) {
33439      return l.reduce(function (a, e, i) {
33440        if (e[key]) {
33441          a.push(i);
33442        }
33443  
33444        return a;
33445      }, []);
33446    };
33447  
33448    var errors = {
33449      INVALID_NUMBER_OF_PERIOD: 'INVALID_NUMBER_OF_PERIOD',
33450      DASH_EMPTY_MANIFEST: 'DASH_EMPTY_MANIFEST',
33451      DASH_INVALID_XML: 'DASH_INVALID_XML',
33452      NO_BASE_URL: 'NO_BASE_URL',
33453      MISSING_SEGMENT_INFORMATION: 'MISSING_SEGMENT_INFORMATION',
33454      SEGMENT_TIME_UNSPECIFIED: 'SEGMENT_TIME_UNSPECIFIED',
33455      UNSUPPORTED_UTC_TIMING_SCHEME: 'UNSUPPORTED_UTC_TIMING_SCHEME'
33456    };
33457    /**
33458     * @typedef {Object} SingleUri
33459     * @property {string} uri - relative location of segment
33460     * @property {string} resolvedUri - resolved location of segment
33461     * @property {Object} byterange - Object containing information on how to make byte range
33462     *   requests following byte-range-spec per RFC2616.
33463     * @property {String} byterange.length - length of range request
33464     * @property {String} byterange.offset - byte offset of range request
33465     *
33466     * @see https://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.35.1
33467     */
33468  
33469    /**
33470     * Converts a URLType node (5.3.9.2.3 Table 13) to a segment object
33471     * that conforms to how m3u8-parser is structured
33472     *
33473     * @see https://github.com/videojs/m3u8-parser
33474     *
33475     * @param {string} baseUrl - baseUrl provided by <BaseUrl> nodes
33476     * @param {string} source - source url for segment
33477     * @param {string} range - optional range used for range calls,
33478     *   follows  RFC 2616, Clause 14.35.1
33479     * @return {SingleUri} full segment information transformed into a format similar
33480     *   to m3u8-parser
33481     */
33482  
33483    var urlTypeToSegment = function urlTypeToSegment(_ref) {
33484      var _ref$baseUrl = _ref.baseUrl,
33485          baseUrl = _ref$baseUrl === void 0 ? '' : _ref$baseUrl,
33486          _ref$source = _ref.source,
33487          source = _ref$source === void 0 ? '' : _ref$source,
33488          _ref$range = _ref.range,
33489          range = _ref$range === void 0 ? '' : _ref$range,
33490          _ref$indexRange = _ref.indexRange,
33491          indexRange = _ref$indexRange === void 0 ? '' : _ref$indexRange;
33492      var segment = {
33493        uri: source,
33494        resolvedUri: resolveUrl_1(baseUrl || '', source)
33495      };
33496  
33497      if (range || indexRange) {
33498        var rangeStr = range ? range : indexRange;
33499        var ranges = rangeStr.split('-');
33500        var startRange = parseInt(ranges[0], 10);
33501        var endRange = parseInt(ranges[1], 10); // byterange should be inclusive according to
33502        // RFC 2616, Clause 14.35.1
33503  
33504        segment.byterange = {
33505          length: endRange - startRange + 1,
33506          offset: startRange
33507        };
33508      }
33509  
33510      return segment;
33511    };
33512  
33513    var byteRangeToString = function byteRangeToString(byterange) {
33514      // `endRange` is one less than `offset + length` because the HTTP range
33515      // header uses inclusive ranges
33516      var endRange = byterange.offset + byterange.length - 1;
33517      return byterange.offset + "-" + endRange;
33518    };
33519    /**
33520     * Functions for calculating the range of available segments in static and dynamic
33521     * manifests.
33522     */
33523  
33524  
33525    var segmentRange = {
33526      /**
33527       * Returns the entire range of available segments for a static MPD
33528       *
33529       * @param {Object} attributes
33530       *        Inheritied MPD attributes
33531       * @return {{ start: number, end: number }}
33532       *         The start and end numbers for available segments
33533       */
33534      "static": function _static(attributes) {
33535        var duration = attributes.duration,
33536            _attributes$timescale = attributes.timescale,
33537            timescale = _attributes$timescale === void 0 ? 1 : _attributes$timescale,
33538            sourceDuration = attributes.sourceDuration;
33539        return {
33540          start: 0,
33541          end: Math.ceil(sourceDuration / (duration / timescale))
33542        };
33543      },
33544  
33545      /**
33546       * Returns the current live window range of available segments for a dynamic MPD
33547       *
33548       * @param {Object} attributes
33549       *        Inheritied MPD attributes
33550       * @return {{ start: number, end: number }}
33551       *         The start and end numbers for available segments
33552       */
33553      dynamic: function dynamic(attributes) {
33554        var NOW = attributes.NOW,
33555            clientOffset = attributes.clientOffset,
33556            availabilityStartTime = attributes.availabilityStartTime,
33557            _attributes$timescale2 = attributes.timescale,
33558            timescale = _attributes$timescale2 === void 0 ? 1 : _attributes$timescale2,
33559            duration = attributes.duration,
33560            _attributes$start = attributes.start,
33561            start = _attributes$start === void 0 ? 0 : _attributes$start,
33562            _attributes$minimumUp = attributes.minimumUpdatePeriod,
33563            minimumUpdatePeriod = _attributes$minimumUp === void 0 ? 0 : _attributes$minimumUp,
33564            _attributes$timeShift = attributes.timeShiftBufferDepth,
33565            timeShiftBufferDepth = _attributes$timeShift === void 0 ? Infinity : _attributes$timeShift;
33566        var now = (NOW + clientOffset) / 1000;
33567        var periodStartWC = availabilityStartTime + start;
33568        var periodEndWC = now + minimumUpdatePeriod;
33569        var periodDuration = periodEndWC - periodStartWC;
33570        var segmentCount = Math.ceil(periodDuration * timescale / duration);
33571        var availableStart = Math.floor((now - periodStartWC - timeShiftBufferDepth) * timescale / duration);
33572        var availableEnd = Math.floor((now - periodStartWC) * timescale / duration);
33573        return {
33574          start: Math.max(0, availableStart),
33575          end: Math.min(segmentCount, availableEnd)
33576        };
33577      }
33578    };
33579    /**
33580     * Maps a range of numbers to objects with information needed to build the corresponding
33581     * segment list
33582     *
33583     * @name toSegmentsCallback
33584     * @function
33585     * @param {number} number
33586     *        Number of the segment
33587     * @param {number} index
33588     *        Index of the number in the range list
33589     * @return {{ number: Number, duration: Number, timeline: Number, time: Number }}
33590     *         Object with segment timing and duration info
33591     */
33592  
33593    /**
33594     * Returns a callback for Array.prototype.map for mapping a range of numbers to
33595     * information needed to build the segment list.
33596     *
33597     * @param {Object} attributes
33598     *        Inherited MPD attributes
33599     * @return {toSegmentsCallback}
33600     *         Callback map function
33601     */
33602  
33603    var toSegments = function toSegments(attributes) {
33604      return function (number, index) {
33605        var duration = attributes.duration,
33606            _attributes$timescale3 = attributes.timescale,
33607            timescale = _attributes$timescale3 === void 0 ? 1 : _attributes$timescale3,
33608            periodIndex = attributes.periodIndex,
33609            _attributes$startNumb = attributes.startNumber,
33610            startNumber = _attributes$startNumb === void 0 ? 1 : _attributes$startNumb;
33611        return {
33612          number: startNumber + number,
33613          duration: duration / timescale,
33614          timeline: periodIndex,
33615          time: index * duration
33616        };
33617      };
33618    };
33619    /**
33620     * Returns a list of objects containing segment timing and duration info used for
33621     * building the list of segments. This uses the @duration attribute specified
33622     * in the MPD manifest to derive the range of segments.
33623     *
33624     * @param {Object} attributes
33625     *        Inherited MPD attributes
33626     * @return {{number: number, duration: number, time: number, timeline: number}[]}
33627     *         List of Objects with segment timing and duration info
33628     */
33629  
33630  
33631    var parseByDuration = function parseByDuration(attributes) {
33632      var _attributes$type = attributes.type,
33633          type = _attributes$type === void 0 ? 'static' : _attributes$type,
33634          duration = attributes.duration,
33635          _attributes$timescale4 = attributes.timescale,
33636          timescale = _attributes$timescale4 === void 0 ? 1 : _attributes$timescale4,
33637          sourceDuration = attributes.sourceDuration;
33638  
33639      var _segmentRange$type = segmentRange[type](attributes),
33640          start = _segmentRange$type.start,
33641          end = _segmentRange$type.end;
33642  
33643      var segments = range(start, end).map(toSegments(attributes));
33644  
33645      if (type === 'static') {
33646        var index = segments.length - 1; // final segment may be less than full segment duration
33647  
33648        segments[index].duration = sourceDuration - duration / timescale * index;
33649      }
33650  
33651      return segments;
33652    };
33653    /**
33654     * Translates SegmentBase into a set of segments.
33655     * (DASH SPEC Section 5.3.9.3.2) contains a set of <SegmentURL> nodes.  Each
33656     * node should be translated into segment.
33657     *
33658     * @param {Object} attributes
33659     *   Object containing all inherited attributes from parent elements with attribute
33660     *   names as keys
33661     * @return {Object.<Array>} list of segments
33662     */
33663  
33664  
33665    var segmentsFromBase = function segmentsFromBase(attributes) {
33666      var baseUrl = attributes.baseUrl,
33667          _attributes$initializ = attributes.initialization,
33668          initialization = _attributes$initializ === void 0 ? {} : _attributes$initializ,
33669          sourceDuration = attributes.sourceDuration,
33670          _attributes$timescale = attributes.timescale,
33671          timescale = _attributes$timescale === void 0 ? 1 : _attributes$timescale,
33672          _attributes$indexRang = attributes.indexRange,
33673          indexRange = _attributes$indexRang === void 0 ? '' : _attributes$indexRang,
33674          duration = attributes.duration; // base url is required for SegmentBase to work, per spec (Section 5.3.9.2.1)
33675  
33676      if (!baseUrl) {
33677        throw new Error(errors.NO_BASE_URL);
33678      }
33679  
33680      var initSegment = urlTypeToSegment({
33681        baseUrl: baseUrl,
33682        source: initialization.sourceURL,
33683        range: initialization.range
33684      });
33685      var segment = urlTypeToSegment({
33686        baseUrl: baseUrl,
33687        source: baseUrl,
33688        indexRange: indexRange
33689      });
33690      segment.map = initSegment; // If there is a duration, use it, otherwise use the given duration of the source
33691      // (since SegmentBase is only for one total segment)
33692  
33693      if (duration) {
33694        var segmentTimeInfo = parseByDuration(attributes);
33695  
33696        if (segmentTimeInfo.length) {
33697          segment.duration = segmentTimeInfo[0].duration;
33698          segment.timeline = segmentTimeInfo[0].timeline;
33699        }
33700      } else if (sourceDuration) {
33701        segment.duration = sourceDuration / timescale;
33702        segment.timeline = 0;
33703      } // This is used for mediaSequence
33704  
33705  
33706      segment.number = 0;
33707      return [segment];
33708    };
33709    /**
33710     * Given a playlist, a sidx box, and a baseUrl, update the segment list of the playlist
33711     * according to the sidx information given.
33712     *
33713     * playlist.sidx has metadadata about the sidx where-as the sidx param
33714     * is the parsed sidx box itself.
33715     *
33716     * @param {Object} playlist the playlist to update the sidx information for
33717     * @param {Object} sidx the parsed sidx box
33718     * @return {Object} the playlist object with the updated sidx information
33719     */
33720  
33721  
33722    var addSegmentsToPlaylist = function addSegmentsToPlaylist(playlist, sidx, baseUrl) {
33723      // Retain init segment information
33724      var initSegment = playlist.sidx.map ? playlist.sidx.map : null; // Retain source duration from initial master manifest parsing
33725  
33726      var sourceDuration = playlist.sidx.duration; // Retain source timeline
33727  
33728      var timeline = playlist.timeline || 0;
33729      var sidxByteRange = playlist.sidx.byterange;
33730      var sidxEnd = sidxByteRange.offset + sidxByteRange.length; // Retain timescale of the parsed sidx
33731  
33732      var timescale = sidx.timescale; // referenceType 1 refers to other sidx boxes
33733  
33734      var mediaReferences = sidx.references.filter(function (r) {
33735        return r.referenceType !== 1;
33736      });
33737      var segments = []; // firstOffset is the offset from the end of the sidx box
33738  
33739      var startIndex = sidxEnd + sidx.firstOffset;
33740  
33741      for (var i = 0; i < mediaReferences.length; i++) {
33742        var reference = sidx.references[i]; // size of the referenced (sub)segment
33743  
33744        var size = reference.referencedSize; // duration of the referenced (sub)segment, in  the  timescale
33745        // this will be converted to seconds when generating segments
33746  
33747        var duration = reference.subsegmentDuration; // should be an inclusive range
33748  
33749        var endIndex = startIndex + size - 1;
33750        var indexRange = startIndex + "-" + endIndex;
33751        var attributes = {
33752          baseUrl: baseUrl,
33753          timescale: timescale,
33754          timeline: timeline,
33755          // this is used in parseByDuration
33756          periodIndex: timeline,
33757          duration: duration,
33758          sourceDuration: sourceDuration,
33759          indexRange: indexRange
33760        };
33761        var segment = segmentsFromBase(attributes)[0];
33762  
33763        if (initSegment) {
33764          segment.map = initSegment;
33765        }
33766  
33767        segments.push(segment);
33768        startIndex += size;
33769      }
33770  
33771      playlist.segments = segments;
33772      return playlist;
33773    };
33774  
33775    var mergeDiscontiguousPlaylists = function mergeDiscontiguousPlaylists(playlists) {
33776      var mergedPlaylists = values(playlists.reduce(function (acc, playlist) {
33777        // assuming playlist IDs are the same across periods
33778        // TODO: handle multiperiod where representation sets are not the same
33779        // across periods
33780        var name = playlist.attributes.id + (playlist.attributes.lang || ''); // Periods after first
33781  
33782        if (acc[name]) {
33783          var _acc$name$segments; // first segment of subsequent periods signal a discontinuity
33784  
33785  
33786          if (playlist.segments[0]) {
33787            playlist.segments[0].discontinuity = true;
33788          }
33789  
33790          (_acc$name$segments = acc[name].segments).push.apply(_acc$name$segments, playlist.segments); // bubble up contentProtection, this assumes all DRM content
33791          // has the same contentProtection
33792  
33793  
33794          if (playlist.attributes.contentProtection) {
33795            acc[name].attributes.contentProtection = playlist.attributes.contentProtection;
33796          }
33797        } else {
33798          // first Period
33799          acc[name] = playlist;
33800        }
33801  
33802        return acc;
33803      }, {}));
33804      return mergedPlaylists.map(function (playlist) {
33805        playlist.discontinuityStarts = findIndexes(playlist.segments, 'discontinuity');
33806        return playlist;
33807      });
33808    };
33809  
33810    var addSegmentInfoFromSidx = function addSegmentInfoFromSidx(playlists, sidxMapping) {
33811      if (sidxMapping === void 0) {
33812        sidxMapping = {};
33813      }
33814  
33815      if (!Object.keys(sidxMapping).length) {
33816        return playlists;
33817      }
33818  
33819      for (var i in playlists) {
33820        var playlist = playlists[i];
33821  
33822        if (!playlist.sidx) {
33823          continue;
33824        }
33825  
33826        var sidxKey = playlist.sidx.uri + '-' + byteRangeToString(playlist.sidx.byterange);
33827        var sidxMatch = sidxMapping[sidxKey] && sidxMapping[sidxKey].sidx;
33828  
33829        if (playlist.sidx && sidxMatch) {
33830          addSegmentsToPlaylist(playlist, sidxMatch, playlist.sidx.resolvedUri);
33831        }
33832      }
33833  
33834      return playlists;
33835    };
33836  
33837    var formatAudioPlaylist = function formatAudioPlaylist(_ref) {
33838      var _attributes;
33839  
33840      var attributes = _ref.attributes,
33841          segments = _ref.segments,
33842          sidx = _ref.sidx;
33843      var playlist = {
33844        attributes: (_attributes = {
33845          NAME: attributes.id,
33846          BANDWIDTH: attributes.bandwidth,
33847          CODECS: attributes.codecs
33848        }, _attributes['PROGRAM-ID'] = 1, _attributes),
33849        uri: '',
33850        endList: (attributes.type || 'static') === 'static',
33851        timeline: attributes.periodIndex,
33852        resolvedUri: '',
33853        targetDuration: attributes.duration,
33854        segments: segments,
33855        mediaSequence: segments.length ? segments[0].number : 1
33856      };
33857  
33858      if (attributes.contentProtection) {
33859        playlist.contentProtection = attributes.contentProtection;
33860      }
33861  
33862      if (sidx) {
33863        playlist.sidx = sidx;
33864      }
33865  
33866      return playlist;
33867    };
33868  
33869    var formatVttPlaylist = function formatVttPlaylist(_ref2) {
33870      var _attributes2;
33871  
33872      var attributes = _ref2.attributes,
33873          segments = _ref2.segments;
33874  
33875      if (typeof segments === 'undefined') {
33876        // vtt tracks may use single file in BaseURL
33877        segments = [{
33878          uri: attributes.baseUrl,
33879          timeline: attributes.periodIndex,
33880          resolvedUri: attributes.baseUrl || '',
33881          duration: attributes.sourceDuration,
33882          number: 0
33883        }]; // targetDuration should be the same duration as the only segment
33884  
33885        attributes.duration = attributes.sourceDuration;
33886      }
33887  
33888      return {
33889        attributes: (_attributes2 = {
33890          NAME: attributes.id,
33891          BANDWIDTH: attributes.bandwidth
33892        }, _attributes2['PROGRAM-ID'] = 1, _attributes2),
33893        uri: '',
33894        endList: (attributes.type || 'static') === 'static',
33895        timeline: attributes.periodIndex,
33896        resolvedUri: attributes.baseUrl || '',
33897        targetDuration: attributes.duration,
33898        segments: segments,
33899        mediaSequence: segments.length ? segments[0].number : 1
33900      };
33901    };
33902  
33903    var organizeAudioPlaylists = function organizeAudioPlaylists(playlists, sidxMapping) {
33904      if (sidxMapping === void 0) {
33905        sidxMapping = {};
33906      }
33907  
33908      var mainPlaylist;
33909      var formattedPlaylists = playlists.reduce(function (a, playlist) {
33910        var role = playlist.attributes.role && playlist.attributes.role.value || '';
33911        var language = playlist.attributes.lang || '';
33912        var label = 'main';
33913  
33914        if (language) {
33915          var roleLabel = role ? " (" + role + ")" : '';
33916          label = "" + playlist.attributes.lang + roleLabel;
33917        } // skip if we already have the highest quality audio for a language
33918  
33919  
33920        if (a[label] && a[label].playlists[0].attributes.BANDWIDTH > playlist.attributes.bandwidth) {
33921          return a;
33922        }
33923  
33924        a[label] = {
33925          language: language,
33926          autoselect: true,
33927          "default": role === 'main',
33928          playlists: addSegmentInfoFromSidx([formatAudioPlaylist(playlist)], sidxMapping),
33929          uri: ''
33930        };
33931  
33932        if (typeof mainPlaylist === 'undefined' && role === 'main') {
33933          mainPlaylist = playlist;
33934          mainPlaylist["default"] = true;
33935        }
33936  
33937        return a;
33938      }, {}); // if no playlists have role "main", mark the first as main
33939  
33940      if (!mainPlaylist) {
33941        var firstLabel = Object.keys(formattedPlaylists)[0];
33942        formattedPlaylists[firstLabel]["default"] = true;
33943      }
33944  
33945      return formattedPlaylists;
33946    };
33947  
33948    var organizeVttPlaylists = function organizeVttPlaylists(playlists, sidxMapping) {
33949      if (sidxMapping === void 0) {
33950        sidxMapping = {};
33951      }
33952  
33953      return playlists.reduce(function (a, playlist) {
33954        var label = playlist.attributes.lang || 'text'; // skip if we already have subtitles
33955  
33956        if (a[label]) {
33957          return a;
33958        }
33959  
33960        a[label] = {
33961          language: label,
33962          "default": false,
33963          autoselect: false,
33964          playlists: addSegmentInfoFromSidx([formatVttPlaylist(playlist)], sidxMapping),
33965          uri: ''
33966        };
33967        return a;
33968      }, {});
33969    };
33970  
33971    var formatVideoPlaylist = function formatVideoPlaylist(_ref3) {
33972      var _attributes3;
33973  
33974      var attributes = _ref3.attributes,
33975          segments = _ref3.segments,
33976          sidx = _ref3.sidx;
33977      var playlist = {
33978        attributes: (_attributes3 = {
33979          NAME: attributes.id,
33980          AUDIO: 'audio',
33981          SUBTITLES: 'subs',
33982          RESOLUTION: {
33983            width: attributes.width,
33984            height: attributes.height
33985          },
33986          CODECS: attributes.codecs,
33987          BANDWIDTH: attributes.bandwidth
33988        }, _attributes3['PROGRAM-ID'] = 1, _attributes3),
33989        uri: '',
33990        endList: (attributes.type || 'static') === 'static',
33991        timeline: attributes.periodIndex,
33992        resolvedUri: '',
33993        targetDuration: attributes.duration,
33994        segments: segments,
33995        mediaSequence: segments.length ? segments[0].number : 1
33996      };
33997  
33998      if (attributes.contentProtection) {
33999        playlist.contentProtection = attributes.contentProtection;
34000      }
34001  
34002      if (sidx) {
34003        playlist.sidx = sidx;
34004      }
34005  
34006      return playlist;
34007    };
34008  
34009    var toM3u8 = function toM3u8(dashPlaylists, sidxMapping) {
34010      var _mediaGroups;
34011  
34012      if (sidxMapping === void 0) {
34013        sidxMapping = {};
34014      }
34015  
34016      if (!dashPlaylists.length) {
34017        return {};
34018      } // grab all master attributes
34019  
34020  
34021      var _dashPlaylists$0$attr = dashPlaylists[0].attributes,
34022          duration = _dashPlaylists$0$attr.sourceDuration,
34023          _dashPlaylists$0$attr2 = _dashPlaylists$0$attr.type,
34024          type = _dashPlaylists$0$attr2 === void 0 ? 'static' : _dashPlaylists$0$attr2,
34025          suggestedPresentationDelay = _dashPlaylists$0$attr.suggestedPresentationDelay,
34026          _dashPlaylists$0$attr3 = _dashPlaylists$0$attr.minimumUpdatePeriod,
34027          minimumUpdatePeriod = _dashPlaylists$0$attr3 === void 0 ? 0 : _dashPlaylists$0$attr3;
34028  
34029      var videoOnly = function videoOnly(_ref4) {
34030        var attributes = _ref4.attributes;
34031        return attributes.mimeType === 'video/mp4' || attributes.contentType === 'video';
34032      };
34033  
34034      var audioOnly = function audioOnly(_ref5) {
34035        var attributes = _ref5.attributes;
34036        return attrib
vendor: 14,003 bytes, lines 34036-34398
34036utes.mimeType === 'audio/mp4' || attributes.contentType === 'audio';
34037      };
34038  
34039      var vttOnly = function vttOnly(_ref6) {
34040        var attributes = _ref6.attributes;
34041        return attributes.mimeType === 'text/vtt' || attributes.contentType === 'text';
34042      };
34043  
34044      var videoPlaylists = mergeDiscontiguousPlaylists(dashPlaylists.filter(videoOnly)).map(formatVideoPlaylist);
34045      var audioPlaylists = mergeDiscontiguousPlaylists(dashPlaylists.filter(audioOnly));
34046      var vttPlaylists = dashPlaylists.filter(vttOnly);
34047      var master = {
34048        allowCache: true,
34049        discontinuityStarts: [],
34050        segments: [],
34051        endList: true,
34052        mediaGroups: (_mediaGroups = {
34053          AUDIO: {},
34054          VIDEO: {}
34055        }, _mediaGroups['CLOSED-CAPTIONS'] = {}, _mediaGroups.SUBTITLES = {}, _mediaGroups),
34056        uri: '',
34057        duration: duration,
34058        playlists: addSegmentInfoFromSidx(videoPlaylists, sidxMapping),
34059        minimumUpdatePeriod: minimumUpdatePeriod * 1000
34060      };
34061  
34062      if (type === 'dynamic') {
34063        master.suggestedPresentationDelay = suggestedPresentationDelay;
34064      }
34065  
34066      if (audioPlaylists.length) {
34067        master.mediaGroups.AUDIO.audio = organizeAudioPlaylists(audioPlaylists, sidxMapping);
34068      }
34069  
34070      if (vttPlaylists.length) {
34071        master.mediaGroups.SUBTITLES.subs = organizeVttPlaylists(vttPlaylists, sidxMapping);
34072      }
34073  
34074      return master;
34075    };
34076    /**
34077     * Calculates the R (repetition) value for a live stream (for the final segment
34078     * in a manifest where the r value is negative 1)
34079     *
34080     * @param {Object} attributes
34081     *        Object containing all inherited attributes from parent elements with attribute
34082     *        names as keys
34083     * @param {number} time
34084     *        current time (typically the total time up until the final segment)
34085     * @param {number} duration
34086     *        duration property for the given <S />
34087     *
34088     * @return {number}
34089     *        R value to reach the end of the given period
34090     */
34091  
34092  
34093    var getLiveRValue = function getLiveRValue(attributes, time, duration) {
34094      var NOW = attributes.NOW,
34095          clientOffset = attributes.clientOffset,
34096          availabilityStartTime = attributes.availabilityStartTime,
34097          _attributes$timescale = attributes.timescale,
34098          timescale = _attributes$timescale === void 0 ? 1 : _attributes$timescale,
34099          _attributes$start = attributes.start,
34100          start = _attributes$start === void 0 ? 0 : _attributes$start,
34101          _attributes$minimumUp = attributes.minimumUpdatePeriod,
34102          minimumUpdatePeriod = _attributes$minimumUp === void 0 ? 0 : _attributes$minimumUp;
34103      var now = (NOW + clientOffset) / 1000;
34104      var periodStartWC = availabilityStartTime + start;
34105      var periodEndWC = now + minimumUpdatePeriod;
34106      var periodDuration = periodEndWC - periodStartWC;
34107      return Math.ceil((periodDuration * timescale - time) / duration);
34108    };
34109    /**
34110     * Uses information provided by SegmentTemplate.SegmentTimeline to determine segment
34111     * timing and duration
34112     *
34113     * @param {Object} attributes
34114     *        Object containing all inherited attributes from parent elements with attribute
34115     *        names as keys
34116     * @param {Object[]} segmentTimeline
34117     *        List of objects representing the attributes of each S element contained within
34118     *
34119     * @return {{number: number, duration: number, time: number, timeline: number}[]}
34120     *         List of Objects with segment timing and duration info
34121     */
34122  
34123  
34124    var parseByTimeline = function parseByTimeline(attributes, segmentTimeline) {
34125      var _attributes$type = attributes.type,
34126          type = _attributes$type === void 0 ? 'static' : _attributes$type,
34127          _attributes$minimumUp2 = attributes.minimumUpdatePeriod,
34128          minimumUpdatePeriod = _attributes$minimumUp2 === void 0 ? 0 : _attributes$minimumUp2,
34129          _attributes$media = attributes.media,
34130          media = _attributes$media === void 0 ? '' : _attributes$media,
34131          sourceDuration = attributes.sourceDuration,
34132          _attributes$timescale2 = attributes.timescale,
34133          timescale = _attributes$timescale2 === void 0 ? 1 : _attributes$timescale2,
34134          _attributes$startNumb = attributes.startNumber,
34135          startNumber = _attributes$startNumb === void 0 ? 1 : _attributes$startNumb,
34136          timeline = attributes.periodIndex;
34137      var segments = [];
34138      var time = -1;
34139  
34140      for (var sIndex = 0; sIndex < segmentTimeline.length; sIndex++) {
34141        var S = segmentTimeline[sIndex];
34142        var duration = S.d;
34143        var repeat = S.r || 0;
34144        var segmentTime = S.t || 0;
34145  
34146        if (time < 0) {
34147          // first segment
34148          time = segmentTime;
34149        }
34150  
34151        if (segmentTime && segmentTime > time) {
34152          // discontinuity
34153          // TODO: How to handle this type of discontinuity
34154          // timeline++ here would treat it like HLS discontuity and content would
34155          // get appended without gap
34156          // E.G.
34157          //  <S t="0" d="1" />
34158          //  <S d="1" />
34159          //  <S d="1" />
34160          //  <S t="5" d="1" />
34161          // would have $Time$ values of [0, 1, 2, 5]
34162          // should this be appened at time positions [0, 1, 2, 3],(#EXT-X-DISCONTINUITY)
34163          // or [0, 1, 2, gap, gap, 5]? (#EXT-X-GAP)
34164          // does the value of sourceDuration consider this when calculating arbitrary
34165          // negative @r repeat value?
34166          // E.G. Same elements as above with this added at the end
34167          //  <S d="1" r="-1" />
34168          //  with a sourceDuration of 10
34169          // Would the 2 gaps be included in the time duration calculations resulting in
34170          // 8 segments with $Time$ values of [0, 1, 2, 5, 6, 7, 8, 9] or 10 segments
34171          // with $Time$ values of [0, 1, 2, 5, 6, 7, 8, 9, 10, 11] ?
34172          time = segmentTime;
34173        }
34174  
34175        var count = void 0;
34176  
34177        if (repeat < 0) {
34178          var nextS = sIndex + 1;
34179  
34180          if (nextS === segmentTimeline.length) {
34181            // last segment
34182            if (type === 'dynamic' && minimumUpdatePeriod > 0 && media.indexOf('$Number$') > 0) {
34183              count = getLiveRValue(attributes, time, duration);
34184            } else {
34185              // TODO: This may be incorrect depending on conclusion of TODO above
34186              count = (sourceDuration * timescale - time) / duration;
34187            }
34188          } else {
34189            count = (segmentTimeline[nextS].t - time) / duration;
34190          }
34191        } else {
34192          count = repeat + 1;
34193        }
34194  
34195        var end = startNumber + segments.length + count;
34196        var number = startNumber + segments.length;
34197  
34198        while (number < end) {
34199          segments.push({
34200            number: number,
34201            duration: duration / timescale,
34202            time: time,
34203            timeline: timeline
34204          });
34205          time += duration;
34206          number++;
34207        }
34208      }
34209  
34210      return segments;
34211    };
34212  
34213    var identifierPattern = /\$([A-z]*)(?:(%0)([0-9]+)d)?\$/g;
34214    /**
34215     * Replaces template identifiers with corresponding values. To be used as the callback
34216     * for String.prototype.replace
34217     *
34218     * @name replaceCallback
34219     * @function
34220     * @param {string} match
34221     *        Entire match of identifier
34222     * @param {string} identifier
34223     *        Name of matched identifier
34224     * @param {string} format
34225     *        Format tag string. Its presence indicates that padding is expected
34226     * @param {string} width
34227     *        Desired length of the replaced value. Values less than this width shall be left
34228     *        zero padded
34229     * @return {string}
34230     *         Replacement for the matched identifier
34231     */
34232  
34233    /**
34234     * Returns a function to be used as a callback for String.prototype.replace to replace
34235     * template identifiers
34236     *
34237     * @param {Obect} values
34238     *        Object containing values that shall be used to replace known identifiers
34239     * @param {number} values.RepresentationID
34240     *        Value of the Representation@id attribute
34241     * @param {number} values.Number
34242     *        Number of the corresponding segment
34243     * @param {number} values.Bandwidth
34244     *        Value of the Representation@bandwidth attribute.
34245     * @param {number} values.Time
34246     *        Timestamp value of the corresponding segment
34247     * @return {replaceCallback}
34248     *         Callback to be used with String.prototype.replace to replace identifiers
34249     */
34250  
34251    var identifierReplacement = function identifierReplacement(values) {
34252      return function (match, identifier, format, width) {
34253        if (match === '$$') {
34254          // escape sequence
34255          return '$';
34256        }
34257  
34258        if (typeof values[identifier] === 'undefined') {
34259          return match;
34260        }
34261  
34262        var value = '' + values[identifier];
34263  
34264        if (identifier === 'RepresentationID') {
34265          // Format tag shall not be present with RepresentationID
34266          return value;
34267        }
34268  
34269        if (!format) {
34270          width = 1;
34271        } else {
34272          width = parseInt(width, 10);
34273        }
34274  
34275        if (value.length >= width) {
34276          return value;
34277        }
34278  
34279        return "" + new Array(width - value.length + 1).join('0') + value;
34280      };
34281    };
34282    /**
34283     * Constructs a segment url from a template string
34284     *
34285     * @param {string} url
34286     *        Template string to construct url from
34287     * @param {Obect} values
34288     *        Object containing values that shall be used to replace known identifiers
34289     * @param {number} values.RepresentationID
34290     *        Value of the Representation@id attribute
34291     * @param {number} values.Number
34292     *        Number of the corresponding segment
34293     * @param {number} values.Bandwidth
34294     *        Value of the Representation@bandwidth attribute.
34295     * @param {number} values.Time
34296     *        Timestamp value of the corresponding segment
34297     * @return {string}
34298     *         Segment url with identifiers replaced
34299     */
34300  
34301  
34302    var constructTemplateUrl = function constructTemplateUrl(url, values) {
34303      return url.replace(identifierPattern, identifierReplacement(values));
34304    };
34305    /**
34306     * Generates a list of objects containing timing and duration information about each
34307     * segment needed to generate segment uris and the complete segment object
34308     *
34309     * @param {Object} attributes
34310     *        Object containing all inherited attributes from parent elements with attribute
34311     *        names as keys
34312     * @param {Object[]|undefined} segmentTimeline
34313     *        List of objects representing the attributes of each S element contained within
34314     *        the SegmentTimeline element
34315     * @return {{number: number, duration: number, time: number, timeline: number}[]}
34316     *         List of Objects with segment timing and duration info
34317     */
34318  
34319  
34320    var parseTemplateInfo = function parseTemplateInfo(attributes, segmentTimeline) {
34321      if (!attributes.duration && !segmentTimeline) {
34322        // if neither @duration or SegmentTimeline are present, then there shall be exactly
34323        // one media segment
34324        return [{
34325          number: attributes.startNumber || 1,
34326          duration: attributes.sourceDuration,
34327          time: 0,
34328          timeline: attributes.periodIndex
34329        }];
34330      }
34331  
34332      if (attributes.duration) {
34333        return parseByDuration(attributes);
34334      }
34335  
34336      return parseByTimeline(attributes, segmentTimeline);
34337    };
34338    /**
34339     * Generates a list of segments using information provided by the SegmentTemplate element
34340     *
34341     * @param {Object} attributes
34342     *        Object containing all inherited attributes from parent elements with attribute
34343     *        names as keys
34344     * @param {Object[]|undefined} segmentTimeline
34345     *        List of objects representing the attributes of each S element contained within
34346     *        the SegmentTimeline element
34347     * @return {Object[]}
34348     *         List of segment objects
34349     */
34350  
34351  
34352    var segmentsFromTemplate = function segmentsFromTemplate(attributes, segmentTimeline) {
34353      var templateValues = {
34354        RepresentationID: attributes.id,
34355        Bandwidth: attributes.bandwidth || 0
34356      };
34357      var _attributes$initializ = attributes.initialization,
34358          initialization = _attributes$initializ === void 0 ? {
34359        sourceURL: '',
34360        range: ''
34361      } : _attributes$initializ;
34362      var mapSegment = urlTypeToSegment({
34363        baseUrl: attributes.baseUrl,
34364        source: constructTemplateUrl(initialization.sourceURL, templateValues),
34365        range: initialization.range
34366      });
34367      var segments = parseTemplateInfo(attributes, segmentTimeline);
34368      return segments.map(function (segment) {
34369        templateValues.Number = segment.number;
34370        templateValues.Time = segment.time;
34371        var uri = constructTemplateUrl(attributes.media || '', templateValues);
34372        return {
34373          uri: uri,
34374          timeline: segment.timeline,
34375          duration: segment.duration,
34376          resolvedUri: resolveUrl_1(attributes.baseUrl || '', uri),
34377          map: mapSegment,
34378          number: segment.number
34379        };
34380      });
34381    };
34382    /**
34383     * Converts a <SegmentUrl> (of type URLType from the DASH spec 5.3.9.2 Table 14)
34384     * to an object that matches the output of a segment in videojs/mpd-parser
34385     *
34386     * @param {Object} attributes
34387     *   Object containing all inherited attributes from parent elements with attribute
34388     *   names as keys
34389     * @param {Object} segmentUrl
34390     *   <SegmentURL> node to translate into a segment object
34391     * @return {Object} translated segment object
34392     */
34393  
34394  
34395    var SegmentURLToSegmentObject = function SegmentURLToSegmentObject(attributes, segmentUrl) {
34396      var baseUrl = attributes.baseUrl,
34397          _attributes$initializ = attributes.initialization,
34398          initialization = _attributes$initializ === void 0 ? {}
34398 : _attributes$initializ;
34399      var initSegment = urlTypeToSegment({
34400        baseUrl: baseUrl,
34401        source: initialization.sourceURL,
34402        range: initialization.range
34403      });
34404      var segment = urlTypeToSegment({
34405        baseUrl: baseUrl,
34406        source: segmentUrl.media,
34407        range: segmentUrl.mediaRange
34408      });
34409      segment.map = initSegment;
34410      return segment;
34411    };
34412    /**
34413     * Generates a list of segments using information provided by the SegmentList element
34414     * SegmentList (DASH SPEC Section 5.3.9.3.2) contains a set of <SegmentURL> nodes.  Each
34415     * node should be translated into segment.
34416     *
34417     * @param {Object} attributes
34418     *   Object containing all inherited attributes from parent elements with attribute
34419     *   names as keys
34420     * @param {Object[]|undefined} segmentTimeline
34421     *        List of objects representing the attributes of each S element contained within
34422     *        the SegmentTimeline element
34423     * @return {Object.<Array>} list of segments
34424     */
34425  
34426  
34427    var segmentsFromList = function segmentsFromList(attributes, segmentTimeline) {
34428      var duration = attributes.duration,
34429          _attributes$segmentUr = attributes.segmentUrls,
34430          segmentUrls = _attributes$segmentUr === void 0 ? [] : _attributes$segmentUr; // Per spec (5.3.9.2.1) no way to determine segment duration OR
34431      // if both SegmentTimeline and @duration are defined, it is outside of spec.
34432  
34433      if (!duration && !segmentTimeline || duration && segmentTimeline) {
34434        throw new Error(errors.SEGMENT_TIME_UNSPECIFIED);
34435      }
34436  
34437      var segmentUrlMap = segmentUrls.map(function (segmentUrlObject) {
34438        return SegmentURLToSegmentObject(attributes, segmentUrlObject);
34439      });
34440      var segmentTimeInfo;
34441  
34442      if (duration) {
34443        segmentTimeInfo = parseByDuration(attributes);
34444      }
34445  
34446      if (segmentTimeline) {
34447        segmentTimeInfo = parseByTimeline(attributes, segmentTimeline);
34448      }
34449  
34450      var segments = segmentTimeInfo.map(function (segmentTime, index) {
34451        if (segmentUrlMap[index]) {
34452          var segment = segmentUrlMap[index];
34453          segment.timeline = segmentTime.timeline;
34454          segment.duration = segmentTime.duration;
34455          segment.number = segmentTime.number;
34456          return segment;
34457        } // Since we're mapping we should get rid of any blank segments (in case
34458        // the given SegmentTimeline is handling for more elements than we have
34459        // SegmentURLs for).
34460  
34461      }).filter(function (segment) {
34462        return segment;
34463      });
34464      return segments;
34465    };
34466  
34467    var generateSegments = function generateSegments(_ref) {
34468      var attributes = _ref.attributes,
34469          segmentInfo = _ref.segmentInfo;
34470      var segmentAttributes;
34471      var segmentsFn;
34472  
34473      if (segmentInfo.template) {
34474        segmentsFn = segmentsFromTemplate;
34475        segmentAttributes = merge(attributes, segmentInfo.template);
34476      } else if (segmentInfo.base) {
34477        segmentsFn = segmentsFromBase;
34478        segmentAttributes = merge(attributes, segmentInfo.base);
34479      } else if (segmentInfo.list) {
34480        segmentsFn = segmentsFromList;
34481        segmentAttributes = merge(attributes, segmentInfo.list);
34482      }
34483  
34484      var segmentsInfo = {
34485        attributes: attributes
34486      };
34487  
34488      if (!segmentsFn) {
34489        return segmentsInfo;
34490      }
34491  
34492      var segments = segmentsFn(segmentAttributes, segmentInfo.timeline); // The @duration attribute will be used to determin the playlist's targetDuration which
34493      // must be in seconds. Since we've generated the segment list, we no longer need
34494      // @duration to be in @timescale units, so we can convert it here.
34495  
34496      if (segmentAttributes.duration) {
34497        var _segmentAttributes = segmentAttributes,
34498            duration = _segmentAttributes.duration,
34499            _segmentAttributes$ti = _segmentAttributes.timescale,
34500            timescale = _segmentAttributes$ti === void 0 ? 1 : _segmentAttributes$ti;
34501        segmentAttributes.duration = duration / timescale;
34502      } else if (segments.length) {
34503        // if there is no @duration attribute, use the largest segment duration as
34504        // as target duration
34505        segmentAttributes.duration = segments.reduce(function (max, segment) {
34506          return Math.max(max, Math.ceil(segment.duration));
34507        }, 0);
34508      } else {
34509        segmentAttributes.duration = 0;
34510      }
34511  
34512      segmentsInfo.attributes = segmentAttributes;
34513      segmentsInfo.segments = segments; // This is a sidx box without actual segment information
34514  
34515      if (segmentInfo.base && segmentAttributes.indexRange) {
34516        segmentsInfo.sidx = segments[0];
34517        segmentsInfo.segments = [];
34518      }
34519  
34520      return segmentsInfo;
34521    };
34522  
34523    var toPlaylists = function toPlaylists(representations) {
34524      return representations.map(generateSegments);
34525    };
34526  
34527    var findChildren = function findChildren(element, name) {
34528      return from(element.childNodes).filter(function (_ref) {
34529        var tagName = _ref.tagName;
34530        return tagName === name;
34531      });
34532    };
34533  
34534    var getContent = function getContent(element) {
34535      return element.textContent.trim();
34536    };
34537  
34538    var parseDuration = function parseDuration(str) {
34539      var SECONDS_IN_YEAR = 365 * 24 * 60 * 60;
34540      var SECONDS_IN_MONTH = 30 * 24 * 60 * 60;
34541      var SECONDS_IN_DAY = 24 * 60 * 60;
34542      var SECONDS_IN_HOUR = 60 * 60;
34543      var SECONDS_IN_MIN = 60; // P10Y10M10DT10H10M10.1S
34544  
34545      var durationRegex = /P(?:(\d*)Y)?(?:(\d*)M)?(?:(\d*)D)?(?:T(?:(\d*)H)?(?:(\d*)M)?(?:([\d.]*)S)?)?/;
34546      var match = durationRegex.exec(str);
34547  
34548      if (!match) {
34549        return 0;
34550      }
34551  
34552      var _match$slice = match.slice(1),
34553          year = _match$slice[0],
vendor: 13,483 bytes, lines 34554-34938
34554          month = _match$slice[1],
34555          day = _match$slice[2],
34556          hour = _match$slice[3],
34557          minute = _match$slice[4],
34558          second = _match$slice[5];
34559  
34560      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);
34561    };
34562  
34563    var parseDate = function parseDate(str) {
34564      // Date format without timezone according to ISO 8601
34565      // YYY-MM-DDThh:mm:ss.ssssss
34566      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
34567      // expressed by ending with 'Z'
34568  
34569      if (dateRegex.test(str)) {
34570        str += 'Z';
34571      }
34572  
34573      return Date.parse(str);
34574    };
34575  
34576    var parsers = {
34577      /**
34578       * Specifies the duration of the entire Media Presentation. Format is a duration string
34579       * as specified in ISO 8601
34580       *
34581       * @param {string} value
34582       *        value of attribute as a string
34583       * @return {number}
34584       *         The duration in seconds
34585       */
34586      mediaPresentationDuration: function mediaPresentationDuration(value) {
34587        return parseDuration(value);
34588      },
34589  
34590      /**
34591       * Specifies the Segment availability start time for all Segments referred to in this
34592       * MPD. For a dynamic manifest, it specifies the anchor for the earliest availability
34593       * time. Format is a date string as specified in ISO 8601
34594       *
34595       * @param {string} value
34596       *        value of attribute as a string
34597       * @return {number}
34598       *         The date as seconds from unix epoch
34599       */
34600      availabilityStartTime: function availabilityStartTime(value) {
34601        return parseDate(value) / 1000;
34602      },
34603  
34604      /**
34605       * Specifies the smallest period between potential changes to the MPD. Format is a
34606       * duration string as specified in ISO 8601
34607       *
34608       * @param {string} value
34609       *        value of attribute as a string
34610       * @return {number}
34611       *         The duration in seconds
34612       */
34613      minimumUpdatePeriod: function minimumUpdatePeriod(value) {
34614        return parseDuration(value);
34615      },
34616  
34617      /**
34618       * Specifies the suggested presentation delay. Format is a
34619       * duration string as specified in ISO 8601
34620       *
34621       * @param {string} value
34622       *        value of attribute as a string
34623       * @return {number}
34624       *         The duration in seconds
34625       */
34626      suggestedPresentationDelay: function suggestedPresentationDelay(value) {
34627        return parseDuration(value);
34628      },
34629  
34630      /**
34631       * specifices the type of mpd. Can be either "static" or "dynamic"
34632       *
34633       * @param {string} value
34634       *        value of attribute as a string
34635       *
34636       * @return {string}
34637       *         The type as a string
34638       */
34639      type: function type(value) {
34640        return value;
34641      },
34642  
34643      /**
34644       * Specifies the duration of the smallest time shifting buffer for any Representation
34645       * in the MPD. Format is a duration string as specified in ISO 8601
34646       *
34647       * @param {string} value
34648       *        value of attribute as a string
34649       * @return {number}
34650       *         The duration in seconds
34651       */
34652      timeShiftBufferDepth: function timeShiftBufferDepth(value) {
34653        return parseDuration(value);
34654      },
34655  
34656      /**
34657       * Specifies the PeriodStart time of the Period relative to the availabilityStarttime.
34658       * Format is a duration string as specified in ISO 8601
34659       *
34660       * @param {string} value
34661       *        value of attribute as a string
34662       * @return {number}
34663       *         The duration in seconds
34664       */
34665      start: function start(value) {
34666        return parseDuration(value);
34667      },
34668  
34669      /**
34670       * Specifies the width of the visual presentation
34671       *
34672       * @param {string} value
34673       *        value of attribute as a string
34674       * @return {number}
34675       *         The parsed width
34676       */
34677      width: function width(value) {
34678        return parseInt(value, 10);
34679      },
34680  
34681      /**
34682       * Specifies the height of the visual presentation
34683       *
34684       * @param {string} value
34685       *        value of attribute as a string
34686       * @return {number}
34687       *         The parsed height
34688       */
34689      height: function height(value) {
34690        return parseInt(value, 10);
34691      },
34692  
34693      /**
34694       * Specifies the bitrate of the representation
34695       *
34696       * @param {string} value
34697       *        value of attribute as a string
34698       * @return {number}
34699       *         The parsed bandwidth
34700       */
34701      bandwidth: function bandwidth(value) {
34702        return parseInt(value, 10);
34703      },
34704  
34705      /**
34706       * Specifies the number of the first Media Segment in this Representation in the Period
34707       *
34708       * @param {string} value
34709       *        value of attribute as a string
34710       * @return {number}
34711       *         The parsed number
34712       */
34713      startNumber: function startNumber(value) {
34714        return parseInt(value, 10);
34715      },
34716  
34717      /**
34718       * Specifies the timescale in units per seconds
34719       *
34720       * @param {string} value
34721       *        value of attribute as a string
34722       * @return {number}
34723       *         The aprsed timescale
34724       */
34725      timescale: function timescale(value) {
34726        return parseInt(value, 10);
34727      },
34728  
34729      /**
34730       * Specifies the constant approximate Segment duration
34731       * NOTE: The <Period> element also contains an @duration attribute. This duration
34732       *       specifies the duration of the Period. This attribute is currently not
34733       *       supported by the rest of the parser, however we still check for it to prevent
34734       *       errors.
34735       *
34736       * @param {string} value
34737       *        value of attribute as a string
34738       * @return {number}
34739       *         The parsed duration
34740       */
34741      duration: function duration(value) {
34742        var parsedValue = parseInt(value, 10);
34743  
34744        if (isNaN(parsedValue)) {
34745          return parseDuration(value);
34746        }
34747  
34748        return parsedValue;
34749      },
34750  
34751      /**
34752       * Specifies the Segment duration, in units of the value of the @timescale.
34753       *
34754       * @param {string} value
34755       *        value of attribute as a string
34756       * @return {number}
34757       *         The parsed duration
34758       */
34759      d: function d(value) {
34760        return parseInt(value, 10);
34761      },
34762  
34763      /**
34764       * Specifies the MPD start time, in @timescale units, the first Segment in the series
34765       * starts relative to the beginning of the Period
34766       *
34767       * @param {string} value
34768       *        value of attribute as a string
34769       * @return {number}
34770       *         The parsed time
34771       */
34772      t: function t(value) {
34773        return parseInt(value, 10);
34774      },
34775  
34776      /**
34777       * Specifies the repeat count of the number of following contiguous Segments with the
34778       * same duration expressed by the value of @d
34779       *
34780       * @param {string} value
34781       *        value of attribute as a string
34782       * @return {number}
34783       *         The parsed number
34784       */
34785      r: function r(value) {
34786        return parseInt(value, 10);
34787      },
34788  
34789      /**
34790       * Default parser for all other attributes. Acts as a no-op and just returns the value
34791       * as a string
34792       *
34793       * @param {string} value
34794       *        value of attribute as a string
34795       * @return {string}
34796       *         Unparsed value
34797       */
34798      DEFAULT: function DEFAULT(value) {
34799        return value;
34800      }
34801    };
34802    /**
34803     * Gets all the attributes and values of the provided node, parses attributes with known
34804     * types, and returns an object with attribute names mapped to values.
34805     *
34806     * @param {Node} el
34807     *        The node to parse attributes from
34808     * @return {Object}
34809     *         Object with all attributes of el parsed
34810     */
34811  
34812    var parseAttributes$1 = function parseAttributes(el) {
34813      if (!(el && el.attributes)) {
34814        return {};
34815      }
34816  
34817      return from(el.attributes).reduce(function (a, e) {
34818        var parseFn = parsers[e.name] || parsers.DEFAULT;
34819        a[e.name] = parseFn(e.value);
34820        return a;
34821      }, {});
34822    };
34823  
34824    var keySystemsMap = {
34825      'urn:uuid:1077efec-c0b2-4d02-ace3-3c1e52e2fb4b': 'org.w3.clearkey',
34826      'urn:uuid:edef8ba9-79d6-4ace-a3c8-27dcd51d21ed': 'com.widevine.alpha',
34827      'urn:uuid:9a04f079-9840-4286-ab92-e65be0885f95': 'com.microsoft.playready',
34828      'urn:uuid:f239e769-efa3-4850-9c16-a903c6932efb': 'com.adobe.primetime'
34829    };
34830    /**
34831     * Builds a list of urls that is the product of the reference urls and BaseURL values
34832     *
34833     * @param {string[]} referenceUrls
34834     *        List of reference urls to resolve to
34835     * @param {Node[]} baseUrlElements
34836     *        List of BaseURL nodes from the mpd
34837     * @return {string[]}
34838     *         List of resolved urls
34839     */
34840  
34841    var buildBaseUrls = function buildBaseUrls(referenceUrls, baseUrlElements) {
34842      if (!baseUrlElements.length) {
34843        return referenceUrls;
34844      }
34845  
34846      return flatten(referenceUrls.map(function (reference) {
34847        return baseUrlElements.map(function (baseUrlElement) {
34848          return resolveUrl_1(reference, getContent(baseUrlElement));
34849        });
34850      }));
34851    };
34852    /**
34853     * Contains all Segment information for its containing AdaptationSet
34854     *
34855     * @typedef {Object} SegmentInformation
34856     * @property {Object|undefined} template
34857     *           Contains the attributes for the SegmentTemplate node
34858     * @property {Object[]|undefined} timeline
34859     *           Contains a list of atrributes for each S node within the SegmentTimeline node
34860     * @property {Object|undefined} list
34861     *           Contains the attributes for the SegmentList node
34862     * @property {Object|undefined} base
34863     *           Contains the attributes for the SegmentBase node
34864     */
34865  
34866    /**
34867     * Returns all available Segment information contained within the AdaptationSet node
34868     *
34869     * @param {Node} adaptationSet
34870     *        The AdaptationSet node to get Segment information from
34871     * @return {SegmentInformation}
34872     *         The Segment information contained within the provided AdaptationSet
34873     */
34874  
34875  
34876    var getSegmentInformation = function getSegmentInformation(adaptationSet) {
34877      var segmentTemplate = findChildren(adaptationSet, 'SegmentTemplate')[0];
34878      var segmentList = findChildren(adaptationSet, 'SegmentList')[0];
34879      var segmentUrls = segmentList && findChildren(segmentList, 'SegmentURL').map(function (s) {
34880        return merge({
34881          tag: 'SegmentURL'
34882        }, parseAttributes$1(s));
34883      });
34884      var segmentBase = findChildren(adaptationSet, 'SegmentBase')[0];
34885      var segmentTimelineParentNode = segmentList || segmentTemplate;
34886      var segmentTimeline = segmentTimelineParentNode && findChildren(segmentTimelineParentNode, 'SegmentTimeline')[0];
34887      var segmentInitializationParentNode = segmentList || segmentBase || segmentTemplate;
34888      var segmentInitialization = segmentInitializationParentNode && findChildren(segmentInitializationParentNode, 'Initialization')[0]; // SegmentTemplate is handled slightly differently, since it can have both
34889      // @initialization and an <Initialization> node.  @initialization can be templated,
34890      // while the node can have a url and range specified.  If the <SegmentTemplate> has
34891      // both @initialization and an <Initialization> subelement we opt to override with
34892      // the node, as this interaction is not defined in the spec.
34893  
34894      var template = segmentTemplate && parseAttributes$1(segmentTemplate);
34895  
34896      if (template && segmentInitialization) {
34897        template.initialization = segmentInitialization && parseAttributes$1(segmentInitialization);
34898      } else if (template && template.initialization) {
34899        // If it is @initialization we convert it to an object since this is the format that
34900        // later functions will rely on for the initialization segment.  This is only valid
34901        // for <SegmentTemplate>
34902        template.initialization = {
34903          sourceURL: template.initialization
34904        };
34905      }
34906  
34907      var segmentInfo = {
34908        template: template,
34909        timeline: segmentTimeline && findChildren(segmentTimeline, 'S').map(function (s) {
34910          return parseAttributes$1(s);
34911        }),
34912        list: segmentList && merge(parseAttributes$1(segmentList), {
34913          segmentUrls: segmentUrls,
34914          initialization: parseAttributes$1(segmentInitialization)
34915        }),
34916        base: segmentBase && merge(parseAttributes$1(segmentBase), {
34917          initialization: parseAttributes$1(segmentInitialization)
34918        })
34919      };
34920      Object.keys(segmentInfo).forEach(function (key) {
34921        if (!segmentInfo[key]) {
34922          delete segmentInfo[key];
34923        }
34924      });
34925      return segmentInfo;
34926    };
34927    /**
34928     * Contains Segment information and attributes needed to construct a Playlist object
34929     * from a Representation
34930     *
34931     * @typedef {Object} RepresentationInformation
34932     * @property {SegmentInformation} segmentInfo
34933     *           Segment information for this Representation
34934     * @property {Object} attributes
34935     *           Inherited attributes for this Representation
34936     */
34937  
34938    /**
34939     * Maps a Representation node to an object containing Segment information and attributes
34940     *
34941     * @name inheritBaseUrlsCallback
34942     * @function
34943     * @param {Node} representation
34944     *        Representation node from the mpd
34945     * @return {RepresentationInformation}
34946     *         Representation information needed to construct a Playlist object
34947     */
34948  
34949    /**
34950     * Returns a callback for Array.prototype.map for mapping Representation nodes to
34951     * Segment information and attributes using inherited BaseURL nodes.
34952     *
34953     * @param {Object} adaptationSetAttributes
34954     *        Contains attributes inherited by the AdaptationSet
34955     * @param {string[]} adaptationSetBaseUrls
34956     *        Contains list of resolved base urls inherited by the AdaptationSet
34957     * @param {SegmentInformation} adaptationSetSegmentInfo
34958     *        Contains Segment information for the AdaptationSet
34959     * @return {inheritBaseUrlsCallback}
34960     *         Callback map function
34961     */
34962  
34963  
34964    var inheritBaseUrls = function inheritBaseUrls(adaptationSetAttributes, adaptationSetBaseUrls, adaptationSetSegmentInfo) {
34965      return function (representation) {
34966        var repBaseUrlElements = findChildren(representation, 'BaseURL');
34967        var repBaseUrls = buildBaseUrls(adaptationSetBaseUrls, repBaseUrlElements);
34968        var attributes = merge(adaptationSetAttributes, parseAttributes$1(representation));
34969        var representationSegmentInfo = getSegmentInformation(representation);
34970        return repBaseUrls.map(function (baseUrl) {
34971          return {
34972            segmentInfo: merge(adaptationSetSegmentInfo, representationSegmentInfo),
34973            attributes: merge(attributes, {
34974              baseUrl: baseUrl
34975            })
34976          };
34977        });
34978      };
34979    };
34980    /**
34981     * Tranforms a series of content protection nodes to
34982     * an object containing pssh data by key system
34983     *
34984     * @param {Node[]} contentProtectionNodes
34985     *        Content protection nodes
34986     * @return {Object}
34987     *        Object containing pssh data by key system
34988     */
34989  
34990  
34991    var generateKeySystemInformation = function generateKeySystemInformation(contentProtectionNodes) {
34992      return contentProtectionNodes.reduce(function (acc, node) {
34993        var attributes = parseAttributes$1(node);
34994        var keySystem = keySystemsMap[attributes.schemeIdUri];
34995  
34996        if (keySystem) {
34997          acc[keySystem] = {
34998            attributes: attributes
34999          };
35000          var psshNode = findChildren(node, 'cenc:pssh')[0];
35001  
35002          if (psshNode) {
35003            var pssh = getContent(psshNode);
35004            var psshBuffer = pssh && decodeB64ToUint8Array_1(pssh);
35005            acc[keySystem].pssh = psshBuffer;
35006          }
35007        }
35008  
35009        return acc;
35010      }, {});
35011    };
35012    /**
35013     * Maps an AdaptationSet node to a list of Representation information objects
35014     *
35015     * @name toRepresentationsCallback
35016     * @function
35017     * @param {Node} adaptationSet
35018     *        AdaptationSet node from the mpd
35019     * @return {RepresentationInformation[]}
35020     *         List of objects containing Representaion information
35021     */
35022  
35023    /**
35024     * Returns a callback for Array.prototype.map for mapping AdaptationSet nodes to a list of
35025     * Representation information objects
35026     *
35027     * @param {Object} periodAttributes
35028     *        Contains attributes inherited by the Period
35029     * @param {string[]} periodBaseUrls
35030     *        Contains list of resolved base urls inherited by the Period
35031     * @param {string[]} periodSegmentInfo
35032     *        Contains Segment Information at the period level
35033     * @return {toRepresentationsCallback}
35034     *         Callback map function
35035     */
35036  
35037  
35038    var toRepresentations = function toRepresentations(periodAttributes, periodBaseUrls, periodSegmentInfo) {
35039      return function (adaptationSet) {
35040        var adaptationSetAttributes = parseAttributes$1(adaptationSet);
35041        var adaptationSetBaseUrls = buildBaseUrls(periodBaseUrls, findChildren(adaptationSet, 'BaseURL'));
35042        var role = findChildren(adaptationSet, 'Role')[0];
35043        var roleAttributes = {
35044          role: parseAttributes$1(role)
35045        };
35046        var attrs = merge(periodAttributes, adaptationSetAttributes, roleAttributes);
35047        var contentProtection = generateKeySystemInformation(findChildren(adaptationSet, 'ContentProtection'));
35048  
35049        if (Object.keys(contentProtection).length) {
35050          attrs = merge(attrs, {
35051            contentProtection: contentProtection
35052          });
35053        }
35054  
35055        var segmentInfo = getSegmentInformation(adaptationSet);
35056        var representations = findChildren(adaptationSet, 'Representation');
35057        var adaptationSetSegmentInfo = merge(periodSegmentInfo, segmentInfo);
35058        return flatten(representations.map(inheritBaseUrls(attrs, adaptationSetBaseUrls, adaptationSetSegmentInfo)));
35059      };
35060    };
35061    /**
35062     * Maps an Period node to a list of Representation inforamtion objects for all
35063     * AdaptationSet nodes contained within the Period
35064     *
35065     * @name toAdaptationSetsCallback
35066     * @function
35067     * @param {Node} period
35068     *        Period node from the mpd
35069     * @param {number} periodIndex
35070     *        Index of the Period within the mpd
35071     * @return {RepresentationInformation[]}
35072     *         List of objects containing Representaion information
35073     */
35074  
35075    /**
35076     * Returns a callback for Array.prototype.map for mapping Period nodes to a list of
35077     * Representation information objects
35078     *
35079     * @param {Object} mpdAttributes
35080     *        Contains attributes inherited by the mpd
35081     * @param {string[]} mpdBaseUrls
35082     *        Contains list of resolved base urls inherited by the mpd
35083     * @return {toAdaptationSetsCallback}
35084     *         Callback map function
35085     */
35086  
35087  
35088    var toAdaptationSets = function toAdaptationSets(mpdAttributes, mpdBaseUrls) {
35089      return function (period, index) {
35090        var periodBaseUrls = buildBaseUrls(mpdBaseUrls, findChildren(period, 'BaseURL'));
35091        var periodAtt = parseAttributes$1(period);
35092        var parsedPeriodId = parseInt(periodAtt.id, 10); // fallback to mapping index if Period@id is not a number
35093  
35094        var periodIndex = window$3.isNaN(parsedPeriodId) ? index : parsedPeriodId;
35095        var periodAttributes = merge(mpdAttributes, {
35096          periodIndex: periodIndex
35097        });
35098        var adaptationSets = findChildren(period, 'AdaptationSet');
35099        var periodSegmentInfo = getSegmentInformation(period);
35100        return flatten(adaptationSets.map(toRepresentations(periodAttributes, periodBaseUrls, periodSegmentInfo)));
35101      };
35102    };
35103    /**
35104     * Traverses the mpd xml tree to generate a list of Representation information objects
35105     * that have inherited attributes from parent nodes
35106     *
35107     * @param {Node} mpd
35108     *        The root node of the mpd
35109     * @param {Object} options
35110     *        Available options for inheritAttributes
35111     * @param {string} options.manifestUri
35112     *        The uri source of the mpd
35113     * @param {number} options.NOW
35114     *        Current time per DASH IOP.  Default is current time in ms since epoch
35115     * @param {number} options.clientOffset
35116     *        Client time difference from NOW (in milliseconds)
35117     * @return {RepresentationInformation[]}
35118     *         List of objects containing Representation information
35119     */
35120  
35121  
35122    var inheritAttributes = function inheritAttributes(mpd, options) {
35123      if (options === void 0) {
35124        options = {};
35125      }
35126  
35127      var _options = options,
35128          _options$manifestUri = _options.manifestUri,
35129          manifestUri = _options$manifestUri === void 0 ? '' : _options$manifestUri,
35130          _options$NOW = _options.NOW,
35131          NOW = _options$NOW === void 0 ? Date.now() : _options$NOW,
35132          _options$clientOffset = _options.clientOffset,
35133          clientOffset = _options$clientOffset === void 0 ? 0 : _options$clientOffset;
vendor: 7,437 bytes, lines 35134-35372
35134      var periods = findChildren(mpd, 'Period');
35135  
35136      if (!periods.length) {
35137        throw new Error(errors.INVALID_NUMBER_OF_PERIOD);
35138      }
35139  
35140      var mpdAttributes = parseAttributes$1(mpd);
35141      var mpdBaseUrls = buildBaseUrls([manifestUri], findChildren(mpd, 'BaseURL'));
35142      mpdAttributes.sourceDuration = mpdAttributes.mediaPresentationDuration || 0;
35143      mpdAttributes.NOW = NOW;
35144      mpdAttributes.clientOffset = clientOffset;
35145      return flatten(periods.map(toAdaptationSets(mpdAttributes, mpdBaseUrls)));
35146    };
35147  
35148    var stringToMpdXml = function stringToMpdXml(manifestString) {
35149      if (manifestString === '') {
35150        throw new Error(errors.DASH_EMPTY_MANIFEST);
35151      }
35152  
35153      var parser = new domParser_3();
35154      var xml = parser.parseFromString(manifestString, 'application/xml');
35155      var mpd = xml && xml.documentElement.tagName === 'MPD' ? xml.documentElement : null;
35156  
35157      if (!mpd || mpd && mpd.getElementsByTagName('parsererror').length > 0) {
35158        throw new Error(errors.DASH_INVALID_XML);
35159      }
35160  
35161      return mpd;
35162    };
35163    /**
35164     * Parses the manifest for a UTCTiming node, returning the nodes attributes if found
35165     *
35166     * @param {string} mpd
35167     *        XML string of the MPD manifest
35168     * @return {Object|null}
35169     *         Attributes of UTCTiming node specified in the manifest. Null if none found
35170     */
35171  
35172  
35173    var parseUTCTimingScheme = function parseUTCTimingScheme(mpd) {
35174      var UTCTimingNode = findChildren(mpd, 'UTCTiming')[0];
35175  
35176      if (!UTCTimingNode) {
35177        return null;
35178      }
35179  
35180      var attributes = parseAttributes$1(UTCTimingNode);
35181  
35182      switch (attributes.schemeIdUri) {
35183        case 'urn:mpeg:dash:utc:http-head:2014':
35184        case 'urn:mpeg:dash:utc:http-head:2012':
35185          attributes.method = 'HEAD';
35186          break;
35187  
35188        case 'urn:mpeg:dash:utc:http-xsdate:2014':
35189        case 'urn:mpeg:dash:utc:http-iso:2014':
35190        case 'urn:mpeg:dash:utc:http-xsdate:2012':
35191        case 'urn:mpeg:dash:utc:http-iso:2012':
35192          attributes.method = 'GET';
35193          break;
35194  
35195        case 'urn:mpeg:dash:utc:direct:2014':
35196        case 'urn:mpeg:dash:utc:direct:2012':
35197          attributes.method = 'DIRECT';
35198          attributes.value = Date.parse(attributes.value);
35199          break;
35200  
35201        case 'urn:mpeg:dash:utc:http-ntp:2014':
35202        case 'urn:mpeg:dash:utc:ntp:2014':
35203        case 'urn:mpeg:dash:utc:sntp:2014':
35204        default:
35205          throw new Error(errors.UNSUPPORTED_UTC_TIMING_SCHEME);
35206      }
35207  
35208      return attributes;
35209    };
35210  
35211    var parse = function parse(manifestString, options) {
35212      if (options === void 0) {
35213        options = {};
35214      }
35215  
35216      return toM3u8(toPlaylists(inheritAttributes(stringToMpdXml(manifestString), options)), options.sidxMapping);
35217    };
35218    /**
35219     * Parses the manifest for a UTCTiming node, returning the nodes attributes if found
35220     *
35221     * @param {string} manifestString
35222     *        XML string of the MPD manifest
35223     * @return {Object|null}
35224     *         Attributes of UTCTiming node specified in the manifest. Null if none found
35225     */
35226  
35227  
35228    var parseUTCTiming = function parseUTCTiming(manifestString) {
35229      return parseUTCTimingScheme(stringToMpdXml(manifestString));
35230    };
35231  
35232    /**
35233     * mux.js
35234     *
35235     * Copyright (c) Brightcove
35236     * Licensed Apache-2.0 https://github.com/videojs/mux.js/blob/master/LICENSE
35237     */
35238    var toUnsigned = function toUnsigned(value) {
35239      return value >>> 0;
35240    };
35241  
35242    var toHexString = function toHexString(value) {
35243      return ('00' + value.toString(16)).slice(-2);
35244    };
35245  
35246    var bin = {
35247      toUnsigned: toUnsigned,
35248      toHexString: toHexString
35249    };
35250  
35251    var inspectMp4,
35252        _textifyMp,
35253        toUnsigned$1 = bin.toUnsigned,
35254        parseMp4Date = function parseMp4Date(seconds) {
35255      return new Date(seconds * 1000 - 2082844800000);
35256    },
35257        parseSampleFlags = function parseSampleFlags(flags) {
35258      return {
35259        isLeading: (flags[0] & 0x0c) >>> 2,
35260        dependsOn: flags[0] & 0x03,
35261        isDependedOn: (flags[1] & 0xc0) >>> 6,
35262        hasRedundancy: (flags[1] & 0x30) >>> 4,
35263        paddingValue: (flags[1] & 0x0e) >>> 1,
35264        isNonSyncSample: flags[1] & 0x01,
35265        degradationPriority: flags[2] << 8 | flags[3]
35266      };
35267    },
35268  
35269    /**
35270     * Returns the string representation of an ASCII encoded four byte buffer.
35271     * @param buffer {Uint8Array} a four-byte buffer to translate
35272     * @return {string} the corresponding string
35273     */
35274    parseType = function parseType(buffer) {
35275      var result = '';
35276      result += String.fromCharCode(buffer[0]);
35277      result += String.fromCharCode(buffer[1]);
35278      result += String.fromCharCode(buffer[2]);
35279      result += String.fromCharCode(buffer[3]);
35280      return result;
35281    },
35282        // Find the data for a box specified by its path
35283    findBox = function findBox(data, path) {
35284      var results = [],
35285          i,
35286          size,
35287          type,
35288          end,
35289          subresults;
35290  
35291      if (!path.length) {
35292        // short-circuit the search for empty paths
35293        return null;
35294      }
35295  
35296      for (i = 0; i < data.byteLength;) {
35297        size = toUnsigned$1(data[i] << 24 | data[i + 1] << 16 | data[i + 2] << 8 | data[i + 3]);
35298        type = parseType(data.subarray(i + 4, i + 8));
35299        end = size > 1 ? i + size : data.byteLength;
35300  
35301        if (type === path[0]) {
35302          if (path.length === 1) {
35303            // this is the end of the path and we've found the box we were
35304            // looking for
35305            results.push(data.subarray(i + 8, end));
35306          } else {
35307            // recursively search for the next box along the path
35308            subresults = findBox(data.subarray(i + 8, end), path.slice(1));
35309  
35310            if (subresults.length) {
35311              results = results.concat(subresults);
35312            }
35313          }
35314        }
35315  
35316        i = end;
35317      } // we've finished searching all of data
35318  
35319  
35320      return results;
35321    },
35322        nalParse = function nalParse(avcStream) {
35323      var avcView = new DataView(avcStream.buffer, avcStream.byteOffset, avcStream.byteLength),
35324          result = [],
35325          i,
35326          length;
35327  
35328      for (i = 0; i + 4 < avcStream.length; i += length) {
35329        length = avcView.getUint32(i);
35330        i += 4; // bail if this doesn't appear to be an H264 stream
35331  
35332        if (length <= 0) {
35333          result.push('<span style=\'color:red;\'>MALFORMED DATA</span>');
35334          continue;
35335        }
35336  
35337        switch (avcStream[i] & 0x1F) {
35338          case 0x01:
35339            result.push('slice_layer_without_partitioning_rbsp');
35340            break;
35341  
35342          case 0x05:
35343            result.push('slice_layer_without_partitioning_rbsp_idr');
35344            break;
35345  
35346          case 0x06:
35347            result.push('sei_rbsp');
35348            break;
35349  
35350          case 0x07:
35351            result.push('seq_parameter_set_rbsp');
35352            break;
35353  
35354          case 0x08:
35355            result.push('pic_parameter_set_rbsp');
35356            break;
35357  
35358          case 0x09:
35359            result.push('access_unit_delimiter_rbsp');
35360            break;
35361  
35362          default:
35363            result.push('UNKNOWN NAL - ' + avcStream[i] & 0x1F);
35364            break;
35365        }
35366      }
35367  
35368      return result;
35369    },
35370        // registry of handlers for individual mp4 box types
35371    parse$1 = {
35372      // codingname, not a first-class box type. stsd entries share the
vendor: 6,898 bytes, lines 35373-35573
35373      // same format as real boxes so the parsing infrastructure can be
35374      // shared
35375      avc1: function avc1(data) {
35376        var view = new DataView(data.buffer, data.byteOffset, data.byteLength);
35377        return {
35378          dataReferenceIndex: view.getUint16(6),
35379          width: view.getUint16(24),
35380          height: view.getUint16(26),
35381          horizresolution: view.getUint16(28) + view.getUint16(30) / 16,
35382          vertresolution: view.getUint16(32) + view.getUint16(34) / 16,
35383          frameCount: view.getUint16(40),
35384          depth: view.getUint16(74),
35385          config: inspectMp4(data.subarray(78, data.byteLength))
35386        };
35387      },
35388      avcC: function avcC(data) {
35389        var view = new DataView(data.buffer, data.byteOffset, data.byteLength),
35390            result = {
35391          configurationVersion: data[0],
35392          avcProfileIndication: data[1],
35393          profileCompatibility: data[2],
35394          avcLevelIndication: data[3],
35395          lengthSizeMinusOne: data[4] & 0x03,
35396          sps: [],
35397          pps: []
35398        },
35399            numOfSequenceParameterSets = data[5] & 0x1f,
35400            numOfPictureParameterSets,
35401            nalSize,
35402            offset,
35403            i; // iterate past any SPSs
35404  
35405        offset = 6;
35406  
35407        for (i = 0; i < numOfSequenceParameterSets; i++) {
35408          nalSize = view.getUint16(offset);
35409          offset += 2;
35410          result.sps.push(new Uint8Array(data.subarray(offset, offset + nalSize)));
35411          offset += nalSize;
35412        } // iterate past any PPSs
35413  
35414  
35415        numOfPictureParameterSets = data[offset];
35416        offset++;
35417  
35418        for (i = 0; i < numOfPictureParameterSets; i++) {
35419          nalSize = view.getUint16(offset);
35420          offset += 2;
35421          result.pps.push(new Uint8Array(data.subarray(offset, offset + nalSize)));
35422          offset += nalSize;
35423        }
35424  
35425        return result;
35426      },
35427      btrt: function btrt(data) {
35428        var view = new DataView(data.buffer, data.byteOffset, data.byteLength);
35429        return {
35430          bufferSizeDB: view.getUint32(0),
35431          maxBitrate: view.getUint32(4),
35432          avgBitrate: view.getUint32(8)
35433        };
35434      },
35435      esds: function esds(data) {
35436        return {
35437          version: data[0],
35438          flags: new Uint8Array(data.subarray(1, 4)),
35439          esId: data[6] << 8 | data[7],
35440          streamPriority: data[8] & 0x1f,
35441          decoderConfig: {
35442            objectProfileIndication: data[11],
35443            streamType: data[12] >>> 2 & 0x3f,
35444            bufferSize: data[13] << 16 | data[14] << 8 | data[15],
35445            maxBitrate: data[16] << 24 | data[17] << 16 | data[18] << 8 | data[19],
35446            avgBitrate: data[20] << 24 | data[21] << 16 | data[22] << 8 | data[23],
35447            decoderConfigDescriptor: {
35448              tag: data[24],
35449              length: data[25],
35450              audioObjectType: data[26] >>> 3 & 0x1f,
35451              samplingFrequencyIndex: (data[26] & 0x07) << 1 | data[27] >>> 7 & 0x01,
35452              channelConfiguration: data[27] >>> 3 & 0x0f
35453            }
35454          }
35455        };
35456      },
35457      ftyp: function ftyp(data) {
35458        var view = new DataView(data.buffer, data.byteOffset, data.byteLength),
35459            result = {
35460          majorBrand: parseType(data.subarray(0, 4)),
35461          minorVersion: view.getUint32(4),
35462          compatibleBrands: []
35463        },
35464            i = 8;
35465  
35466        while (i < data.byteLength) {
35467          result.compatibleBrands.push(parseType(data.subarray(i, i + 4)));
35468          i += 4;
35469        }
35470  
35471        return result;
35472      },
35473      dinf: function dinf(data) {
35474        return {
35475          boxes: inspectMp4(data)
35476        };
35477      },
35478      dref: function dref(data) {
35479        return {
35480          version: data[0],
35481          flags: new Uint8Array(data.subarray(1, 4)),
35482          dataReferences: inspectMp4(data.subarray(8))
35483        };
35484      },
35485      hdlr: function hdlr(data) {
35486        var view = new DataView(data.buffer, data.byteOffset, data.byteLength),
35487            result = {
35488          version: view.getUint8(0),
35489          flags: new Uint8Array(data.subarray(1, 4)),
35490          handlerType: parseType(data.subarray(8, 12)),
35491          name: ''
35492        },
35493            i = 8; // parse out the name field
35494  
35495        for (i = 24; i < data.byteLength; i++) {
35496          if (data[i] === 0x00) {
35497            // the name field is null-terminated
35498            i++;
35499            break;
35500          }
35501  
35502          result.name += String.fromCharCode(data[i]);
35503        } // decode UTF-8 to javascript's internal representation
35504        // see http://ecmanaut.blogspot.com/2006/07/encoding-decoding-utf8-in-javascript.html
35505  
35506  
35507        result.name = decodeURIComponent(escape(result.name));
35508        return result;
35509      },
35510      mdat: function mdat(data) {
35511        return {
35512          byteLength: data.byteLength,
35513          nals: nalParse(data)
35514        };
35515      },
35516      mdhd: function mdhd(data) {
35517        var view = new DataView(data.buffer, data.byteOffset, data.byteLength),
35518            i = 4,
35519            language,
35520            result = {
35521          version: view.getUint8(0),
35522          flags: new Uint8Array(data.subarray(1, 4)),
35523          language: ''
35524        };
35525  
35526        if (result.version === 1) {
35527          i += 4;
35528          result.creationTime = parseMp4Date(view.getUint32(i)); // truncating top 4 bytes
35529  
35530          i += 8;
35531          result.modificationTime = parseMp4Date(view.getUint32(i)); // truncating top 4 bytes
35532  
35533          i += 4;
35534          result.timescale = view.getUint32(i);
35535          i += 8;
35536          result.duration = view.getUint32(i); // truncating top 4 bytes
35537        } else {
35538          result.creationTime = parseMp4Date(view.getUint32(i));
35539          i += 4;
35540          result.modificationTime = parseMp4Date(view.getUint32(i));
35541          i += 4;
35542          result.timescale = view.getUint32(i);
35543          i += 4;
35544          result.duration = view.getUint32(i);
35545        }
35546  
35547        i += 4; // language is stored as an ISO-639-2/T code in an array of three 5-bit fields
35548        // each field is the packed difference between its ASCII value and 0x60
35549  
35550        language = view.getUint16(i);
35551        result.language += String.fromCharCode((language >> 10) + 0x60);
35552        result.language += String.fromCharCode(((language & 0x03e0) >> 5) + 0x60);
35553        result.language += String.fromCharCode((language & 0x1f) + 0x60);
35554        return result;
35555      },
35556      mdia: function mdia(data) {
35557        return {
35558          boxes: inspectMp4(data)
35559        };
35560      },
35561      mfhd: function mfhd(data) {
35562        return {
35563          version: data[0],
35564          flags: new Uint8Array(data.subarray(1, 4)),
35565          sequenceNumber: data[4] << 24 | data[5] << 16 | data[6] << 8 | data[7]
35566        };
35567      },
35568      minf: function minf(data) {
35569        return {
35570          boxes: inspectMp4(data)
35571        };
35572      },
35573      // codingname, not a first-class box type. stsd entries share the
vendor: 26,181 bytes, lines 35574-36333
35574      // same format as real boxes so the parsing infrastructure can be
35575      // shared
35576      mp4a: function mp4a(data) {
35577        var view = new DataView(data.buffer, data.byteOffset, data.byteLength),
35578            result = {
35579          // 6 bytes reserved
35580          dataReferenceIndex: view.getUint16(6),
35581          // 4 + 4 bytes reserved
35582          channelcount: view.getUint16(16),
35583          samplesize: view.getUint16(18),
35584          // 2 bytes pre_defined
35585          // 2 bytes reserved
35586          samplerate: view.getUint16(24) + view.getUint16(26) / 65536
35587        }; // if there are more bytes to process, assume this is an ISO/IEC
35588        // 14496-14 MP4AudioSampleEntry and parse the ESDBox
35589  
35590        if (data.byteLength > 28) {
35591          result.streamDescriptor = inspectMp4(data.subarray(28))[0];
35592        }
35593  
35594        return result;
35595      },
35596      moof: function moof(data) {
35597        return {
35598          boxes: inspectMp4(data)
35599        };
35600      },
35601      moov: function moov(data) {
35602        return {
35603          boxes: inspectMp4(data)
35604        };
35605      },
35606      mvex: function mvex(data) {
35607        return {
35608          boxes: inspectMp4(data)
35609        };
35610      },
35611      mvhd: function mvhd(data) {
35612        var view = new DataView(data.buffer, data.byteOffset, data.byteLength),
35613            i = 4,
35614            result = {
35615          version: view.getUint8(0),
35616          flags: new Uint8Array(data.subarray(1, 4))
35617        };
35618  
35619        if (result.version === 1) {
35620          i += 4;
35621          result.creationTime = parseMp4Date(view.getUint32(i)); // truncating top 4 bytes
35622  
35623          i += 8;
35624          result.modificationTime = parseMp4Date(view.getUint32(i)); // truncating top 4 bytes
35625  
35626          i += 4;
35627          result.timescale = view.getUint32(i);
35628          i += 8;
35629          result.duration = view.getUint32(i); // truncating top 4 bytes
35630        } else {
35631          result.creationTime = parseMp4Date(view.getUint32(i));
35632          i += 4;
35633          result.modificationTime = parseMp4Date(view.getUint32(i));
35634          i += 4;
35635          result.timescale = view.getUint32(i);
35636          i += 4;
35637          result.duration = view.getUint32(i);
35638        }
35639  
35640        i += 4; // convert fixed-point, base 16 back to a number
35641  
35642        result.rate = view.getUint16(i) + view.getUint16(i + 2) / 16;
35643        i += 4;
35644        result.volume = view.getUint8(i) + view.getUint8(i + 1) / 8;
35645        i += 2;
35646        i += 2;
35647        i += 2 * 4;
35648        result.matrix = new Uint32Array(data.subarray(i, i + 9 * 4));
35649        i += 9 * 4;
35650        i += 6 * 4;
35651        result.nextTrackId = view.getUint32(i);
35652        return result;
35653      },
35654      pdin: function pdin(data) {
35655        var view = new DataView(data.buffer, data.byteOffset, data.byteLength);
35656        return {
35657          version: view.getUint8(0),
35658          flags: new Uint8Array(data.subarray(1, 4)),
35659          rate: view.getUint32(4),
35660          initialDelay: view.getUint32(8)
35661        };
35662      },
35663      sdtp: function sdtp(data) {
35664        var result = {
35665          version: data[0],
35666          flags: new Uint8Array(data.subarray(1, 4)),
35667          samples: []
35668        },
35669            i;
35670  
35671        for (i = 4; i < data.byteLength; i++) {
35672          result.samples.push({
35673            dependsOn: (data[i] & 0x30) >> 4,
35674            isDependedOn: (data[i] & 0x0c) >> 2,
35675            hasRedundancy: data[i] & 0x03
35676          });
35677        }
35678  
35679        return result;
35680      },
35681      sidx: function sidx(data) {
35682        var view = new DataView(data.buffer, data.byteOffset, data.byteLength),
35683            result = {
35684          version: data[0],
35685          flags: new Uint8Array(data.subarray(1, 4)),
35686          references: [],
35687          referenceId: view.getUint32(4),
35688          timescale: view.getUint32(8),
35689          earliestPresentationTime: view.getUint32(12),
35690          firstOffset: view.getUint32(16)
35691        },
35692            referenceCount = view.getUint16(22),
35693            i;
35694  
35695        for (i = 24; referenceCount; i += 12, referenceCount--) {
35696          result.references.push({
35697            referenceType: (data[i] & 0x80) >>> 7,
35698            referencedSize: view.getUint32(i) & 0x7FFFFFFF,
35699            subsegmentDuration: view.getUint32(i + 4),
35700            startsWithSap: !!(data[i + 8] & 0x80),
35701            sapType: (data[i + 8] & 0x70) >>> 4,
35702            sapDeltaTime: view.getUint32(i + 8) & 0x0FFFFFFF
35703          });
35704        }
35705  
35706        return result;
35707      },
35708      smhd: function smhd(data) {
35709        return {
35710          version: data[0],
35711          flags: new Uint8Array(data.subarray(1, 4)),
35712          balance: data[4] + data[5] / 256
35713        };
35714      },
35715      stbl: function stbl(data) {
35716        return {
35717          boxes: inspectMp4(data)
35718        };
35719      },
35720      stco: function stco(data) {
35721        var view = new DataView(data.buffer, data.byteOffset, data.byteLength),
35722            result = {
35723          version: data[0],
35724          flags: new Uint8Array(data.subarray(1, 4)),
35725          chunkOffsets: []
35726        },
35727            entryCount = view.getUint32(4),
35728            i;
35729  
35730        for (i = 8; entryCount; i += 4, entryCount--) {
35731          result.chunkOffsets.push(view.getUint32(i));
35732        }
35733  
35734        return result;
35735      },
35736      stsc: function stsc(data) {
35737        var view = new DataView(data.buffer, data.byteOffset, data.byteLength),
35738            entryCount = view.getUint32(4),
35739            result = {
35740          version: data[0],
35741          flags: new Uint8Array(data.subarray(1, 4)),
35742          sampleToChunks: []
35743        },
35744            i;
35745  
35746        for (i = 8; entryCount; i += 12, entryCount--) {
35747          result.sampleToChunks.push({
35748            firstChunk: view.getUint32(i),
35749            samplesPerChunk: view.getUint32(i + 4),
35750            sampleDescriptionIndex: view.getUint32(i + 8)
35751          });
35752        }
35753  
35754        return result;
35755      },
35756      stsd: function stsd(data) {
35757        return {
35758          version: data[0],
35759          flags: new Uint8Array(data.subarray(1, 4)),
35760          sampleDescriptions: inspectMp4(data.subarray(8))
35761        };
35762      },
35763      stsz: function stsz(data) {
35764        var view = new DataView(data.buffer, data.byteOffset, data.byteLength),
35765            result = {
35766          version: data[0],
35767          flags: new Uint8Array(data.subarray(1, 4)),
35768          sampleSize: view.getUint32(4),
35769          entries: []
35770        },
35771            i;
35772  
35773        for (i = 12; i < data.byteLength; i += 4) {
35774          result.entries.push(view.getUint32(i));
35775        }
35776  
35777        return result;
35778      },
35779      stts: function stts(data) {
35780        var view = new DataView(data.buffer, data.byteOffset, data.byteLength),
35781            result = {
35782          version: data[0],
35783          flags: new Uint8Array(data.subarray(1, 4)),
35784          timeToSamples: []
35785        },
35786            entryCount = view.getUint32(4),
35787            i;
35788  
35789        for (i = 8; entryCount; i += 8, entryCount--) {
35790          result.timeToSamples.push({
35791            sampleCount: view.getUint32(i),
35792            sampleDelta: view.getUint32(i + 4)
35793          });
35794        }
35795  
35796        return result;
35797      },
35798      styp: function styp(data) {
35799        return parse$1.ftyp(data);
35800      },
35801      tfdt: function tfdt(data) {
35802        var result = {
35803          version: data[0],
35804          flags: new Uint8Array(data.subarray(1, 4)),
35805          baseMediaDecodeTime: toUnsigned$1(data[4] << 24 | data[5] << 16 | data[6] << 8 | data[7])
35806        };
35807  
35808        if (result.version === 1) {
35809          result.baseMediaDecodeTime *= Math.pow(2, 32);
35810          result.baseMediaDecodeTime += toUnsigned$1(data[8] << 24 | data[9] << 16 | data[10] << 8 | data[11]);
35811        }
35812  
35813        return result;
35814      },
35815      tfhd: function tfhd(data) {
35816        var view = new DataView(data.buffer, data.byteOffset, data.byteLength),
35817            result = {
35818          version: data[0],
35819          flags: new Uint8Array(data.subarray(1, 4)),
35820          trackId: view.getUint32(4)
35821        },
35822            baseDataOffsetPresent = result.flags[2] & 0x01,
35823            sampleDescriptionIndexPresent = result.flags[2] & 0x02,
35824            defaultSampleDurationPresent = result.flags[2] & 0x08,
35825            defaultSampleSizePresent = result.flags[2] & 0x10,
35826            defaultSampleFlagsPresent = result.flags[2] & 0x20,
35827            durationIsEmpty = result.flags[0] & 0x010000,
35828            defaultBaseIsMoof = result.flags[0] & 0x020000,
35829            i;
35830        i = 8;
35831  
35832        if (baseDataOffsetPresent) {
35833          i += 4; // truncate top 4 bytes
35834          // FIXME: should we read the full 64 bits?
35835  
35836          result.baseDataOffset = view.getUint32(12);
35837          i += 4;
35838        }
35839  
35840        if (sampleDescriptionIndexPresent) {
35841          result.sampleDescriptionIndex = view.getUint32(i);
35842          i += 4;
35843        }
35844  
35845        if (defaultSampleDurationPresent) {
35846          result.defaultSampleDuration = view.getUint32(i);
35847          i += 4;
35848        }
35849  
35850        if (defaultSampleSizePresent) {
35851          result.defaultSampleSize = view.getUint32(i);
35852          i += 4;
35853        }
35854  
35855        if (defaultSampleFlagsPresent) {
35856          result.defaultSampleFlags = view.getUint32(i);
35857        }
35858  
35859        if (durationIsEmpty) {
35860          result.durationIsEmpty = true;
35861        }
35862  
35863        if (!baseDataOffsetPresent && defaultBaseIsMoof) {
35864          result.baseDataOffsetIsMoof = true;
35865        }
35866  
35867        return result;
35868      },
35869      tkhd: function tkhd(data) {
35870        var view = new DataView(data.buffer, data.byteOffset, data.byteLength),
35871            i = 4,
35872            result = {
35873          version: view.getUint8(0),
35874          flags: new Uint8Array(data.subarray(1, 4))
35875        };
35876  
35877        if (result.version === 1) {
35878          i += 4;
35879          result.creationTime = parseMp4Date(view.getUint32(i)); // truncating top 4 bytes
35880  
35881          i += 8;
35882          result.modificationTime = parseMp4Date(view.getUint32(i)); // truncating top 4 bytes
35883  
35884          i += 4;
35885          result.trackId = view.getUint32(i);
35886          i += 4;
35887          i += 8;
35888          result.duration = view.getUint32(i); // truncating top 4 bytes
35889        } else {
35890          result.creationTime = parseMp4Date(view.getUint32(i));
35891          i += 4;
35892          result.modificationTime = parseMp4Date(view.getUint32(i));
35893          i += 4;
35894          result.trackId = view.getUint32(i);
35895          i += 4;
35896          i += 4;
35897          result.duration = view.getUint32(i);
35898        }
35899  
35900        i += 4;
35901        i += 2 * 4;
35902        result.layer = view.getUint16(i);
35903        i += 2;
35904        result.alternateGroup = view.getUint16(i);
35905        i += 2; // convert fixed-point, base 16 back to a number
35906  
35907        result.volume = view.getUint8(i) + view.getUint8(i + 1) / 8;
35908        i += 2;
35909        i += 2;
35910        result.matrix = new Uint32Array(data.subarray(i, i + 9 * 4));
35911        i += 9 * 4;
35912        result.width = view.getUint16(i) + view.getUint16(i + 2) / 16;
35913        i += 4;
35914        result.height = view.getUint16(i) + view.getUint16(i + 2) / 16;
35915        return result;
35916      },
35917      traf: function traf(data) {
35918        return {
35919          boxes: inspectMp4(data)
35920        };
35921      },
35922      trak: function trak(data) {
35923        return {
35924          boxes: inspectMp4(data)
35925        };
35926      },
35927      trex: function trex(data) {
35928        var view = new DataView(data.buffer, data.byteOffset, data.byteLength);
35929        return {
35930          version: data[0],
35931          flags: new Uint8Array(data.subarray(1, 4)),
35932          trackId: view.getUint32(4),
35933          defaultSampleDescriptionIndex: view.getUint32(8),
35934          defaultSampleDuration: view.getUint32(12),
35935          defaultSampleSize: view.getUint32(16),
35936          sampleDependsOn: data[20] & 0x03,
35937          sampleIsDependedOn: (data[21] & 0xc0) >> 6,
35938          sampleHasRedundancy: (data[21] & 0x30) >> 4,
35939          samplePaddingValue: (data[21] & 0x0e) >> 1,
35940          sampleIsDifferenceSample: !!(data[21] & 0x01),
35941          sampleDegradationPriority: view.getUint16(22)
35942        };
35943      },
35944      trun: function trun(data) {
35945        var result = {
35946          version: data[0],
35947          flags: new Uint8Array(data.subarray(1, 4)),
35948          samples: []
35949        },
35950            view = new DataView(data.buffer, data.byteOffset, data.byteLength),
35951            // Flag interpretation
35952        dataOffsetPresent = result.flags[2] & 0x01,
35953            // compare with 2nd byte of 0x1
35954        firstSampleFlagsPresent = result.flags[2] & 0x04,
35955            // compare with 2nd byte of 0x4
35956        sampleDurationPresent = result.flags[1] & 0x01,
35957            // compare with 2nd byte of 0x100
35958        sampleSizePresent = result.flags[1] & 0x02,
35959            // compare with 2nd byte of 0x200
35960        sampleFlagsPresent = result.flags[1] & 0x04,
35961            // compare with 2nd byte of 0x400
35962        sampleCompositionTimeOffsetPresent = result.flags[1] & 0x08,
35963            // compare with 2nd byte of 0x800
35964        sampleCount = view.getUint32(4),
35965            offset = 8,
35966            sample;
35967  
35968        if (dataOffsetPresent) {
35969          // 32 bit signed integer
35970          result.dataOffset = view.getInt32(offset);
35971          offset += 4;
35972        } // Overrides the flags for the first sample only. The order of
35973        // optional values will be: duration, size, compositionTimeOffset
35974  
35975  
35976        if (firstSampleFlagsPresent && sampleCount) {
35977          sample = {
35978            flags: parseSampleFlags(data.subarray(offset, offset + 4))
35979          };
35980          offset += 4;
35981  
35982          if (sampleDurationPresent) {
35983            sample.duration = view.getUint32(offset);
35984            offset += 4;
35985          }
35986  
35987          if (sampleSizePresent) {
35988            sample.size = view.getUint32(offset);
35989            offset += 4;
35990          }
35991  
35992          if (sampleCompositionTimeOffsetPresent) {
35993            // Note: this should be a signed int if version is 1
35994            sample.compositionTimeOffset = view.getUint32(offset);
35995            offset += 4;
35996          }
35997  
35998          result.samples.push(sample);
35999          sampleCount--;
36000        }
36001  
36002        while (sampleCount--) {
36003          sample = {};
36004  
36005          if (sampleDurationPresent) {
36006            sample.duration = view.getUint32(offset);
36007            offset += 4;
36008          }
36009  
36010          if (sampleSizePresent) {
36011            sample.size = view.getUint32(offset);
36012            offset += 4;
36013          }
36014  
36015          if (sampleFlagsPresent) {
36016            sample.flags = parseSampleFlags(data.subarray(offset, offset + 4));
36017            offset += 4;
36018          }
36019  
36020          if (sampleCompositionTimeOffsetPresent) {
36021            // Note: this should be a signed int if version is 1
36022            sample.compositionTimeOffset = view.getUint32(offset);
36023            offset += 4;
36024          }
36025  
36026          result.samples.push(sample);
36027        }
36028  
36029        return result;
36030      },
36031      'url ': function url(data) {
36032        return {
36033          version: data[0],
36034          flags: new Uint8Array(data.subarray(1, 4))
36035        };
36036      },
36037      vmhd: function vmhd(data) {
36038        var view = new DataView(data.buffer, data.byteOffset, data.byteLength);
36039        return {
36040          version: data[0],
36041          flags: new Uint8Array(data.subarray(1, 4)),
36042          graphicsmode: view.getUint16(4),
36043          opcolor: new Uint16Array([view.getUint16(6), view.getUint16(8), view.getUint16(10)])
36044        };
36045      }
36046    };
36047    /**
36048     * Return a javascript array of box objects parsed from an ISO base
36049     * media file.
36050     * @param data {Uint8Array} the binary data of the media to be inspected
36051     * @return {array} a javascript array of potentially nested box objects
36052     */
36053  
36054  
36055    inspectMp4 = function inspectMp4(data) {
36056      var i = 0,
36057          result = [],
36058          view,
36059          size,
36060          type,
36061          end,
36062          box; // Convert data from Uint8Array to ArrayBuffer, to follow Dataview API
36063  
36064      var ab = new ArrayBuffer(data.length);
36065      var v = new Uint8Array(ab);
36066  
36067      for (var z = 0; z < data.length; ++z) {
36068        v[z] = data[z];
36069      }
36070  
36071      view = new DataView(ab);
36072  
36073      while (i < data.byteLength) {
36074        // parse box data
36075        size = view.getUint32(i);
36076        type = parseType(data.subarray(i + 4, i + 8));
36077        end = size > 1 ? i + size : data.byteLength; // parse type-specific data
36078  
36079        box = (parse$1[type] || function (data) {
36080          return {
36081            data: data
36082          };
36083        })(data.subarray(i + 8, end));
36084  
36085        box.size = size;
36086        box.type = type; // store this box and move to the next
36087  
36088        result.push(box);
36089        i = end;
36090      }
36091  
36092      return result;
36093    };
36094    /**
36095     * Returns a textual representation of the javascript represtentation
36096     * of an MP4 file. You can use it as an alternative to
36097     * JSON.stringify() to compare inspected MP4s.
36098     * @param inspectedMp4 {array} the parsed array of boxes in an MP4
36099     * file
36100     * @param depth {number} (optional) the number of ancestor boxes of
36101     * the elements of inspectedMp4. Assumed to be zero if unspecified.
36102     * @return {string} a text representation of the parsed MP4
36103     */
36104  
36105  
36106    _textifyMp = function textifyMp4(inspectedMp4, depth) {
36107      var indent;
36108      depth = depth || 0;
36109      indent = new Array(depth * 2 + 1).join(' '); // iterate over all the boxes
36110  
36111      return inspectedMp4.map(function (box, index) {
36112        // list the box type first at the current indentation level
36113        return indent + box.type + '\n' + // the type is already included and handle child boxes separately
36114        Object.keys(box).filter(function (key) {
36115          return key !== 'type' && key !== 'boxes'; // output all the box properties
36116        }).map(function (key) {
36117          var prefix = indent + '  ' + key + ': ',
36118              value = box[key]; // print out raw bytes as hexademical
36119  
36120          if (value instanceof Uint8Array || value instanceof Uint32Array) {
36121            var bytes = Array.prototype.slice.call(new Uint8Array(value.buffer, value.byteOffset, value.byteLength)).map(function (_byte) {
36122              return ' ' + ('00' + _byte.toString(16)).slice(-2);
36123            }).join('').match(/.{1,24}/g);
36124  
36125            if (!bytes) {
36126              return prefix + '<>';
36127            }
36128  
36129            if (bytes.length === 1) {
36130              return prefix + '<' + bytes.join('').slice(1) + '>';
36131            }
36132  
36133            return prefix + '<\n' + bytes.map(function (line) {
36134              return indent + '  ' + line;
36135            }).join('\n') + '\n' + indent + '  >';
36136          } // stringify generic objects
36137  
36138  
36139          return prefix + JSON.stringify(value, null, 2).split('\n').map(function (line, index) {
36140            if (index === 0) {
36141              return line;
36142            }
36143  
36144            return indent + '  ' + line;
36145          }).join('\n');
36146        }).join('\n') + ( // recursively textify the child boxes
36147        box.boxes ? '\n' + _textifyMp(box.boxes, depth + 1) : '');
36148      }).join('\n');
36149    };
36150  
36151    var mp4Inspector = {
36152      inspect: inspectMp4,
36153      textify: _textifyMp,
36154      parseType: parseType,
36155      findBox: findBox,
36156      parseTraf: parse$1.traf,
36157      parseTfdt: parse$1.tfdt,
36158      parseHdlr: parse$1.hdlr,
36159      parseTfhd: parse$1.tfhd,
36160      parseTrun: parse$1.trun,
36161      parseSidx: parse$1.sidx
36162    };
36163  
36164    var toUnsigned$2 = bin.toUnsigned;
36165    var toHexString$1 = bin.toHexString;
36166    var timescale, startTime, compositionStartTime, getVideoTrackIds, getTracks;
36167    /**
36168     * Parses an MP4 initialization segment and extracts the timescale
36169     * values for any declared tracks. Timescale values indicate the
36170     * number of clock ticks per second to assume for time-based values
36171     * elsewhere in the MP4.
36172     *
36173     * To determine the start time of an MP4, you need two pieces of
36174     * information: the timescale unit and the earliest base media decode
36175     * time. Multiple timescales can be specified within an MP4 but the
36176     * base media decode time is always expressed in the timescale from
36177     * the media header box for the track:
36178     * ```
36179     * moov > trak > mdia > mdhd.timescale
36180     * ```
36181     * @param init {Uint8Array} the bytes of the init segment
36182     * @return {object} a hash of track ids to timescale values or null if
36183     * the init segment is malformed.
36184     */
36185  
36186    timescale = function timescale(init) {
36187      var result = {},
36188          traks = mp4Inspector.findBox(init, ['moov', 'trak']); // mdhd timescale
36189  
36190      return traks.reduce(function (result, trak) {
36191        var tkhd, version, index, id, mdhd;
36192        tkhd = mp4Inspector.findBox(trak, ['tkhd'])[0];
36193  
36194        if (!tkhd) {
36195          return null;
36196        }
36197  
36198        version = tkhd[0];
36199        index = version === 0 ? 12 : 20;
36200        id = toUnsigned$2(tkhd[index] << 24 | tkhd[index + 1] << 16 | tkhd[index + 2] << 8 | tkhd[index + 3]);
36201        mdhd = mp4Inspector.findBox(trak, ['mdia', 'mdhd'])[0];
36202  
36203        if (!mdhd) {
36204          return null;
36205        }
36206  
36207        version = mdhd[0];
36208        index = version === 0 ? 12 : 20;
36209        result[id] = toUnsigned$2(mdhd[index] << 24 | mdhd[index + 1] << 16 | mdhd[index + 2] << 8 | mdhd[index + 3]);
36210        return result;
36211      }, result);
36212    };
36213    /**
36214     * Determine the base media decode start time, in seconds, for an MP4
36215     * fragment. If multiple fragments are specified, the earliest time is
36216     * returned.
36217     *
36218     * The base media decode time can be parsed from track fragment
36219     * metadata:
36220     * ```
36221     * moof > traf > tfdt.baseMediaDecodeTime
36222     * ```
36223     * It requires the timescale value from the mdhd to interpret.
36224     *
36225     * @param timescale {object} a hash of track ids to timescale values.
36226     * @return {number} the earliest base media decode start time for the
36227     * fragment, in seconds
36228     */
36229  
36230  
36231    startTime = function startTime(timescale, fragment) {
36232      var trafs, baseTimes, result; // we need info from two childrend of each track fragment box
36233  
36234      trafs = mp4Inspector.findBox(fragment, ['moof', 'traf']); // determine the start times for each track
36235  
36236      baseTimes = [].concat.apply([], trafs.map(function (traf) {
36237        return mp4Inspector.findBox(traf, ['tfhd']).map(function (tfhd) {
36238          var id, scale, baseTime; // get the track id from the tfhd
36239  
36240          id = toUnsigned$2(tfhd[4] << 24 | tfhd[5] << 16 | tfhd[6] << 8 | tfhd[7]); // assume a 90kHz clock if no timescale was specified
36241  
36242          scale = timescale[id] || 90e3; // get the base media decode time from the tfdt
36243  
36244          baseTime = mp4Inspector.findBox(traf, ['tfdt']).map(function (tfdt) {
36245            var version, result;
36246            version = tfdt[0];
36247            result = toUnsigned$2(tfdt[4] << 24 | tfdt[5] << 16 | tfdt[6] << 8 | tfdt[7]);
36248  
36249            if (version === 1) {
36250              result *= Math.pow(2, 32);
36251              result += toUnsigned$2(tfdt[8] << 24 | tfdt[9] << 16 | tfdt[10] << 8 | tfdt[11]);
36252            }
36253  
36254            return result;
36255          })[0];
36256          baseTime = baseTime || Infinity; // convert base time to seconds
36257  
36258          return baseTime / scale;
36259        });
36260      })); // return the minimum
36261  
36262      result = Math.min.apply(null, baseTimes);
36263      return isFinite(result) ? result : 0;
36264    };
36265    /**
36266     * Determine the composition start, in seconds, for an MP4
36267     * fragment.
36268     *
36269     * The composition start time of a fragment can be calculated using the base
36270     * media decode time, composition time offset, and timescale, as follows:
36271     *
36272     * compositionStartTime = (baseMediaDecodeTime + compositionTimeOffset) / timescale
36273     *
36274     * All of the aforementioned information is contained within a media fragment's
36275     * `traf` box, except for timescale info, which comes from the initialization
36276     * segment, so a track id (also contained within a `traf`) is also necessary to
36277     * associate it with a timescale
36278     *
36279     *
36280     * @param timescales {object} - a hash of track ids to timescale values.
36281     * @param fragment {Unit8Array} - the bytes of a media segment
36282     * @return {number} the composition start time for the fragment, in seconds
36283     **/
36284  
36285  
36286    compositionStartTime = function compositionStartTime(timescales, fragment) {
36287      var trafBoxes = mp4Inspector.findBox(fragment, ['moof', 'traf']);
36288      var baseMediaDecodeTime = 0;
36289      var compositionTimeOffset = 0;
36290      var trackId;
36291  
36292      if (trafBoxes && trafBoxes.length) {
36293        // The spec states that track run samples contained within a `traf` box are contiguous, but
36294        // it does not explicitly state whether the `traf` boxes themselves are contiguous.
36295        // We will assume that they are, so we only need the first to calculate start time.
36296        var parsedTraf = mp4Inspector.parseTraf(trafBoxes[0]);
36297  
36298        for (var i = 0; i < parsedTraf.boxes.length; i++) {
36299          if (parsedTraf.boxes[i].type === 'tfhd') {
36300            trackId = parsedTraf.boxes[i].trackId;
36301          } else if (parsedTraf.boxes[i].type === 'tfdt') {
36302            baseMediaDecodeTime = parsedTraf.boxes[i].baseMediaDecodeTime;
36303          } else if (parsedTraf.boxes[i].type === 'trun' && parsedTraf.boxes[i].samples.length) {
36304            compositionTimeOffset = parsedTraf.boxes[i].samples[0].compositionTimeOffset || 0;
36305          }
36306        }
36307      } // Get timescale for this specific track. Assume a 90kHz clock if no timescale was
36308      // specified.
36309  
36310  
36311      var timescale = timescales[trackId] || 90e3; // return the composition start time, in seconds
36312  
36313      return (baseMediaDecodeTime + compositionTimeOffset) / timescale;
36314    };
36315    /**
36316      * Find the trackIds of the video tracks in this source.
36317      * Found by parsing the Handler Reference and Track Header Boxes:
36318      *   moov > trak > mdia > hdlr
36319      *   moov > trak > tkhd
36320      *
36321      * @param {Uint8Array} init - The bytes of the init segment for this source
36322      * @return {Number[]} A list of trackIds
36323      *
36324      * @see ISO-BMFF-12/2015, Section 8.4.3
36325     **/
36326  
36327  
36328    getVideoTrackIds = function getVideoTrackIds(init) {
36329      var traks = mp4Inspector.findBox(init, ['moov', 'trak']);
36330      var videoTrackIds = [];
36331      traks.forEach(function (trak) {
36332        var hdlrs = mp4Inspector.findBox(trak, ['mdia', 'hdlr']);
36333        var tkhds = mp4Inspector.findBox(trak, ['tkhd']);
vendor: 7,252 bytes, lines 36334-36540
36334        hdlrs.forEach(function (hdlr, index) {
36335          var handlerType = mp4Inspector.parseType(hdlr.subarray(8, 12));
36336          var tkhd = tkhds[index];
36337          var view;
36338          var version;
36339          var trackId;
36340  
36341          if (handlerType === 'vide') {
36342            view = new DataView(tkhd.buffer, tkhd.byteOffset, tkhd.byteLength);
36343            version = view.getUint8(0);
36344            trackId = version === 0 ? view.getUint32(12) : view.getUint32(20);
36345            videoTrackIds.push(trackId);
36346          }
36347        });
36348      });
36349      return videoTrackIds;
36350    };
36351    /**
36352     * Get all the video, audio, and hint tracks from a non fragmented
36353     * mp4 segment
36354     */
36355  
36356  
36357    getTracks = function getTracks(init) {
36358      var traks = mp4Inspector.findBox(init, ['moov', 'trak']);
36359      var tracks = [];
36360      traks.forEach(function (trak) {
36361        var track = {};
36362        var tkhd = mp4Inspector.findBox(trak, ['tkhd'])[0];
36363        var view, version; // id
36364  
36365        if (tkhd) {
36366          view = new DataView(tkhd.buffer, tkhd.byteOffset, tkhd.byteLength);
36367          version = view.getUint8(0);
36368          track.id = version === 0 ? view.getUint32(12) : view.getUint32(20);
36369        }
36370  
36371        var hdlr = mp4Inspector.findBox(trak, ['mdia', 'hdlr'])[0]; // type
36372  
36373        if (hdlr) {
36374          var type = mp4Inspector.parseType(hdlr.subarray(8, 12));
36375  
36376          if (type === 'vide') {
36377            track.type = 'video';
36378          } else if (type === 'soun') {
36379            track.type = 'audio';
36380          } else {
36381            track.type = type;
36382          }
36383        } // codec
36384  
36385  
36386        var stsd = mp4Inspector.findBox(trak, ['mdia', 'minf', 'stbl', 'stsd'])[0];
36387  
36388        if (stsd) {
36389          var sampleDescriptions = stsd.subarray(8); // gives the codec type string
36390  
36391          track.codec = mp4Inspector.parseType(sampleDescriptions.subarray(4, 8));
36392          var codecBox = mp4Inspector.findBox(sampleDescriptions, [track.codec])[0];
36393          var codecConfig, codecConfigType;
36394  
36395          if (codecBox) {
36396            // https://tools.ietf.org/html/rfc6381#section-3.3
36397            if (/^[a-z]vc[1-9]$/i.test(track.codec)) {
36398              // we don't need anything but the "config" parameter of the
36399              // avc1 codecBox
36400              codecConfig = codecBox.subarray(78);
36401              codecConfigType = mp4Inspector.parseType(codecConfig.subarray(4, 8));
36402  
36403              if (codecConfigType === 'avcC' && codecConfig.length > 11) {
36404                track.codec += '.'; // left padded with zeroes for single digit hex
36405                // profile idc
36406  
36407                track.codec += toHexString$1(codecConfig[9]); // the byte containing the constraint_set flags
36408  
36409                track.codec += toHexString$1(codecConfig[10]); // level idc
36410  
36411                track.codec += toHexString$1(codecConfig[11]);
36412              } else {
36413                // TODO: show a warning that we couldn't parse the codec
36414                // and are using the default
36415                track.codec = 'avc1.4d400d';
36416              }
36417            } else if (/^mp4[a,v]$/i.test(track.codec)) {
36418              // we do not need anything but the streamDescriptor of the mp4a codecBox
36419              codecConfig = codecBox.subarray(28);
36420              codecConfigType = mp4Inspector.parseType(codecConfig.subarray(4, 8));
36421  
36422              if (codecConfigType === 'esds' && codecConfig.length > 20 && codecConfig[19] !== 0) {
36423                track.codec += '.' + toHexString$1(codecConfig[19]); // this value is only a single digit
36424  
36425                track.codec += '.' + toHexString$1(codecConfig[20] >>> 2 & 0x3f).replace(/^0/, '');
36426              } else {
36427                // TODO: show a warning that we couldn't parse the codec
36428                // and are using the default
36429                track.codec = 'mp4a.40.2';
36430              }
36431            }
36432          }
36433        }
36434  
36435        var mdhd = mp4Inspector.findBox(trak, ['mdia', 'mdhd'])[0];
36436  
36437        if (mdhd && tkhd) {
36438          var index = version === 0 ? 12 : 20;
36439          track.timescale = toUnsigned$2(mdhd[index] << 24 | mdhd[index + 1] << 16 | mdhd[index + 2] << 8 | mdhd[index + 3]);
36440        }
36441  
36442        tracks.push(track);
36443      });
36444      return tracks;
36445    };
36446  
36447    var probe = {
36448      // export mp4 inspector's findBox and parseType for backwards compatibility
36449      findBox: mp4Inspector.findBox,
36450      parseType: mp4Inspector.parseType,
36451      timescale: timescale,
36452      startTime: startTime,
36453      compositionStartTime: compositionStartTime,
36454      videoTrackIds: getVideoTrackIds,
36455      tracks: getTracks
36456    };
36457  
36458    /**
36459     * mux.js
36460     *
36461     * Copyright (c) Brightcove
36462     * Licensed Apache-2.0 https://github.com/videojs/mux.js/blob/master/LICENSE
36463     *
36464     * Reads in-band caption information from a video elementary
36465     * stream. Captions must follow the CEA-708 standard for injection
36466     * into an MPEG-2 transport streams.
36467     * @see https://en.wikipedia.org/wiki/CEA-708
36468     * @see https://www.gpo.gov/fdsys/pkg/CFR-2007-title47-vol1/pdf/CFR-2007-title47-vol1-sec15-119.pdf
36469     */
36470    // payload type field to indicate how they are to be
36471    // interpreted. CEAS-708 caption content is always transmitted with
36472    // payload type 0x04.
36473  
36474    var USER_DATA_REGISTERED_ITU_T_T35 = 4,
36475        RBSP_TRAILING_BITS = 128;
36476    /**
36477      * Parse a supplemental enhancement information (SEI) NAL unit.
36478      * Stops parsing once a message of type ITU T T35 has been found.
36479      *
36480      * @param bytes {Uint8Array} the bytes of a SEI NAL unit
36481      * @return {object} the parsed SEI payload
36482      * @see Rec. ITU-T H.264, 7.3.2.3.1
36483      */
36484  
36485    var parseSei = function parseSei(bytes) {
36486      var i = 0,
36487          result = {
36488        payloadType: -1,
36489        payloadSize: 0
36490      },
36491          payloadType = 0,
36492          payloadSize = 0; // go through the sei_rbsp parsing each each individual sei_message
36493  
36494      while (i < bytes.byteLength) {
36495        // stop once we have hit the end of the sei_rbsp
36496        if (bytes[i] === RBSP_TRAILING_BITS) {
36497          break;
36498        } // Parse payload type
36499  
36500  
36501        while (bytes[i] === 0xFF) {
36502          payloadType += 255;
36503          i++;
36504        }
36505  
36506        payloadType += bytes[i++]; // Parse payload size
36507  
36508        while (bytes[i] === 0xFF) {
36509          payloadSize += 255;
36510          i++;
36511        }
36512  
36513        payloadSize += bytes[i++]; // this sei_message is a 608/708 caption so save it and break
36514        // there can only ever be one caption message in a frame's sei
36515  
36516        if (!result.payload && payloadType === USER_DATA_REGISTERED_ITU_T_T35) {
36517          result.payloadType = payloadType;
36518          result.payloadSize = payloadSize;
36519          result.payload = bytes.subarray(i, i + payloadSize);
36520          break;
36521        } // skip the payload and parse the next message
36522  
36523  
36524        i += payloadSize;
36525        payloadType = 0;
36526        payloadSize = 0;
36527      }
36528  
36529      return result;
36530    }; // see ANSI/SCTE 128-1 (2013), section 8.1
36531  
36532  
36533    var parseUserData = function parseUserData(sei) {
36534      // itu_t_t35_contry_code must be 181 (United States) for
36535      // captions
36536      if (sei.payload[0] !== 181) {
36537        return null;
36538      } // itu_t_t35_provider_code should be 49 (ATSC) for captions
36539  
36540  
vendor: 4,193 bytes, lines 36541-36680
36541      if ((sei.payload[1] << 8 | sei.payload[2]) !== 49) {
36542        return null;
36543      } // the user_identifier should be "GA94" to indicate ATSC1 data
36544  
36545  
36546      if (String.fromCharCode(sei.payload[3], sei.payload[4], sei.payload[5], sei.payload[6]) !== 'GA94') {
36547        return null;
36548      } // finally, user_data_type_code should be 0x03 for caption data
36549  
36550  
36551      if (sei.payload[7] !== 0x03) {
36552        return null;
36553      } // return the user_data_type_structure and strip the trailing
36554      // marker bits
36555  
36556  
36557      return sei.payload.subarray(8, sei.payload.length - 1);
36558    }; // see CEA-708-D, section 4.4
36559  
36560  
36561    var parseCaptionPackets = function parseCaptionPackets(pts, userData) {
36562      var results = [],
36563          i,
36564          count,
36565          offset,
36566          data; // if this is just filler, return immediately
36567  
36568      if (!(userData[0] & 0x40)) {
36569        return results;
36570      } // parse out the cc_data_1 and cc_data_2 fields
36571  
36572  
36573      count = userData[0] & 0x1f;
36574  
36575      for (i = 0; i < count; i++) {
36576        offset = i * 3;
36577        data = {
36578          type: userData[offset + 2] & 0x03,
36579          pts: pts
36580        }; // capture cc data when cc_valid is 1
36581  
36582        if (userData[offset + 2] & 0x04) {
36583          data.ccData = userData[offset + 3] << 8 | userData[offset + 4];
36584          results.push(data);
36585        }
36586      }
36587  
36588      return results;
36589    };
36590  
36591    var discardEmulationPreventionBytes = function discardEmulationPreventionBytes(data) {
36592      var length = data.byteLength,
36593          emulationPreventionBytesPositions = [],
36594          i = 1,
36595          newLength,
36596          newData; // Find all `Emulation Prevention Bytes`
36597  
36598      while (i < length - 2) {
36599        if (data[i] === 0 && data[i + 1] === 0 && data[i + 2] === 0x03) {
36600          emulationPreventionBytesPositions.push(i + 2);
36601          i += 2;
36602        } else {
36603          i++;
36604        }
36605      } // If no Emulation Prevention Bytes were found just return the original
36606      // array
36607  
36608  
36609      if (emulationPreventionBytesPositions.length === 0) {
36610        return data;
36611      } // Create a new array to hold the NAL unit data
36612  
36613  
36614      newLength = length - emulationPreventionBytesPositions.length;
36615      newData = new Uint8Array(newLength);
36616      var sourceIndex = 0;
36617  
36618      for (i = 0; i < newLength; sourceIndex++, i++) {
36619        if (sourceIndex === emulationPreventionBytesPositions[0]) {
36620          // Skip this byte
36621          sourceIndex++; // Remove this position index
36622  
36623          emulationPreventionBytesPositions.shift();
36624        }
36625  
36626        newData[i] = data[sourceIndex];
36627      }
36628  
36629      return newData;
36630    }; // exports
36631  
36632  
36633    var captionPacketParser = {
36634      parseSei: parseSei,
36635      parseUserData: parseUserData,
36636      parseCaptionPackets: parseCaptionPackets,
36637      discardEmulationPreventionBytes: discardEmulationPreventionBytes,
36638      USER_DATA_REGISTERED_ITU_T_T35: USER_DATA_REGISTERED_ITU_T_T35
36639    };
36640  
36641    /**
36642     * mux.js
36643     *
36644     * Copyright (c) Brightcove
36645     * Licensed Apache-2.0 https://github.com/videojs/mux.js/blob/master/LICENSE
36646     *
36647     * A lightweight readable stream implemention that handles event dispatching.
36648     * Objects that inherit from streams should call init in their constructors.
36649     */
36650  
36651    var Stream$1 = function Stream() {
36652      this.init = function () {
36653        var listeners = {};
36654        /**
36655         * Add a listener for a specified event type.
36656         * @param type {string} the event name
36657         * @param listener {function} the callback to be invoked when an event of
36658         * the specified type occurs
36659         */
36660  
36661        this.on = function (type, listener) {
36662          if (!listeners[type]) {
36663            listeners[type] = [];
36664          }
36665  
36666          listeners[type] = listeners[type].concat(listener);
36667        };
36668        /**
36669         * Remove a listener for a specified event type.
36670         * @param type {string} the event name
36671         * @param listener {function} a function previously registered for this
36672         * type of event through `on`
36673         */
36674  
36675  
36676        this.off = function (type, listener) {
36677          var index;
36678  
36679          if (!listeners[type]) {
36680            return false;
vendor: 6,151 bytes, lines 36681-36855
36681          }
36682  
36683          index = listeners[type].indexOf(listener);
36684          listeners[type] = listeners[type].slice();
36685          listeners[type].splice(index, 1);
36686          return index > -1;
36687        };
36688        /**
36689         * Trigger an event of the specified type on this stream. Any additional
36690         * arguments to this function are passed as parameters to event listeners.
36691         * @param type {string} the event name
36692         */
36693  
36694  
36695        this.trigger = function (type) {
36696          var callbacks, i, length, args;
36697          callbacks = listeners[type];
36698  
36699          if (!callbacks) {
36700            return;
36701          } // Slicing the arguments on every invocation of this method
36702          // can add a significant amount of overhead. Avoid the
36703          // intermediate object creation for the common case of a
36704          // single callback argument
36705  
36706  
36707          if (arguments.length === 2) {
36708            length = callbacks.length;
36709  
36710            for (i = 0; i < length; ++i) {
36711              callbacks[i].call(this, arguments[1]);
36712            }
36713          } else {
36714            args = [];
36715            i = arguments.length;
36716  
36717            for (i = 1; i < arguments.length; ++i) {
36718              args.push(arguments[i]);
36719            }
36720  
36721            length = callbacks.length;
36722  
36723            for (i = 0; i < length; ++i) {
36724              callbacks[i].apply(this, args);
36725            }
36726          }
36727        };
36728        /**
36729         * Destroys the stream and cleans up.
36730         */
36731  
36732  
36733        this.dispose = function () {
36734          listeners = {};
36735        };
36736      };
36737    };
36738    /**
36739     * Forwards all `data` events on this stream to the destination stream. The
36740     * destination stream should provide a method `push` to receive the data
36741     * events as they arrive.
36742     * @param destination {stream} the stream that will receive all `data` events
36743     * @param autoFlush {boolean} if false, we will not call `flush` on the destination
36744     *                            when the current stream emits a 'done' event
36745     * @see http://nodejs.org/api/stream.html#stream_readable_pipe_destination_options
36746     */
36747  
36748  
36749    Stream$1.prototype.pipe = function (destination) {
36750      this.on('data', function (data) {
36751        destination.push(data);
36752      });
36753      this.on('done', function (flushSource) {
36754        destination.flush(flushSource);
36755      });
36756      this.on('partialdone', function (flushSource) {
36757        destination.partialFlush(flushSource);
36758      });
36759      this.on('endedtimeline', function (flushSource) {
36760        destination.endTimeline(flushSource);
36761      });
36762      this.on('reset', function (flushSource) {
36763        destination.reset(flushSource);
36764      });
36765      return destination;
36766    }; // Default stream functions that are expected to be overridden to perform
36767    // actual work. These are provided by the prototype as a sort of no-op
36768    // implementation so that we don't have to check for their existence in the
36769    // `pipe` function above.
36770  
36771  
36772    Stream$1.prototype.push = function (data) {
36773      this.trigger('data', data);
36774    };
36775  
36776    Stream$1.prototype.flush = function (flushSource) {
36777      this.trigger('done', flushSource);
36778    };
36779  
36780    Stream$1.prototype.partialFlush = function (flushSource) {
36781      this.trigger('partialdone', flushSource);
36782    };
36783  
36784    Stream$1.prototype.endTimeline = function (flushSource) {
36785      this.trigger('endedtimeline', flushSource);
36786    };
36787  
36788    Stream$1.prototype.reset = function (flushSource) {
36789      this.trigger('reset', flushSource);
36790    };
36791  
36792    var stream = Stream$1;
36793  
36794    // Link To Transport
36795    // -----------------
36796  
36797  
36798    var CaptionStream = function CaptionStream() {
36799      CaptionStream.prototype.init.call(this);
36800      this.captionPackets_ = [];
36801      this.ccStreams_ = [new Cea608Stream(0, 0), // eslint-disable-line no-use-before-define
36802      new Cea608Stream(0, 1), // eslint-disable-line no-use-before-define
36803      new Cea608Stream(1, 0), // eslint-disable-line no-use-before-define
36804      new Cea608Stream(1, 1) // eslint-disable-line no-use-before-define
36805      ];
36806      this.reset(); // forward data and done events from CCs to this CaptionStream
36807  
36808      this.ccStreams_.forEach(function (cc) {
36809        cc.on('data', this.trigger.bind(this, 'data'));
36810        cc.on('partialdone', this.trigger.bind(this, 'partialdone'));
36811        cc.on('done', this.trigger.bind(this, 'done'));
36812      }, this);
36813    };
36814  
36815    CaptionStream.prototype = new stream();
36816  
36817    CaptionStream.prototype.push = function (event) {
36818      var sei, userData, newCaptionPackets; // only examine SEI NALs
36819  
36820      if (event.nalUnitType !== 'sei_rbsp') {
36821        return;
36822      } // parse the sei
36823  
36824  
36825      sei = captionPacketParser.parseSei(event.escapedRBSP); // ignore everything but user_data_registered_itu_t_t35
36826  
36827      if (sei.payloadType !== captionPacketParser.USER_DATA_REGISTERED_ITU_T_T35) {
36828        return;
36829      } // parse out the user data payload
36830  
36831  
36832      userData = captionPacketParser.parseUserData(sei); // ignore unrecognized userData
36833  
36834      if (!userData) {
36835        return;
36836      } // Sometimes, the same segment # will be downloaded twice. To stop the
36837      // caption data from being processed twice, we track the latest dts we've
36838      // received and ignore everything with a dts before that. However, since
36839      // data for a specific dts can be split across packets on either side of
36840      // a segment boundary, we need to make sure we *don't* ignore the packets
36841      // from the *next* segment that have dts === this.latestDts_. By constantly
36842      // tracking the number of packets received with dts === this.latestDts_, we
36843      // know how many should be ignored once we start receiving duplicates.
36844  
36845  
36846      if (event.dts < this.latestDts_) {
36847        // We've started getting older data, so set the flag.
36848        this.ignoreNextEqualDts_ = true;
36849        return;
36850      } else if (event.dts === this.latestDts_ && this.ignoreNextEqualDts_) {
36851        this.numSameDts_--;
36852  
36853        if (!this.numSameDts_) {
36854          // We've received the last duplicate packet, time to start processing again
36855          this.ignoreNextEqualDts_ = false;
vendor: 6,896 bytes, lines 36856-37102
36856        }
36857  
36858        return;
36859      } // parse out CC data packets and save them for later
36860  
36861  
36862      newCaptionPackets = captionPacketParser.parseCaptionPackets(event.pts, userData);
36863      this.captionPackets_ = this.captionPackets_.concat(newCaptionPackets);
36864  
36865      if (this.latestDts_ !== event.dts) {
36866        this.numSameDts_ = 0;
36867      }
36868  
36869      this.numSameDts_++;
36870      this.latestDts_ = event.dts;
36871    };
36872  
36873    CaptionStream.prototype.flushCCStreams = function (flushType) {
36874      this.ccStreams_.forEach(function (cc) {
36875        return flushType === 'flush' ? cc.flush() : cc.partialFlush();
36876      }, this);
36877    };
36878  
36879    CaptionStream.prototype.flushStream = function (flushType) {
36880      // make sure we actually parsed captions before proceeding
36881      if (!this.captionPackets_.length) {
36882        this.flushCCStreams(flushType);
36883        return;
36884      } // In Chrome, the Array#sort function is not stable so add a
36885      // presortIndex that we can use to ensure we get a stable-sort
36886  
36887  
36888      this.captionPackets_.forEach(function (elem, idx) {
36889        elem.presortIndex = idx;
36890      }); // sort caption byte-pairs based on their PTS values
36891  
36892      this.captionPackets_.sort(function (a, b) {
36893        if (a.pts === b.pts) {
36894          return a.presortIndex - b.presortIndex;
36895        }
36896  
36897        return a.pts - b.pts;
36898      });
36899      this.captionPackets_.forEach(function (packet) {
36900        if (packet.type < 2) {
36901          // Dispatch packet to the right Cea608Stream
36902          this.dispatchCea608Packet(packet);
36903        } // this is where an 'else' would go for a dispatching packets
36904        // to a theoretical Cea708Stream that handles SERVICEn data
36905  
36906      }, this);
36907      this.captionPackets_.length = 0;
36908      this.flushCCStreams(flushType);
36909    };
36910  
36911    CaptionStream.prototype.flush = function () {
36912      return this.flushStream('flush');
36913    }; // Only called if handling partial data
36914  
36915  
36916    CaptionStream.prototype.partialFlush = function () {
36917      return this.flushStream('partialFlush');
36918    };
36919  
36920    CaptionStream.prototype.reset = function () {
36921      this.latestDts_ = null;
36922      this.ignoreNextEqualDts_ = false;
36923      this.numSameDts_ = 0;
36924      this.activeCea608Channel_ = [null, null];
36925      this.ccStreams_.forEach(function (ccStream) {
36926        ccStream.reset();
36927      });
36928    }; // From the CEA-608 spec:
36929  
36930    /*
36931     * When XDS sub-packets are interleaved with other services, the end of each sub-packet shall be followed
36932     * by a control pair to change to a different service. When any of the control codes from 0x10 to 0x1F is
36933     * used to begin a control code pair, it indicates the return to captioning or Text data. The control code pair
36934     * and subsequent data should then be processed according to the FCC rules. It may be necessary for the
36935     * line 21 data encoder to automatically insert a control code pair (i.e. RCL, RU2, RU3, RU4, RDC, or RTD)
36936     * to switch to captioning or Text.
36937    */
36938    // With that in mind, we ignore any data between an XDS control code and a
36939    // subsequent closed-captioning control code.
36940  
36941  
36942    CaptionStream.prototype.dispatchCea608Packet = function (packet) {
36943      // NOTE: packet.type is the CEA608 field
36944      if (this.setsTextOrXDSActive(packet)) {
36945        this.activeCea608Channel_[packet.type] = null;
36946      } else if (this.setsChannel1Active(packet)) {
36947        this.activeCea608Channel_[packet.type] = 0;
36948      } else if (this.setsChannel2Active(packet)) {
36949        this.activeCea608Channel_[packet.type] = 1;
36950      }
36951  
36952      if (this.activeCea608Channel_[packet.type] === null) {
36953        // If we haven't received anything to set the active channel, or the
36954        // packets are Text/XDS data, discard the data; we don't want jumbled
36955        // captions
36956        return;
36957      }
36958  
36959      this.ccStreams_[(packet.type << 1) + this.activeCea608Channel_[packet.type]].push(packet);
36960    };
36961  
36962    CaptionStream.prototype.setsChannel1Active = function (packet) {
36963      return (packet.ccData & 0x7800) === 0x1000;
36964    };
36965  
36966    CaptionStream.prototype.setsChannel2Active = function (packet) {
36967      return (packet.ccData & 0x7800) === 0x1800;
36968    };
36969  
36970    CaptionStream.prototype.setsTextOrXDSActive = function (packet) {
36971      return (packet.ccData & 0x7100) === 0x0100 || (packet.ccData & 0x78fe) === 0x102a || (packet.ccData & 0x78fe) === 0x182a;
36972    }; // ----------------------
36973    // Session to Application
36974    // ----------------------
36975    // This hash maps non-ASCII, special, and extended character codes to their
36976    // proper Unicode equivalent. The first keys that are only a single byte
36977    // are the non-standard ASCII characters, which simply map the CEA608 byte
36978    // to the standard ASCII/Unicode. The two-byte keys that follow are the CEA608
36979    // character codes, but have their MSB bitmasked with 0x03 so that a lookup
36980    // can be performed regardless of the field and data channel on which the
36981    // character code was received.
36982  
36983  
36984    var CHARACTER_TRANSLATION = {
36985      0x2a: 0xe1,
36986      // á
36987      0x5c: 0xe9,
36988      // é
36989      0x5e: 0xed,
36990      // í
36991      0x5f: 0xf3,
36992      // ó
36993      0x60: 0xfa,
36994      // ú
36995      0x7b: 0xe7,
36996      // ç
36997      0x7c: 0xf7,
36998      // ÷
36999      0x7d: 0xd1,
37000      // Ñ
37001      0x7e: 0xf1,
37002      // ñ
37003      0x7f: 0x2588,
37004      // █
37005      0x0130: 0xae,
37006      // ®
37007      0x0131: 0xb0,
37008      // °
37009      0x0132: 0xbd,
37010      // ½
37011      0x0133: 0xbf,
37012      // ¿
37013      0x0134: 0x2122,
37014      // ™
37015      0x0135: 0xa2,
37016      // ¢
37017      0x0136: 0xa3,
37018      // £
37019      0x0137: 0x266a,
37020      // ♪
37021      0x0138: 0xe0,
37022      // à
37023      0x0139: 0xa0,
37024      //
37025      0x013a: 0xe8,
37026      // è
37027      0x013b: 0xe2,
37028      // â
37029      0x013c: 0xea,
37030      // ê
37031      0x013d: 0xee,
37032      // î
37033      0x013e: 0xf4,
37034      // ô
37035      0x013f: 0xfb,
37036      // û
37037      0x0220: 0xc1,
37038      // Á
37039      0x0221: 0xc9,
37040      // É
37041      0x0222: 0xd3,
37042      // Ó
37043      0x0223: 0xda,
37044      // Ú
37045      0x0224: 0xdc,
37046      // Ü
37047      0x0225: 0xfc,
37048      // ü
37049      0x0226: 0x2018,
37050      // ‘
37051      0x0227: 0xa1,
37052      // ¡
37053      0x0228: 0x2a,
37054      // *
37055      0x0229: 0x27,
37056      // '
37057      0x022a: 0x2014,
37058      // —
37059      0x022b: 0xa9,
37060      // ©
37061      0x022c: 0x2120,
37062      // ℠
37063      0x022d: 0x2022,
37064      // •
37065      0x022e: 0x201c,
37066      // “
37067      0x022f: 0x201d,
37068      // ”
37069      0x0230: 0xc0,
37070      // À
37071      0x0231: 0xc2,
37072      // Â
37073      0x0232: 0xc7,
37074      // Ç
37075      0x0233: 0xc8,
37076      // È
37077      0x0234: 0xca,
37078      // Ê
37079      0x0235: 0xcb,
37080      // Ë
37081      0x0236: 0xeb,
37082      // ë
37083      0x0237: 0xce,
37084      // Î
37085      0x0238: 0xcf,
37086      // Ï
37087      0x0239: 0xef,
37088      // ï
37089      0x023a: 0xd4,
37090      // Ô
37091      0x023b: 0xd9,
37092      // Ù
37093      0x023c: 0xf9,
37094      // ù
37095      0x023d: 0xdb,
37096      // Û
37097      0x023e: 0xab,
37098      // «
37099      0x023f: 0xbb,
37100      // »
37101      0x0320: 0xc3,
37102      // Ã
vendor: 19,499 bytes, lines 37103-37635
37103      0x0321: 0xe3,
37104      // ã
37105      0x0322: 0xcd,
37106      // Í
37107      0x0323: 0xcc,
37108      // Ì
37109      0x0324: 0xec,
37110      // ì
37111      0x0325: 0xd2,
37112      // Ò
37113      0x0326: 0xf2,
37114      // ò
37115      0x0327: 0xd5,
37116      // Õ
37117      0x0328: 0xf5,
37118      // õ
37119      0x0329: 0x7b,
37120      // {
37121      0x032a: 0x7d,
37122      // }
37123      0x032b: 0x5c,
37124      // \
37125      0x032c: 0x5e,
37126      // ^
37127      0x032d: 0x5f,
37128      // _
37129      0x032e: 0x7c,
37130      // |
37131      0x032f: 0x7e,
37132      // ~
37133      0x0330: 0xc4,
37134      // Ä
37135      0x0331: 0xe4,
37136      // ä
37137      0x0332: 0xd6,
37138      // Ö
37139      0x0333: 0xf6,
37140      // ö
37141      0x0334: 0xdf,
37142      // ß
37143      0x0335: 0xa5,
37144      // ¥
37145      0x0336: 0xa4,
37146      // ¤
37147      0x0337: 0x2502,
37148      // │
37149      0x0338: 0xc5,
37150      // Å
37151      0x0339: 0xe5,
37152      // å
37153      0x033a: 0xd8,
37154      // Ø
37155      0x033b: 0xf8,
37156      // ø
37157      0x033c: 0x250c,
37158      // ┌
37159      0x033d: 0x2510,
37160      // ┐
37161      0x033e: 0x2514,
37162      // └
37163      0x033f: 0x2518 // ┘
37164  
37165    };
37166  
37167    var getCharFromCode = function getCharFromCode(code) {
37168      if (code === null) {
37169        return '';
37170      }
37171  
37172      code = CHARACTER_TRANSLATION[code] || code;
37173      return String.fromCharCode(code);
37174    }; // the index of the last row in a CEA-608 display buffer
37175  
37176  
37177    var BOTTOM_ROW = 14; // This array is used for mapping PACs -> row #, since there's no way of
37178    // getting it through bit logic.
37179  
37180    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
37181    // cells. The "bottom" row is the last element in the outer array.
37182  
37183    var createDisplayBuffer = function createDisplayBuffer() {
37184      var result = [],
37185          i = BOTTOM_ROW + 1;
37186  
37187      while (i--) {
37188        result.push('');
37189      }
37190  
37191      return result;
37192    };
37193  
37194    var Cea608Stream = function Cea608Stream(field, dataChannel) {
37195      Cea608Stream.prototype.init.call(this);
37196      this.field_ = field || 0;
37197      this.dataChannel_ = dataChannel || 0;
37198      this.name_ = 'CC' + ((this.field_ << 1 | this.dataChannel_) + 1);
37199      this.setConstants();
37200      this.reset();
37201  
37202      this.push = function (packet) {
37203        var data, swap, char0, char1, text; // remove the parity bits
37204  
37205        data = packet.ccData & 0x7f7f; // ignore duplicate control codes; the spec demands they're sent twice
37206  
37207        if (data === this.lastControlCode_) {
37208          this.lastControlCode_ = null;
37209          return;
37210        } // Store control codes
37211  
37212  
37213        if ((data & 0xf000) === 0x1000) {
37214          this.lastControlCode_ = data;
37215        } else if (data !== this.PADDING_) {
37216          this.lastControlCode_ = null;
37217        }
37218  
37219        char0 = data >>> 8;
37220        char1 = data & 0xff;
37221  
37222        if (data === this.PADDING_) {
37223          return;
37224        } else if (data === this.RESUME_CAPTION_LOADING_) {
37225          this.mode_ = 'popOn';
37226        } else if (data === this.END_OF_CAPTION_) {
37227          // If an EOC is received while in paint-on mode, the displayed caption
37228          // text should be swapped to non-displayed memory as if it was a pop-on
37229          // caption. Because of that, we should explicitly switch back to pop-on
37230          // mode
37231          this.mode_ = 'popOn';
37232          this.clearFormatting(packet.pts); // if a caption was being displayed, it's gone now
37233  
37234          this.flushDisplayed(packet.pts); // flip memory
37235  
37236          swap = this.displayed_;
37237          this.displayed_ = this.nonDisplayed_;
37238          this.nonDisplayed_ = swap; // start measuring the time to display the caption
37239  
37240          this.startPts_ = packet.pts;
37241        } else if (data === this.ROLL_UP_2_ROWS_) {
37242          this.rollUpRows_ = 2;
37243          this.setRollUp(packet.pts);
37244        } else if (data === this.ROLL_UP_3_ROWS_) {
37245          this.rollUpRows_ = 3;
37246          this.setRollUp(packet.pts);
37247        } else if (data === this.ROLL_UP_4_ROWS_) {
37248          this.rollUpRows_ = 4;
37249          this.setRollUp(packet.pts);
37250        } else if (data === this.CARRIAGE_RETURN_) {
37251          this.clearFormatting(packet.pts);
37252          this.flushDisplayed(packet.pts);
37253          this.shiftRowsUp_();
37254          this.startPts_ = packet.pts;
37255        } else if (data === this.BACKSPACE_) {
37256          if (this.mode_ === 'popOn') {
37257            this.nonDisplayed_[this.row_] = this.nonDisplayed_[this.row_].slice(0, -1);
37258          } else {
37259            this.displayed_[this.row_] = this.displayed_[this.row_].slice(0, -1);
37260          }
37261        } else if (data === this.ERASE_DISPLAYED_MEMORY_) {
37262          this.flushDisplayed(packet.pts);
37263          this.displayed_ = createDisplayBuffer();
37264        } else if (data === this.ERASE_NON_DISPLAYED_MEMORY_) {
37265          this.nonDisplayed_ = createDisplayBuffer();
37266        } else if (data === this.RESUME_DIRECT_CAPTIONING_) {
37267          if (this.mode_ !== 'paintOn') {
37268            // NOTE: This should be removed when proper caption positioning is
37269            // implemented
37270            this.flushDisplayed(packet.pts);
37271            this.displayed_ = createDisplayBuffer();
37272          }
37273  
37274          this.mode_ = 'paintOn';
37275          this.startPts_ = packet.pts; // Append special characters to caption text
37276        } else if (this.isSpecialCharacter(char0, char1)) {
37277          // Bitmask char0 so that we can apply character transformations
37278          // regardless of field and data channel.
37279          // Then byte-shift to the left and OR with char1 so we can pass the
37280          // entire character code to `getCharFromCode`.
37281          char0 = (char0 & 0x03) << 8;
37282          text = getCharFromCode(char0 | char1);
37283          this[this.mode_](packet.pts, text);
37284          this.column_++; // Append extended characters to caption text
37285        } else if (this.isExtCharacter(char0, char1)) {
37286          // Extended characters always follow their "non-extended" equivalents.
37287          // IE if a "è" is desired, you'll always receive "eè"; non-compliant
37288          // decoders are supposed to drop the "è", while compliant decoders
37289          // backspace the "e" and insert "è".
37290          // Delete the previous character
37291          if (this.mode_ === 'popOn') {
37292            this.nonDisplayed_[this.row_] = this.nonDisplayed_[this.row_].slice(0, -1);
37293          } else {
37294            this.displayed_[this.row_] = this.displayed_[this.row_].slice(0, -1);
37295          } // Bitmask char0 so that we can apply character transformations
37296          // regardless of field and data channel.
37297          // Then byte-shift to the left and OR with char1 so we can pass the
37298          // entire character code to `getCharFromCode`.
37299  
37300  
37301          char0 = (char0 & 0x03) << 8;
37302          text = getCharFromCode(char0 | char1);
37303          this[this.mode_](packet.pts, text);
37304          this.column_++; // Process mid-row codes
37305        } else if (this.isMidRowCode(char0, char1)) {
37306          // Attributes are not additive, so clear all formatting
37307          this.clearFormatting(packet.pts); // According to the standard, mid-row codes
37308          // should be replaced with spaces, so add one now
37309  
37310          this[this.mode_](packet.pts, ' ');
37311          this.column_++;
37312  
37313          if ((char1 & 0xe) === 0xe) {
37314            this.addFormatting(packet.pts, ['i']);
37315          }
37316  
37317          if ((char1 & 0x1) === 0x1) {
37318            this.addFormatting(packet.pts, ['u']);
37319          } // Detect offset control codes and adjust cursor
37320  
37321        } else if (this.isOffsetControlCode(char0, char1)) {
37322          // Cursor position is set by indent PAC (see below) in 4-column
37323          // increments, with an additional offset code of 1-3 to reach any
37324          // of the 32 columns specified by CEA-608. So all we need to do
37325          // here is increment the column cursor by the given offset.
37326          this.column_ += char1 & 0x03; // Detect PACs (Preamble Address Codes)
37327        } else if (this.isPAC(char0, char1)) {
37328          // There's no logic for PAC -> row mapping, so we have to just
37329          // find the row code in an array and use its index :(
37330          var row = ROWS.indexOf(data & 0x1f20); // Configure the caption window if we're in roll-up mode
37331  
37332          if (this.mode_ === 'rollUp') {
37333            // This implies that the base row is incorrectly set.
37334            // As per the recommendation in CEA-608(Base Row Implementation), defer to the number
37335            // of roll-up rows set.
37336            if (row - this.rollUpRows_ + 1 < 0) {
37337              row = this.rollUpRows_ - 1;
37338            }
37339  
37340            this.setRollUp(packet.pts, row);
37341          }
37342  
37343          if (row !== this.row_) {
37344            // formatting is only persistent for current row
37345            this.clearFormatting(packet.pts);
37346            this.row_ = row;
37347          } // All PACs can apply underline, so detect and apply
37348          // (All odd-numbered second bytes set underline)
37349  
37350  
37351          if (char1 & 0x1 && this.formatting_.indexOf('u') === -1) {
37352            this.addFormatting(packet.pts, ['u']);
37353          }
37354  
37355          if ((data & 0x10) === 0x10) {
37356            // We've got an indent level code. Each successive even number
37357            // increments the column cursor by 4, so we can get the desired
37358            // column position by bit-shifting to the right (to get n/2)
37359            // and multiplying by 4.
37360            this.column_ = ((data & 0xe) >> 1) * 4;
37361          }
37362  
37363          if (this.isColorPAC(char1)) {
37364            // it's a color code, though we only support white, which
37365            // can be either normal or italicized. white italics can be
37366            // either 0x4e or 0x6e depending on the row, so we just
37367            // bitwise-and with 0xe to see if italics should be turned on
37368            if ((char1 & 0xe) === 0xe) {
37369              this.addFormatting(packet.pts, ['i']);
37370            }
37371          } // We have a normal character in char0, and possibly one in char1
37372  
37373        } else if (this.isNormalChar(char0)) {
37374          if (char1 === 0x00) {
37375            char1 = null;
37376          }
37377  
37378          text = getCharFromCode(char0);
37379          text += getCharFromCode(char1);
37380          this[this.mode_](packet.pts, text);
37381          this.column_ += text.length;
37382        } // finish data processing
37383  
37384      };
37385    };
37386  
37387    Cea608Stream.prototype = new stream(); // Trigger a cue point that captures the current state of the
37388    // display buffer
37389  
37390    Cea608Stream.prototype.flushDisplayed = function (pts) {
37391      var content = this.displayed_ // remove spaces from the start and end of the string
37392      .map(function (row) {
37393        try {
37394          return row.trim();
37395        } catch (e) {
37396          // Ordinarily, this shouldn't happen. However, caption
37397          // parsing errors should not throw exceptions and
37398          // break playback.
37399          // eslint-disable-next-line no-console
37400          console.error('Skipping malformed caption.');
37401          return '';
37402        }
37403      }) // combine all text rows to display in one cue
37404      .join('\n') // and remove blank rows from the start and end, but not the middle
37405      .replace(/^\n+|\n+$/g, '');
37406  
37407      if (content.length) {
37408        this.trigger('data', {
37409          startPts: this.startPts_,
37410          endPts: pts,
37411          text: content,
37412          stream: this.name_
37413        });
37414      }
37415    };
37416    /**
37417     * Zero out the data, used for startup and on seek
37418     */
37419  
37420  
37421    Cea608Stream.prototype.reset = function () {
37422      this.mode_ = 'popOn'; // When in roll-up mode, the index of the last row that will
37423      // actually display captions. If a caption is shifted to a row
37424      // with a lower index than this, it is cleared from the display
37425      // buffer
37426  
37427      this.topRow_ = 0;
37428      this.startPts_ = 0;
37429      this.displayed_ = createDisplayBuffer();
37430      this.nonDisplayed_ = createDisplayBuffer();
37431      this.lastControlCode_ = null; // Track row and column for proper line-breaking and spacing
37432  
37433      this.column_ = 0;
37434      this.row_ = BOTTOM_ROW;
37435      this.rollUpRows_ = 2; // This variable holds currently-applied formatting
37436  
37437      this.formatting_ = [];
37438    };
37439    /**
37440     * Sets up control code and related constants for this instance
37441     */
37442  
37443  
37444    Cea608Stream.prototype.setConstants = function () {
37445      // The following attributes have these uses:
37446      // ext_ :    char0 for mid-row codes, and the base for extended
37447      //           chars (ext_+0, ext_+1, and ext_+2 are char0s for
37448      //           extended codes)
37449      // control_: char0 for control codes, except byte-shifted to the
37450      //           left so that we can do this.control_ | CONTROL_CODE
37451      // offset_:  char0 for tab offset codes
37452      //
37453      // It's also worth noting that control codes, and _only_ control codes,
37454      // differ between field 1 and field2. Field 2 control codes are always
37455      // their field 1 value plus 1. That's why there's the "| field" on the
37456      // control value.
37457      if (this.dataChannel_ === 0) {
37458        this.BASE_ = 0x10;
37459        this.EXT_ = 0x11;
37460        this.CONTROL_ = (0x14 | this.field_) << 8;
37461        this.OFFSET_ = 0x17;
37462      } else if (this.dataChannel_ === 1) {
37463        this.BASE_ = 0x18;
37464        this.EXT_ = 0x19;
37465        this.CONTROL_ = (0x1c | this.field_) << 8;
37466        this.OFFSET_ = 0x1f;
37467      } // Constants for the LSByte command codes recognized by Cea608Stream. This
37468      // list is not exhaustive. For a more comprehensive listing and semantics see
37469      // http://www.gpo.gov/fdsys/pkg/CFR-2010-title47-vol1/pdf/CFR-2010-title47-vol1-sec15-119.pdf
37470      // Padding
37471  
37472  
37473      this.PADDING_ = 0x0000; // Pop-on Mode
37474  
37475      this.RESUME_CAPTION_LOADING_ = this.CONTROL_ | 0x20;
37476      this.END_OF_CAPTION_ = this.CONTROL_ | 0x2f; // Roll-up Mode
37477  
37478      this.ROLL_UP_2_ROWS_ = this.CONTROL_ | 0x25;
37479      this.ROLL_UP_3_ROWS_ = this.CONTROL_ | 0x26;
37480      this.ROLL_UP_4_ROWS_ = this.CONTROL_ | 0x27;
37481      this.CARRIAGE_RETURN_ = this.CONTROL_ | 0x2d; // paint-on mode
37482  
37483      this.RESUME_DIRECT_CAPTIONING_ = this.CONTROL_ | 0x29; // Erasure
37484  
37485      this.BACKSPACE_ = this.CONTROL_ | 0x21;
37486      this.ERASE_DISPLAYED_MEMORY_ = this.CONTROL_ | 0x2c;
37487      this.ERASE_NON_DISPLAYED_MEMORY_ = this.CONTROL_ | 0x2e;
37488    };
37489    /**
37490     * Detects if the 2-byte packet data is a special character
37491     *
37492     * Special characters have a second byte in the range 0x30 to 0x3f,
37493     * with the first byte being 0x11 (for data channel 1) or 0x19 (for
37494     * data channel 2).
37495     *
37496     * @param  {Integer} char0 The first byte
37497     * @param  {Integer} char1 The second byte
37498     * @return {Boolean}       Whether the 2 bytes are an special character
37499     */
37500  
37501  
37502    Cea608Stream.prototype.isSpecialCharacter = function (char0, char1) {
37503      return char0 === this.EXT_ && char1 >= 0x30 && char1 <= 0x3f;
37504    };
37505    /**
37506     * Detects if the 2-byte packet data is an extended character
37507     *
37508     * Extended characters have a second byte in the range 0x20 to 0x3f,
37509     * with the first byte being 0x12 or 0x13 (for data channel 1) or
37510     * 0x1a or 0x1b (for data channel 2).
37511     *
37512     * @param  {Integer} char0 The first byte
37513     * @param  {Integer} char1 The second byte
37514     * @return {Boolean}       Whether the 2 bytes are an extended character
37515     */
37516  
37517  
37518    Cea608Stream.prototype.isExtCharacter = function (char0, char1) {
37519      return (char0 === this.EXT_ + 1 || char0 === this.EXT_ + 2) && char1 >= 0x20 && char1 <= 0x3f;
37520    };
37521    /**
37522     * Detects if the 2-byte packet is a mid-row code
37523     *
37524     * Mid-row codes have a second byte in the range 0x20 to 0x2f, with
37525     * the first byte being 0x11 (for data channel 1) or 0x19 (for data
37526     * channel 2).
37527     *
37528     * @param  {Integer} char0 The first byte
37529     * @param  {Integer} char1 The second byte
37530     * @return {Boolean}       Whether the 2 bytes are a mid-row code
37531     */
37532  
37533  
37534    Cea608Stream.prototype.isMidRowCode = function (char0, char1) {
37535      return char0 === this.EXT_ && char1 >= 0x20 && char1 <= 0x2f;
37536    };
37537    /**
37538     * Detects if the 2-byte packet is an offset control code
37539     *
37540     * Offset control codes have a second byte in the range 0x21 to 0x23,
37541     * with the first byte being 0x17 (for data channel 1) or 0x1f (for
37542     * data channel 2).
37543     *
37544     * @param  {Integer} char0 The first byte
37545     * @param  {Integer} char1 The second byte
37546     * @return {Boolean}       Whether the 2 bytes are an offset control code
37547     */
37548  
37549  
37550    Cea608Stream.prototype.isOffsetControlCode = function (char0, char1) {
37551      return char0 === this.OFFSET_ && char1 >= 0x21 && char1 <= 0x23;
37552    };
37553    /**
37554     * Detects if the 2-byte packet is a Preamble Address Code
37555     *
37556     * PACs have a first byte in the range 0x10 to 0x17 (for data channel 1)
37557     * or 0x18 to 0x1f (for data channel 2), with the second byte in the
37558     * range 0x40 to 0x7f.
37559     *
37560     * @param  {Integer} char0 The first byte
37561     * @param  {Integer} char1 The second byte
37562     * @return {Boolean}       Whether the 2 bytes are a PAC
37563     */
37564  
37565  
37566    Cea608Stream.prototype.isPAC = function (char0, char1) {
37567      return char0 >= this.BASE_ && char0 < this.BASE_ + 8 && char1 >= 0x40 && char1 <= 0x7f;
37568    };
37569    /**
37570     * Detects if a packet's second byte is in the range of a PAC color code
37571     *
37572     * PAC color codes have the second byte be in the range 0x40 to 0x4f, or
37573     * 0x60 to 0x6f.
37574     *
37575     * @param  {Integer} char1 The second byte
37576     * @return {Boolean}       Whether the byte is a color PAC
37577     */
37578  
37579  
37580    Cea608Stream.prototype.isColorPAC = function (char1) {
37581      return char1 >= 0x40 && char1 <= 0x4f || char1 >= 0x60 && char1 <= 0x7f;
37582    };
37583    /**
37584     * Detects if a single byte is in the range of a normal character
37585     *
37586     * Normal text bytes are in the range 0x20 to 0x7f.
37587     *
37588     * @param  {Integer} char  The byte
37589     * @return {Boolean}       Whether the byte is a normal character
37590     */
37591  
37592  
37593    Cea608Stream.prototype.isNormalChar = function (_char) {
37594      return _char >= 0x20 && _char <= 0x7f;
37595    };
37596    /**
37597     * Configures roll-up
37598     *
37599     * @param  {Integer} pts         Current PTS
37600     * @param  {Integer} newBaseRow  Used by PACs to slide the current window to
37601     *                               a new position
37602     */
37603  
37604  
37605    Cea608Stream.prototype.setRollUp = function (pts, newBaseRow) {
37606      // Reset the base row to the bottom row when switching modes
37607      if (this.mode_ !== 'rollUp') {
37608        this.row_ = BOTTOM_ROW;
37609        this.mode_ = 'rollUp'; // Spec says to wipe memories when switching to roll-up
37610  
37611        this.flushDisplayed(pts);
37612        this.nonDisplayed_ = createDisplayBuffer();
37613        this.displayed_ = createDisplayBuffer();
37614      }
37615  
37616      if (newBaseRow !== undefined && newBaseRow !== this.row_) {
37617        // move currently displayed captions (up or down) to the new base row
37618        for (var i = 0; i < this.rollUpRows_; i++) {
37619          this.displayed_[newBaseRow - i] = this.displayed_[this.row_ - i];
37620          this.displayed_[this.row_ - i] = '';
37621        }
37622      }
37623  
37624      if (newBaseRow === undefined) {
37625        newBaseRow = this.row_;
37626      }
37627  
37628      this.topRow_ = newBaseRow - this.rollUpRows_ + 1;
37629    }; // Adds the opening HTML tag for the passed character to the caption text,
37630    // and keeps track of it for later closing
37631  
37632  
37633    Cea608Stream.prototype.addFormatting = function (pts, format) {
37634      this.formatting_ = this.formatting_.concat(format);
37635      var text = format.reduce(function (text, format) {
vendor: 3,099 bytes, lines 37636-37736
37636        return text + '<' + format + '>';
37637      }, '');
37638      this[this.mode_](pts, text);
37639    }; // Adds HTML closing tags for current formatting to caption text and
37640    // clears remembered formatting
37641  
37642  
37643    Cea608Stream.prototype.clearFormatting = function (pts) {
37644      if (!this.formatting_.length) {
37645        return;
37646      }
37647  
37648      var text = this.formatting_.reverse().reduce(function (text, format) {
37649        return text + '</' + format + '>';
37650      }, '');
37651      this.formatting_ = [];
37652      this[this.mode_](pts, text);
37653    }; // Mode Implementations
37654  
37655  
37656    Cea608Stream.prototype.popOn = function (pts, text) {
37657      var baseRow = this.nonDisplayed_[this.row_]; // buffer characters
37658  
37659      baseRow += text;
37660      this.nonDisplayed_[this.row_] = baseRow;
37661    };
37662  
37663    Cea608Stream.prototype.rollUp = function (pts, text) {
37664      var baseRow = this.displayed_[this.row_];
37665      baseRow += text;
37666      this.displayed_[this.row_] = baseRow;
37667    };
37668  
37669    Cea608Stream.prototype.shiftRowsUp_ = function () {
37670      var i; // clear out inactive rows
37671  
37672      for (i = 0; i < this.topRow_; i++) {
37673        this.displayed_[i] = '';
37674      }
37675  
37676      for (i = this.row_ + 1; i < BOTTOM_ROW + 1; i++) {
37677        this.displayed_[i] = '';
37678      } // shift displayed rows up
37679  
37680  
37681      for (i = this.topRow_; i < this.row_; i++) {
37682        this.displayed_[i] = this.displayed_[i + 1];
37683      } // clear out the bottom row
37684  
37685  
37686      this.displayed_[this.row_] = '';
37687    };
37688  
37689    Cea608Stream.prototype.paintOn = function (pts, text) {
37690      var baseRow = this.displayed_[this.row_];
37691      baseRow += text;
37692      this.displayed_[this.row_] = baseRow;
37693    }; // exports
37694  
37695  
37696    var captionStream = {
37697      CaptionStream: CaptionStream,
37698      Cea608Stream: Cea608Stream
37699    };
37700  
37701    var discardEmulationPreventionBytes$1 = captionPacketParser.discardEmulationPreventionBytes;
37702    var CaptionStream$1 = captionStream.CaptionStream;
37703    /**
37704      * Maps an offset in the mdat to a sample based on the the size of the samples.
37705      * Assumes that `parseSamples` has been called first.
37706      *
37707      * @param {Number} offset - The offset into the mdat
37708      * @param {Object[]} samples - An array of samples, parsed using `parseSamples`
37709      * @return {?Object} The matching sample, or null if no match was found.
37710      *
37711      * @see ISO-BMFF-12/2015, Section 8.8.8
37712     **/
37713  
37714    var mapToSample = function mapToSample(offset, samples) {
37715      var approximateOffset = offset;
37716  
37717      for (var i = 0; i < samples.length; i++) {
37718        var sample = samples[i];
37719  
37720        if (approximateOffset < sample.size) {
37721          return sample;
37722        }
37723  
37724        approximateOffset -= sample.size;
37725      }
37726  
37727      return null;
37728    };
37729    /**
37730      * Finds SEI nal units contained in a Media Data Box.
37731      * Assumes that `parseSamples` has been called first.
37732      *
37733      * @param {Uint8Array} avcStream - The bytes of the mdat
37734      * @param {Object[]} samples - The samples parsed out by `parseSamples`
37735      * @param {Number} trackId - The trackId of this video track
37736      * @return {Object[]}
vendor: 8,256 bytes, lines 37736-37961
37736 seiNals - the parsed SEI NALUs found.
37737      *   The contents of the seiNal should match what is expected by
37738      *   CaptionStream.push (nalUnitType, size, data, escapedRBSP, pts, dts)
37739      *
37740      * @see ISO-BMFF-12/2015, Section 8.1.1
37741      * @see Rec. ITU-T H.264, 7.3.2.3.1
37742     **/
37743  
37744  
37745    var findSeiNals = function findSeiNals(avcStream, samples, trackId) {
37746      var avcView = new DataView(avcStream.buffer, avcStream.byteOffset, avcStream.byteLength),
37747          result = [],
37748          seiNal,
37749          i,
37750          length,
37751          lastMatchedSample;
37752  
37753      for (i = 0; i + 4 < avcStream.length; i += length) {
37754        length = avcView.getUint32(i);
37755        i += 4; // Bail if this doesn't appear to be an H264 stream
37756  
37757        if (length <= 0) {
37758          continue;
37759        }
37760  
37761        switch (avcStream[i] & 0x1F) {
37762          case 0x06:
37763            var data = avcStream.subarray(i + 1, i + 1 + length);
37764            var matchingSample = mapToSample(i, samples);
37765            seiNal = {
37766              nalUnitType: 'sei_rbsp',
37767              size: length,
37768              data: data,
37769              escapedRBSP: discardEmulationPreventionBytes$1(data),
37770              trackId: trackId
37771            };
37772  
37773            if (matchingSample) {
37774              seiNal.pts = matchingSample.pts;
37775              seiNal.dts = matchingSample.dts;
37776              lastMatchedSample = matchingSample;
37777            } else {
37778              // If a matching sample cannot be found, use the last
37779              // sample's values as they should be as close as possible
37780              seiNal.pts = lastMatchedSample.pts;
37781              seiNal.dts = lastMatchedSample.dts;
37782            }
37783  
37784            result.push(seiNal);
37785            break;
37786        }
37787      }
37788  
37789      return result;
37790    };
37791    /**
37792      * Parses sample information out of Track Run Boxes and calculates
37793      * the absolute presentation and decode timestamps of each sample.
37794      *
37795      * @param {Array<Uint8Array>} truns - The Trun Run boxes to be parsed
37796      * @param {Number} baseMediaDecodeTime - base media decode time from tfdt
37797          @see ISO-BMFF-12/2015, Section 8.8.12
37798      * @param {Object} tfhd - The parsed Track Fragment Header
37799      *   @see inspect.parseTfhd
37800      * @return {Object[]} the parsed samples
37801      *
37802      * @see ISO-BMFF-12/2015, Section 8.8.8
37803     **/
37804  
37805  
37806    var parseSamples = function parseSamples(truns, baseMediaDecodeTime, tfhd) {
37807      var currentDts = baseMediaDecodeTime;
37808      var defaultSampleDuration = tfhd.defaultSampleDuration || 0;
37809      var defaultSampleSize = tfhd.defaultSampleSize || 0;
37810      var trackId = tfhd.trackId;
37811      var allSamples = [];
37812      truns.forEach(function (trun) {
37813        // Note: We currently do not parse the sample table as well
37814        // as the trun. It's possible some sources will require this.
37815        // moov > trak > mdia > minf > stbl
37816        var trackRun = mp4Inspector.parseTrun(trun);
37817        var samples = trackRun.samples;
37818        samples.forEach(function (sample) {
37819          if (sample.duration === undefined) {
37820            sample.duration = defaultSampleDuration;
37821          }
37822  
37823          if (sample.size === undefined) {
37824            sample.size = defaultSampleSize;
37825          }
37826  
37827          sample.trackId = trackId;
37828          sample.dts = currentDts;
37829  
37830          if (sample.compositionTimeOffset === undefined) {
37831            sample.compositionTimeOffset = 0;
37832          }
37833  
37834          sample.pts = currentDts + sample.compositionTimeOffset;
37835          currentDts += sample.duration;
37836        });
37837        allSamples = allSamples.concat(samples);
37838      });
37839      return allSamples;
37840    };
37841    /**
37842      * Parses out caption nals from an FMP4 segment's video tracks.
37843      *
37844      * @param {Uint8Array} segment - The bytes of a single segment
37845      * @param {Number} videoTrackId - The trackId of a video track in the segment
37846      * @return {Object.<Number, Object[]>} A mapping of video trackId to
37847      *   a list of seiNals found in that track
37848     **/
37849  
37850  
37851    var parseCaptionNals = function parseCaptionNals(segment, videoTrackId) {
37852      // To get the samples
37853      var trafs = probe.findBox(segment, ['moof', 'traf']); // To get SEI NAL units
37854  
37855      var mdats = probe.findBox(segment, ['mdat']);
37856      var captionNals = {};
37857      var mdatTrafPairs = []; // Pair up each traf with a mdat as moofs and mdats are in pairs
37858  
37859      mdats.forEach(function (mdat, index) {
37860        var matchingTraf = trafs[index];
37861        mdatTrafPairs.push({
37862          mdat: mdat,
37863          traf: matchingTraf
37864        });
37865      });
37866      mdatTrafPairs.forEach(function (pair) {
37867        var mdat = pair.mdat;
37868        var traf = pair.traf;
37869        var tfhd = probe.findBox(traf, ['tfhd']); // Exactly 1 tfhd per traf
37870  
37871        var headerInfo = mp4Inspector.parseTfhd(tfhd[0]);
37872        var trackId = headerInfo.trackId;
37873        var tfdt = probe.findBox(traf, ['tfdt']); // Either 0 or 1 tfdt per traf
37874  
37875        var baseMediaDecodeTime = tfdt.length > 0 ? mp4Inspector.parseTfdt(tfdt[0]).baseMediaDecodeTime : 0;
37876        var truns = probe.findBox(traf, ['trun']);
37877        var samples;
37878        var seiNals; // Only parse video data for the chosen video track
37879  
37880        if (videoTrackId === trackId && truns.length > 0) {
37881          samples = parseSamples(truns, baseMediaDecodeTime, headerInfo);
37882          seiNals = findSeiNals(mdat, samples, trackId);
37883  
37884          if (!captionNals[trackId]) {
37885            captionNals[trackId] = [];
37886          }
37887  
37888          captionNals[trackId] = captionNals[trackId].concat(seiNals);
37889        }
37890      });
37891      return captionNals;
37892    };
37893    /**
37894      * Parses out inband captions from an MP4 container and returns
37895      * caption objects that can be used by WebVTT and the TextTrack API.
37896      * @see https://developer.mozilla.org/en-US/docs/Web/API/VTTCue
37897      * @see https://developer.mozilla.org/en-US/docs/Web/API/TextTrack
37898      * Assumes that `probe.getVideoTrackIds` and `probe.timescale` have been called first
37899      *
37900      * @param {Uint8Array} segment - The fmp4 segment containing embedded captions
37901      * @param {Number} trackId - The id of the video track to parse
37902      * @param {Number} timescale - The timescale for the video track from the init segment
37903      *
37904      * @return {?Object[]} parsedCaptions - A list of captions or null if no video tracks
37905      * @return {Number} parsedCaptions[].startTime - The time to show the caption in seconds
37906      * @return {Number} parsedCaptions[].endTime - The time to stop showing the caption in seconds
37907      * @return {String} parsedCaptions[].text - The visible content of the caption
37908     **/
37909  
37910  
37911    var parseEmbeddedCaptions = function parseEmbeddedCaptions(segment, trackId, timescale) {
37912      var seiNals; // the ISO-BMFF spec says that trackId can't be zero, but there's some broken content out there
37913  
37914      if (trackId === null) {
37915        return null;
37916      }
37917  
37918      seiNals = parseCaptionNals(segment, trackId);
37919      return {
37920        seiNals: seiNals[trackId],
37921        timescale: timescale
37922      };
37923    };
37924    /**
37925      * Converts SEI NALUs into captions that can be used by video.js
37926     **/
37927  
37928  
37929    var CaptionParser = function CaptionParser() {
37930      var isInitialized = false;
37931      var captionStream; // Stores segments seen before trackId and timescale are set
37932  
37933      var segmentCache; // Stores video track ID of the track being parsed
37934  
37935      var trackId; // Stores the timescale of the track being parsed
37936  
37937      var timescale; // Stores captions parsed so far
37938  
37939      var parsedCaptions; // Stores whether we are receiving partial data or not
37940  
37941      var parsingPartial;
37942      /**
37943        * A method to indicate whether a CaptionParser has been initalized
37944        * @returns {Boolean}
37945       **/
37946  
37947      this.isInitialized = function () {
37948        return isInitialized;
37949      };
37950      /**
37951        * Initializes the underlying CaptionStream, SEI NAL parsing
37952        * and management, and caption collection
37953       **/
37954  
37955  
37956      this.init = function (options) {
37957        captionStream = new CaptionStream$1();
37958        isInitialized = true;
37959        parsingPartial = options ? options.isPartial : false; // Collect dispatched captions
37960  
37961        captionStream.on('data', function (event) {
vendor: 4,700 bytes, lines 37962-38111
37962          // Convert to seconds in the source's timescale
37963          event.startTime = event.startPts / timescale;
37964          event.endTime = event.endPts / timescale;
37965          parsedCaptions.captions.push(event);
37966          parsedCaptions.captionStreams[event.stream] = true;
37967        });
37968      };
37969      /**
37970        * Determines if a new video track will be selected
37971        * or if the timescale changed
37972        * @return {Boolean}
37973       **/
37974  
37975  
37976      this.isNewInit = function (videoTrackIds, timescales) {
37977        if (videoTrackIds && videoTrackIds.length === 0 || timescales && typeof timescales === 'object' && Object.keys(timescales).length === 0) {
37978          return false;
37979        }
37980  
37981        return trackId !== videoTrackIds[0] || timescale !== timescales[trackId];
37982      };
37983      /**
37984        * Parses out SEI captions and interacts with underlying
37985        * CaptionStream to return dispatched captions
37986        *
37987        * @param {Uint8Array} segment - The fmp4 segment containing embedded captions
37988        * @param {Number[]} videoTrackIds - A list of video tracks found in the init segment
37989        * @param {Object.<Number, Number>} timescales - The timescales found in the init segment
37990        * @see parseEmbeddedCaptions
37991        * @see m2ts/caption-stream.js
37992       **/
37993  
37994  
37995      this.parse = function (segment, videoTrackIds, timescales) {
37996        var parsedData;
37997  
37998        if (!this.isInitialized()) {
37999          return null; // This is not likely to be a video segment
38000        } else if (!videoTrackIds || !timescales) {
38001          return null;
38002        } else if (this.isNewInit(videoTrackIds, timescales)) {
38003          // Use the first video track only as there is no
38004          // mechanism to switch to other video tracks
38005          trackId = videoTrackIds[0];
38006          timescale = timescales[trackId]; // If an init segment has not been seen yet, hold onto segment
38007          // data until we have one.
38008          // the ISO-BMFF spec says that trackId can't be zero, but there's some broken content out there
38009        } else if (trackId === null || !timescale) {
38010          segmentCache.push(segment);
38011          return null;
38012        } // Now that a timescale and trackId is set, parse cached segments
38013  
38014  
38015        while (segmentCache.length > 0) {
38016          var cachedSegment = segmentCache.shift();
38017          this.parse(cachedSegment, videoTrackIds, timescales);
38018        }
38019  
38020        parsedData = parseEmbeddedCaptions(segment, trackId, timescale);
38021  
38022        if (parsedData === null || !parsedData.seiNals) {
38023          return null;
38024        }
38025  
38026        this.pushNals(parsedData.seiNals); // Force the parsed captions to be dispatched
38027  
38028        this.flushStream();
38029        return parsedCaptions;
38030      };
38031      /**
38032        * Pushes SEI NALUs onto CaptionStream
38033        * @param {Object[]} nals - A list of SEI nals parsed using `parseCaptionNals`
38034        * Assumes that `parseCaptionNals` has been called first
38035        * @see m2ts/caption-stream.js
38036        **/
38037  
38038  
38039      this.pushNals = function (nals) {
38040        if (!this.isInitialized() || !nals || nals.length === 0) {
38041          return null;
38042        }
38043  
38044        nals.forEach(function (nal) {
38045          captionStream.push(nal);
38046        });
38047      };
38048      /**
38049        * Flushes underlying CaptionStream to dispatch processed, displayable captions
38050        * @see m2ts/caption-stream.js
38051       **/
38052  
38053  
38054      this.flushStream = function () {
38055        if (!this.isInitialized()) {
38056          return null;
38057        }
38058  
38059        if (!parsingPartial) {
38060          captionStream.flush();
38061        } else {
38062          captionStream.partialFlush();
38063        }
38064      };
38065      /**
38066        * Reset caption buckets for new data
38067       **/
38068  
38069  
38070      this.clearParsedCaptions = function () {
38071        parsedCaptions.captions = [];
38072        parsedCaptions.captionStreams = {};
38073      };
38074      /**
38075        * Resets underlying CaptionStream
38076        * @see m2ts/caption-stream.js
38077       **/
38078  
38079  
38080      this.resetCaptionStream = function () {
38081        if (!this.isInitialized()) {
38082          return null;
38083        }
38084  
38085        captionStream.reset();
38086      };
38087      /**
38088        * Convenience method to clear all captions flushed from the
38089        * CaptionStream and still being parsed
38090        * @see m2ts/caption-stream.js
38091       **/
38092  
38093  
38094      this.clearAllCaptions = function () {
38095        this.clearParsedCaptions();
38096        this.resetCaptionStream();
38097      };
38098      /**
38099        * Reset caption parser
38100       **/
38101  
38102  
38103      this.reset = function () {
38104        segmentCache = [];
38105        trackId = null;
38106        timescale = null;
38107  
38108        if (!parsedCaptions) {
38109          parsedCaptions = {
38110            captions: [],
38111            // CC1, CC2, CC3, CC4
vendor: 7,627 bytes, lines 38112-38345
38112            captionStreams: {}
38113          };
38114        } else {
38115          this.clearParsedCaptions();
38116        }
38117  
38118        this.resetCaptionStream();
38119      };
38120  
38121      this.reset();
38122    };
38123  
38124    var captionParser = CaptionParser;
38125  
38126    /**
38127     * mux.js
38128     *
38129     * Copyright (c) Brightcove
38130     * Licensed Apache-2.0 https://github.com/videojs/mux.js/blob/master/LICENSE
38131     */
38132  
38133    var streamTypes = {
38134      H264_STREAM_TYPE: 0x1B,
38135      ADTS_STREAM_TYPE: 0x0F,
38136      METADATA_STREAM_TYPE: 0x15
38137    };
38138  
38139    var MAX_TS = 8589934592;
38140    var RO_THRESH = 4294967296;
38141    var TYPE_SHARED = 'shared';
38142  
38143    var handleRollover = function handleRollover(value, reference) {
38144      var direction = 1;
38145  
38146      if (value > reference) {
38147        // If the current timestamp value is greater than our reference timestamp and we detect a
38148        // timestamp rollover, this means the roll over is happening in the opposite direction.
38149        // Example scenario: Enter a long stream/video just after a rollover occurred. The reference
38150        // point will be set to a small number, e.g. 1. The user then seeks backwards over the
38151        // rollover point. In loading this segment, the timestamp values will be very large,
38152        // e.g. 2^33 - 1. Since this comes before the data we loaded previously, we want to adjust
38153        // the time stamp to be `value - 2^33`.
38154        direction = -1;
38155      } // Note: A seek forwards or back that is greater than the RO_THRESH (2^32, ~13 hours) will
38156      // cause an incorrect adjustment.
38157  
38158  
38159      while (Math.abs(reference - value) > RO_THRESH) {
38160        value += direction * MAX_TS;
38161      }
38162  
38163      return value;
38164    };
38165  
38166    var TimestampRolloverStream = function TimestampRolloverStream(type) {
38167      var lastDTS, referenceDTS;
38168      TimestampRolloverStream.prototype.init.call(this); // The "shared" type is used in cases where a stream will contain muxed
38169      // video and audio. We could use `undefined` here, but having a string
38170      // makes debugging a little clearer.
38171  
38172      this.type_ = type || TYPE_SHARED;
38173  
38174      this.push = function (data) {
38175        // Any "shared" rollover streams will accept _all_ data. Otherwise,
38176        // streams will only accept data that matches their type.
38177        if (this.type_ !== TYPE_SHARED && data.type !== this.type_) {
38178          return;
38179        }
38180  
38181        if (referenceDTS === undefined) {
38182          referenceDTS = data.dts;
38183        }
38184  
38185        data.dts = handleRollover(data.dts, referenceDTS);
38186        data.pts = handleRollover(data.pts, referenceDTS);
38187        lastDTS = data.dts;
38188        this.trigger('data', data);
38189      };
38190  
38191      this.flush = function () {
38192        referenceDTS = lastDTS;
38193        this.trigger('done');
38194      };
38195  
38196      this.endTimeline = function () {
38197        this.flush();
38198        this.trigger('endedtimeline');
38199      };
38200  
38201      this.discontinuity = function () {
38202        referenceDTS = void 0;
38203        lastDTS = void 0;
38204      };
38205  
38206      this.reset = function () {
38207        this.discontinuity();
38208        this.trigger('reset');
38209      };
38210    };
38211  
38212    TimestampRolloverStream.prototype = new stream();
38213    var timestampRolloverStream = {
38214      TimestampRolloverStream: TimestampRolloverStream,
38215      handleRollover: handleRollover
38216    };
38217  
38218    var parsePid = function parsePid(packet) {
38219      var pid = packet[1] & 0x1f;
38220      pid <<= 8;
38221      pid |= packet[2];
38222      return pid;
38223    };
38224  
38225    var parsePayloadUnitStartIndicator = function parsePayloadUnitStartIndicator(packet) {
38226      return !!(packet[1] & 0x40);
38227    };
38228  
38229    var parseAdaptionField = function parseAdaptionField(packet) {
38230      var offset = 0; // if an adaption field is present, its length is specified by the
38231      // fifth byte of the TS packet header. The adaptation field is
38232      // used to add stuffing to PES packets that don't fill a complete
38233      // TS packet, and to specify some forms of timing and control data
38234      // that we do not currently use.
38235  
38236      if ((packet[3] & 0x30) >>> 4 > 0x01) {
38237        offset += packet[4] + 1;
38238      }
38239  
38240      return offset;
38241    };
38242  
38243    var parseType$1 = function parseType(packet, pmtPid) {
38244      var pid = parsePid(packet);
38245  
38246      if (pid === 0) {
38247        return 'pat';
38248      } else if (pid === pmtPid) {
38249        return 'pmt';
38250      } else if (pmtPid) {
38251        return 'pes';
38252      }
38253  
38254      return null;
38255    };
38256  
38257    var parsePat = function parsePat(packet) {
38258      var pusi = parsePayloadUnitStartIndicator(packet);
38259      var offset = 4 + parseAdaptionField(packet);
38260  
38261      if (pusi) {
38262        offset += packet[offset] + 1;
38263      }
38264  
38265      return (packet[offset + 10] & 0x1f) << 8 | packet[offset + 11];
38266    };
38267  
38268    var parsePmt = function parsePmt(packet) {
38269      var programMapTable = {};
38270      var pusi = parsePayloadUnitStartIndicator(packet);
38271      var payloadOffset = 4 + parseAdaptionField(packet);
38272  
38273      if (pusi) {
38274        payloadOffset += packet[payloadOffset] + 1;
38275      } // PMTs can be sent ahead of the time when they should actually
38276      // take effect. We don't believe this should ever be the case
38277      // for HLS but we'll ignore "forward" PMT declarations if we see
38278      // them. Future PMT declarations have the current_next_indicator
38279      // set to zero.
38280  
38281  
38282      if (!(packet[payloadOffset + 5] & 0x01)) {
38283        return;
38284      }
38285  
38286      var sectionLength, tableEnd, programInfoLength; // the mapping table ends at the end of the current section
38287  
38288      sectionLength = (packet[payloadOffset + 1] & 0x0f) << 8 | packet[payloadOffset + 2];
38289      tableEnd = 3 + sectionLength - 4; // to determine where the table is, we have to figure out how
38290      // long the program info descriptors are
38291  
38292      programInfoLength = (packet[payloadOffset + 10] & 0x0f) << 8 | packet[payloadOffset + 11]; // advance the offset to the first entry in the mapping table
38293  
38294      var offset = 12 + programInfoLength;
38295  
38296      while (offset < tableEnd) {
38297        var i = payloadOffset + offset; // add an entry that maps the elementary_pid to the stream_type
38298  
38299        programMapTable[(packet[i + 1] & 0x1F) << 8 | packet[i + 2]] = packet[i]; // move to the next table entry
38300        // skip past the elementary stream descriptors, if present
38301  
38302        offset += ((packet[i + 3] & 0x0F) << 8 | packet[i + 4]) + 5;
38303      }
38304  
38305      return programMapTable;
38306    };
38307  
38308    var parsePesType = function parsePesType(packet, programMapTable) {
38309      var pid = parsePid(packet);
38310      var type = programMapTable[pid];
38311  
38312      switch (type) {
38313        case streamTypes.H264_STREAM_TYPE:
38314          return 'video';
38315  
38316        case streamTypes.ADTS_STREAM_TYPE:
38317          return 'audio';
38318  
38319        case streamTypes.METADATA_STREAM_TYPE:
38320          return 'timed-metadata';
38321  
38322        default:
38323          return null;
38324      }
38325    };
38326  
38327    var parsePesTime = function parsePesTime(packet) {
38328      var pusi = parsePayloadUnitStartIndicator(packet);
38329  
38330      if (!pusi) {
38331        return null;
38332      }
38333  
38334      var offset = 4 + parseAdaptionField(packet);
38335  
38336      if (offset >= packet.byteLength) {
38337        // From the H 222.0 MPEG-TS spec
38338        // "For transport stream packets carrying PES packets, stuffing is needed when there
38339        //  is insufficient PES packet data to completely fill the transport stream packet
38340        //  payload bytes. Stuffing is accomplished by defining an adaptation field longer than
38341        //  the sum of the lengths of the data elements in it, so that the payload bytes
38342        //  remaining after the adaptation field exactly accommodates the available PES packet
38343        //  data."
38344        //
38345        // If the offset is >
vendor: 6,005 bytes, lines 38345-38522
38345= the length of the packet, then the packet contains no data
38346        // and instead is just adaption field stuffing bytes
38347        return null;
38348      }
38349  
38350      var pes = null;
38351      var ptsDtsFlags; // PES packets may be annotated with a PTS value, or a PTS value
38352      // and a DTS value. Determine what combination of values is
38353      // available to work with.
38354  
38355      ptsDtsFlags = packet[offset + 7]; // PTS and DTS are normally stored as a 33-bit number.  Javascript
38356      // performs all bitwise operations on 32-bit integers but javascript
38357      // supports a much greater range (52-bits) of integer using standard
38358      // mathematical operations.
38359      // We construct a 31-bit value using bitwise operators over the 31
38360      // most significant bits and then multiply by 4 (equal to a left-shift
38361      // of 2) before we add the final 2 least significant bits of the
38362      // timestamp (equal to an OR.)
38363  
38364      if (ptsDtsFlags & 0xC0) {
38365        pes = {}; // the PTS and DTS are not written out directly. For information
38366        // on how they are encoded, see
38367        // http://dvd.sourceforge.net/dvdinfo/pes-hdr.html
38368  
38369        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;
38370        pes.pts *= 4; // Left shift by 2
38371  
38372        pes.pts += (packet[offset + 13] & 0x06) >>> 1; // OR by the two LSBs
38373  
38374        pes.dts = pes.pts;
38375  
38376        if (ptsDtsFlags & 0x40) {
38377          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;
38378          pes.dts *= 4; // Left shift by 2
38379  
38380          pes.dts += (packet[offset + 18] & 0x06) >>> 1; // OR by the two LSBs
38381        }
38382      }
38383  
38384      return pes;
38385    };
38386  
38387    var parseNalUnitType = function parseNalUnitType(type) {
38388      switch (type) {
38389        case 0x05:
38390          return 'slice_layer_without_partitioning_rbsp_idr';
38391  
38392        case 0x06:
38393          return 'sei_rbsp';
38394  
38395        case 0x07:
38396          return 'seq_parameter_set_rbsp';
38397  
38398        case 0x08:
38399          return 'pic_parameter_set_rbsp';
38400  
38401        case 0x09:
38402          return 'access_unit_delimiter_rbsp';
38403  
38404        default:
38405          return null;
38406      }
38407    };
38408  
38409    var videoPacketContainsKeyFrame = function videoPacketContainsKeyFrame(packet) {
38410      var offset = 4 + parseAdaptionField(packet);
38411      var frameBuffer = packet.subarray(offset);
38412      var frameI = 0;
38413      var frameSyncPoint = 0;
38414      var foundKeyFrame = false;
38415      var nalType; // advance the sync point to a NAL start, if necessary
38416  
38417      for (; frameSyncPoint < frameBuffer.byteLength - 3; frameSyncPoint++) {
38418        if (frameBuffer[frameSyncPoint + 2] === 1) {
38419          // the sync point is properly aligned
38420          frameI = frameSyncPoint + 5;
38421          break;
38422        }
38423      }
38424  
38425      while (frameI < frameBuffer.byteLength) {
38426        // look at the current byte to determine if we've hit the end of
38427        // a NAL unit boundary
38428        switch (frameBuffer[frameI]) {
38429          case 0:
38430            // skip past non-sync sequences
38431            if (frameBuffer[frameI - 1] !== 0) {
38432              frameI += 2;
38433              break;
38434            } else if (frameBuffer[frameI - 2] !== 0) {
38435              frameI++;
38436              break;
38437            }
38438  
38439            if (frameSyncPoint + 3 !== frameI - 2) {
38440              nalType = parseNalUnitType(frameBuffer[frameSyncPoint + 3] & 0x1f);
38441  
38442              if (nalType === 'slice_layer_without_partitioning_rbsp_idr') {
38443                foundKeyFrame = true;
38444              }
38445            } // drop trailing zeroes
38446  
38447  
38448            do {
38449              frameI++;
38450            } while (frameBuffer[frameI] !== 1 && frameI < frameBuffer.length);
38451  
38452            frameSyncPoint = frameI - 2;
38453            frameI += 3;
38454            break;
38455  
38456          case 1:
38457            // skip past non-sync sequences
38458            if (frameBuffer[frameI - 1] !== 0 || frameBuffer[frameI - 2] !== 0) {
38459              frameI += 3;
38460              break;
38461            }
38462  
38463            nalType = parseNalUnitType(frameBuffer[frameSyncPoint + 3] & 0x1f);
38464  
38465            if (nalType === 'slice_layer_without_partitioning_rbsp_idr') {
38466              foundKeyFrame = true;
38467            }
38468  
38469            frameSyncPoint = frameI - 2;
38470            frameI += 3;
38471            break;
38472  
38473          default:
38474            // the current byte isn't a one or zero, so it cannot be part
38475            // of a sync sequence
38476            frameI += 3;
38477            break;
38478        }
38479      }
38480  
38481      frameBuffer = frameBuffer.subarray(frameSyncPoint);
38482      frameI -= frameSyncPoint;
38483      frameSyncPoint = 0; // parse the final nal
38484  
38485      if (frameBuffer && frameBuffer.byteLength > 3) {
38486        nalType = parseNalUnitType(frameBuffer[frameSyncPoint + 3] & 0x1f);
38487  
38488        if (nalType === 'slice_layer_without_partitioning_rbsp_idr') {
38489          foundKeyFrame = true;
38490        }
38491      }
38492  
38493      return foundKeyFrame;
38494    };
38495  
38496    var probe$1 = {
38497      parseType: parseType$1,
38498      parsePat: parsePat,
38499      parsePmt: parsePmt,
38500      parsePayloadUnitStartIndicator: parsePayloadUnitStartIndicator,
38501      parsePesType: parsePesType,
38502      parsePesTime: parsePesTime,
38503      videoPacketContainsKeyFrame: videoPacketContainsKeyFrame
38504    };
38505  
38506    /**
38507     * mux.js
38508     *
38509     * Copyright (c) Brightcove
38510     * Licensed Apache-2.0 https://github.com/videojs/mux.js/blob/master/LICENSE
38511     *
38512     * Utilities to detect basic properties and metadata about Aac data.
38513     */
38514  
38515    var ADTS_SAMPLING_FREQUENCIES = [96000, 88200, 64000, 48000, 44100, 32000, 24000, 22050, 16000, 12000, 11025, 8000, 7350];
38516  
38517    var isLikelyAacData = function isLikelyAacData(data) {
38518      if (data[0] === 'I'.charCodeAt(0) && data[1] === 'D'.charCodeAt(0) && data[2] === '3'.charCodeAt(0)) {
38519        return true;
38520      }
38521  
38522      return false;
vendor: 5,804 bytes, lines 38523-38700
38523    };
38524  
38525    var parseSyncSafeInteger = function parseSyncSafeInteger(data) {
38526      return data[0] << 21 | data[1] << 14 | data[2] << 7 | data[3];
38527    }; // return a percent-encoded representation of the specified byte range
38528    // @see http://en.wikipedia.org/wiki/Percent-encoding
38529  
38530  
38531    var percentEncode = function percentEncode(bytes, start, end) {
38532      var i,
38533          result = '';
38534  
38535      for (i = start; i < end; i++) {
38536        result += '%' + ('00' + bytes[i].toString(16)).slice(-2);
38537      }
38538  
38539      return result;
38540    }; // return the string representation of the specified byte range,
38541    // interpreted as ISO-8859-1.
38542  
38543  
38544    var parseIso88591 = function parseIso88591(bytes, start, end) {
38545      return unescape(percentEncode(bytes, start, end)); // jshint ignore:line
38546    };
38547  
38548    var parseId3TagSize = function parseId3TagSize(header, byteIndex) {
38549      var returnSize = header[byteIndex + 6] << 21 | header[byteIndex + 7] << 14 | header[byteIndex + 8] << 7 | header[byteIndex + 9],
38550          flags = header[byteIndex + 5],
38551          footerPresent = (flags & 16) >> 4;
38552  
38553      if (footerPresent) {
38554        return returnSize + 20;
38555      }
38556  
38557      return returnSize + 10;
38558    };
38559  
38560    var parseAdtsSize = function parseAdtsSize(header, byteIndex) {
38561      var lowThree = (header[byteIndex + 5] & 0xE0) >> 5,
38562          middle = header[byteIndex + 4] << 3,
38563          highTwo = header[byteIndex + 3] & 0x3 << 11;
38564      return highTwo | middle | lowThree;
38565    };
38566  
38567    var parseType$2 = function parseType(header, byteIndex) {
38568      if (header[byteIndex] === 'I'.charCodeAt(0) && header[byteIndex + 1] === 'D'.charCodeAt(0) && header[byteIndex + 2] === '3'.charCodeAt(0)) {
38569        return 'timed-metadata';
38570      } else if (header[byteIndex] & 0xff === 0xff && (header[byteIndex + 1] & 0xf0) === 0xf0) {
38571        return 'audio';
38572      }
38573  
38574      return null;
38575    };
38576  
38577    var parseSampleRate = function parseSampleRate(packet) {
38578      var i = 0;
38579  
38580      while (i + 5 < packet.length) {
38581        if (packet[i] !== 0xFF || (packet[i + 1] & 0xF6) !== 0xF0) {
38582          // If a valid header was not found,  jump one forward and attempt to
38583          // find a valid ADTS header starting at the next byte
38584          i++;
38585          continue;
38586        }
38587  
38588        return ADTS_SAMPLING_FREQUENCIES[(packet[i + 2] & 0x3c) >>> 2];
38589      }
38590  
38591      return null;
38592    };
38593  
38594    var parseAacTimestamp = function parseAacTimestamp(packet) {
38595      var frameStart, frameSize, frame, frameHeader; // find the start of the first frame and the end of the tag
38596  
38597      frameStart = 10;
38598  
38599      if (packet[5] & 0x40) {
38600        // advance the frame start past the extended header
38601        frameStart += 4; // header size field
38602  
38603        frameStart += parseSyncSafeInteger(packet.subarray(10, 14));
38604      } // parse one or more ID3 frames
38605      // http://id3.org/id3v2.3.0#ID3v2_frame_overview
38606  
38607  
38608      do {
38609        // determine the number of bytes in this frame
38610        frameSize = parseSyncSafeInteger(packet.subarray(frameStart + 4, frameStart + 8));
38611  
38612        if (frameSize < 1) {
38613          return null;
38614        }
38615  
38616        frameHeader = String.fromCharCode(packet[frameStart], packet[frameStart + 1], packet[frameStart + 2], packet[frameStart + 3]);
38617  
38618        if (frameHeader === 'PRIV') {
38619          frame = packet.subarray(frameStart + 10, frameStart + frameSize + 10);
38620  
38621          for (var i = 0; i < frame.byteLength; i++) {
38622            if (frame[i] === 0) {
38623              var owner = parseIso88591(frame, 0, i);
38624  
38625              if (owner === 'com.apple.streaming.transportStreamTimestamp') {
38626                var d = frame.subarray(i + 1);
38627                var size = (d[3] & 0x01) << 30 | d[4] << 22 | d[5] << 14 | d[6] << 6 | d[7] >>> 2;
38628                size *= 4;
38629                size += d[7] & 0x03;
38630                return size;
38631              }
38632  
38633              break;
38634            }
38635          }
38636        }
38637  
38638        frameStart += 10; // advance past the frame header
38639  
38640        frameStart += frameSize; // advance past the frame body
38641      } while (frameStart < packet.byteLength);
38642  
38643      return null;
38644    };
38645  
38646    var utils = {
38647      isLikelyAacData: isLikelyAacData,
38648      parseId3TagSize: parseId3TagSize,
38649      parseAdtsSize: parseAdtsSize,
38650      parseType: parseType$2,
38651      parseSampleRate: parseSampleRate,
38652      parseAacTimestamp: parseAacTimestamp
38653    };
38654  
38655    /**
38656     * mux.js
38657     *
38658     * Copyright (c) Brightcove
38659     * Licensed Apache-2.0 https://github.com/videojs/mux.js/blob/master/LICENSE
38660     */
38661    var ONE_SECOND_IN_TS = 90000,
38662        // 90kHz clock
38663    secondsToVideoTs,
38664        secondsToAudioTs,
38665        videoTsToSeconds,
38666        audioTsToSeconds,
38667        audioTsToVideoTs,
38668        videoTsToAudioTs,
38669        metadataTsToSeconds;
38670  
38671    secondsToVideoTs = function secondsToVideoTs(seconds) {
38672      return seconds * ONE_SECOND_IN_TS;
38673    };
38674  
38675    secondsToAudioTs = function secondsToAudioTs(seconds, sampleRate) {
38676      return seconds * sampleRate;
38677    };
38678  
38679    videoTsToSeconds = function videoTsToSeconds(timestamp) {
38680      return timestamp / ONE_SECOND_IN_TS;
38681    };
38682  
38683    audioTsToSeconds = function audioTsToSeconds(timestamp, sampleRate) {
38684      return timestamp / sampleRate;
38685    };
38686  
38687    audioTsToVideoTs = function audioTsToVideoTs(timestamp, sampleRate) {
38688      return secondsToVideoTs(audioTsToSeconds(timestamp, sampleRate));
38689    };
38690  
38691    videoTsToAudioTs = function videoTsToAudioTs(timestamp, sampleRate) {
38692      return secondsToAudioTs(videoTsToSeconds(timestamp), sampleRate);
38693    };
38694    /**
38695     * Adjust ID3 tag or caption timing information by the timeline pts values
38696     * (if keepOriginalTimestamps is false) and convert to seconds
38697     */
38698  
38699  
38700    metadataTsToSeconds = function metadataTsToSeconds(timestamp, timelineStartPts, 
vendor: 14,915 bytes, lines 38700-39157
38700keepOriginalTimestamps) {
38701      return videoTsToSeconds(keepOriginalTimestamps ? timestamp : timestamp - timelineStartPts);
38702    };
38703  
38704    var clock = {
38705      ONE_SECOND_IN_TS: ONE_SECOND_IN_TS,
38706      secondsToVideoTs: secondsToVideoTs,
38707      secondsToAudioTs: secondsToAudioTs,
38708      videoTsToSeconds: videoTsToSeconds,
38709      audioTsToSeconds: audioTsToSeconds,
38710      audioTsToVideoTs: audioTsToVideoTs,
38711      videoTsToAudioTs: videoTsToAudioTs,
38712      metadataTsToSeconds: metadataTsToSeconds
38713    };
38714  
38715    var handleRollover$1 = timestampRolloverStream.handleRollover;
38716    var probe$2 = {};
38717    probe$2.ts = probe$1;
38718    probe$2.aac = utils;
38719    var ONE_SECOND_IN_TS$1 = clock.ONE_SECOND_IN_TS;
38720    var MP2T_PACKET_LENGTH = 188,
38721        // bytes
38722    SYNC_BYTE = 0x47;
38723    /**
38724     * walks through segment data looking for pat and pmt packets to parse out
38725     * program map table information
38726     */
38727  
38728    var parsePsi_ = function parsePsi_(bytes, pmt) {
38729      var startIndex = 0,
38730          endIndex = MP2T_PACKET_LENGTH,
38731          packet,
38732          type;
38733  
38734      while (endIndex < bytes.byteLength) {
38735        // Look for a pair of start and end sync bytes in the data..
38736        if (bytes[startIndex] === SYNC_BYTE && bytes[endIndex] === SYNC_BYTE) {
38737          // We found a packet
38738          packet = bytes.subarray(startIndex, endIndex);
38739          type = probe$2.ts.parseType(packet, pmt.pid);
38740  
38741          switch (type) {
38742            case 'pat':
38743              if (!pmt.pid) {
38744                pmt.pid = probe$2.ts.parsePat(packet);
38745              }
38746  
38747              break;
38748  
38749            case 'pmt':
38750              if (!pmt.table) {
38751                pmt.table = probe$2.ts.parsePmt(packet);
38752              }
38753  
38754              break;
38755          } // Found the pat and pmt, we can stop walking the segment
38756  
38757  
38758          if (pmt.pid && pmt.table) {
38759            return;
38760          }
38761  
38762          startIndex += MP2T_PACKET_LENGTH;
38763          endIndex += MP2T_PACKET_LENGTH;
38764          continue;
38765        } // If we get here, we have somehow become de-synchronized and we need to step
38766        // forward one byte at a time until we find a pair of sync bytes that denote
38767        // a packet
38768  
38769  
38770        startIndex++;
38771        endIndex++;
38772      }
38773    };
38774    /**
38775     * walks through the segment data from the start and end to get timing information
38776     * for the first and last audio pes packets
38777     */
38778  
38779  
38780    var parseAudioPes_ = function parseAudioPes_(bytes, pmt, result) {
38781      var startIndex = 0,
38782          endIndex = MP2T_PACKET_LENGTH,
38783          packet,
38784          type,
38785          pesType,
38786          pusi,
38787          parsed;
38788      var endLoop = false; // Start walking from start of segment to get first audio packet
38789  
38790      while (endIndex <= bytes.byteLength) {
38791        // Look for a pair of start and end sync bytes in the data..
38792        if (bytes[startIndex] === SYNC_BYTE && (bytes[endIndex] === SYNC_BYTE || endIndex === bytes.byteLength)) {
38793          // We found a packet
38794          packet = bytes.subarray(startIndex, endIndex);
38795          type = probe$2.ts.parseType(packet, pmt.pid);
38796  
38797          switch (type) {
38798            case 'pes':
38799              pesType = probe$2.ts.parsePesType(packet, pmt.table);
38800              pusi = probe$2.ts.parsePayloadUnitStartIndicator(packet);
38801  
38802              if (pesType === 'audio' && pusi) {
38803                parsed = probe$2.ts.parsePesTime(packet);
38804  
38805                if (parsed) {
38806                  parsed.type = 'audio';
38807                  result.audio.push(parsed);
38808                  endLoop = true;
38809                }
38810              }
38811  
38812              break;
38813          }
38814  
38815          if (endLoop) {
38816            break;
38817          }
38818  
38819          startIndex += MP2T_PACKET_LENGTH;
38820          endIndex += MP2T_PACKET_LENGTH;
38821          continue;
38822        } // If we get here, we have somehow become de-synchronized and we need to step
38823        // forward one byte at a time until we find a pair of sync bytes that denote
38824        // a packet
38825  
38826  
38827        startIndex++;
38828        endIndex++;
38829      } // Start walking from end of segment to get last audio packet
38830  
38831  
38832      endIndex = bytes.byteLength;
38833      startIndex = endIndex - MP2T_PACKET_LENGTH;
38834      endLoop = false;
38835  
38836      while (startIndex >= 0) {
38837        // Look for a pair of start and end sync bytes in the data..
38838        if (bytes[startIndex] === SYNC_BYTE && (bytes[endIndex] === SYNC_BYTE || endIndex === bytes.byteLength)) {
38839          // We found a packet
38840          packet = bytes.subarray(startIndex, endIndex);
38841          type = probe$2.ts.parseType(packet, pmt.pid);
38842  
38843          switch (type) {
38844            case 'pes':
38845              pesType = probe$2.ts.parsePesType(packet, pmt.table);
38846              pusi = probe$2.ts.parsePayloadUnitStartIndicator(packet);
38847  
38848              if (pesType === 'audio' && pusi) {
38849                parsed = probe$2.ts.parsePesTime(packet);
38850  
38851                if (parsed) {
38852                  parsed.type = 'audio';
38853                  result.audio.push(parsed);
38854                  endLoop = true;
38855                }
38856              }
38857  
38858              break;
38859          }
38860  
38861          if (endLoop) {
38862            break;
38863          }
38864  
38865          startIndex -= MP2T_PACKET_LENGTH;
38866          endIndex -= MP2T_PACKET_LENGTH;
38867          continue;
38868        } // If we get here, we have somehow become de-synchronized and we need to step
38869        // forward one byte at a time until we find a pair of sync bytes that denote
38870        // a packet
38871  
38872  
38873        startIndex--;
38874        endIndex--;
38875      }
38876    };
38877    /**
38878     * walks through the segment data from the start and end to get timing information
38879     * for the first and last video pes packets as well as timing information for the first
38880     * key frame.
38881     */
38882  
38883  
38884    var parseVideoPes_ = function parseVideoPes_(bytes, pmt, result) {
38885      var startIndex = 0,
38886          endIndex = MP2T_PACKET_LENGTH,
38887          packet,
38888          type,
38889          pesType,
38890          pusi,
38891          parsed,
38892          frame,
38893          i,
38894          pes;
38895      var endLoop = false;
38896      var currentFrame = {
38897        data: [],
38898        size: 0
38899      }; // Start walking from start of segment to get first video packet
38900  
38901      while (endIndex < bytes.byteLength) {
38902        // Look for a pair of start and end sync bytes in the data..
38903        if (bytes[startIndex] === SYNC_BYTE && bytes[endIndex] === SYNC_BYTE) {
38904          // We found a packet
38905          packet = bytes.subarray(startIndex, endIndex);
38906          type = probe$2.ts.parseType(packet, pmt.pid);
38907  
38908          switch (type) {
38909            case 'pes':
38910              pesType = probe$2.ts.parsePesType(packet, pmt.table);
38911              pusi = probe$2.ts.parsePayloadUnitStartIndicator(packet);
38912  
38913              if (pesType === 'video') {
38914                if (pusi && !endLoop) {
38915                  parsed = probe$2.ts.parsePesTime(packet);
38916  
38917                  if (parsed) {
38918                    parsed.type = 'video';
38919                    result.video.push(parsed);
38920                    endLoop = true;
38921                  }
38922                }
38923  
38924                if (!result.firstKeyFrame) {
38925                  if (pusi) {
38926                    if (currentFrame.size !== 0) {
38927                      frame = new Uint8Array(currentFrame.size);
38928                      i = 0;
38929  
38930                      while (currentFrame.data.length) {
38931                        pes = currentFrame.data.shift();
38932                        frame.set(pes, i);
38933                        i += pes.byteLength;
38934                      }
38935  
38936                      if (probe$2.ts.videoPacketContainsKeyFrame(frame)) {
38937                        var firstKeyFrame = probe$2.ts.parsePesTime(frame); // PTS/DTS may not be available. Simply *not* setting
38938                        // the keyframe seems to work fine with HLS playback
38939                        // and definitely preferable to a crash with TypeError...
38940  
38941                        if (firstKeyFrame) {
38942                          result.firstKeyFrame = firstKeyFrame;
38943                          result.firstKeyFrame.type = 'video';
38944                        } else {
38945                          // eslint-disable-next-line
38946                          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.');
38947                        }
38948                      }
38949  
38950                      currentFrame.size = 0;
38951                    }
38952                  }
38953  
38954                  currentFrame.data.push(packet);
38955                  currentFrame.size += packet.byteLength;
38956                }
38957              }
38958  
38959              break;
38960          }
38961  
38962          if (endLoop && result.firstKeyFrame) {
38963            break;
38964          }
38965  
38966          startIndex += MP2T_PACKET_LENGTH;
38967          endIndex += MP2T_PACKET_LENGTH;
38968          continue;
38969        } // If we get here, we have somehow become de-synchronized and we need to step
38970        // forward one byte at a time until we find a pair of sync bytes that denote
38971        // a packet
38972  
38973  
38974        startIndex++;
38975        endIndex++;
38976      } // Start walking from end of segment to get last video packet
38977  
38978  
38979      endIndex = bytes.byteLength;
38980      startIndex = endIndex - MP2T_PACKET_LENGTH;
38981      endLoop = false;
38982  
38983      while (startIndex >= 0) {
38984        // Look for a pair of start and end sync bytes in the data..
38985        if (bytes[startIndex] === SYNC_BYTE && bytes[endIndex] === SYNC_BYTE) {
38986          // We found a packet
38987          packet = bytes.subarray(startIndex, endIndex);
38988          type = probe$2.ts.parseType(packet, pmt.pid);
38989  
38990          switch (type) {
38991            case 'pes':
38992              pesType = probe$2.ts.parsePesType(packet, pmt.table);
38993              pusi = probe$2.ts.parsePayloadUnitStartIndicator(packet);
38994  
38995              if (pesType === 'video' && pusi) {
38996                parsed = probe$2.ts.parsePesTime(packet);
38997  
38998                if (parsed) {
38999                  parsed.type = 'video';
39000                  result.video.push(parsed);
39001                  endLoop = true;
39002                }
39003              }
39004  
39005              break;
39006          }
39007  
39008          if (endLoop) {
39009            break;
39010          }
39011  
39012          startIndex -= MP2T_PACKET_LENGTH;
39013          endIndex -= MP2T_PACKET_LENGTH;
39014          continue;
39015        } // If we get here, we have somehow become de-synchronized and we need to step
39016        // forward one byte at a time until we find a pair of sync bytes that denote
39017        // a packet
39018  
39019  
39020        startIndex--;
39021        endIndex--;
39022      }
39023    };
39024    /**
39025     * Adjusts the timestamp information for the segment to account for
39026     * rollover and convert to seconds based on pes packet timescale (90khz clock)
39027     */
39028  
39029  
39030    var adjustTimestamp_ = function adjustTimestamp_(segmentInfo, baseTimestamp) {
39031      if (segmentInfo.audio && segmentInfo.audio.length) {
39032        var audioBaseTimestamp = baseTimestamp;
39033  
39034        if (typeof audioBaseTimestamp === 'undefined') {
39035          audioBaseTimestamp = segmentInfo.audio[0].dts;
39036        }
39037  
39038        segmentInfo.audio.forEach(function (info) {
39039          info.dts = handleRollover$1(info.dts, audioBaseTimestamp);
39040          info.pts = handleRollover$1(info.pts, audioBaseTimestamp); // time in seconds
39041  
39042          info.dtsTime = info.dts / ONE_SECOND_IN_TS$1;
39043          info.ptsTime = info.pts / ONE_SECOND_IN_TS$1;
39044        });
39045      }
39046  
39047      if (segmentInfo.video && segmentInfo.video.length) {
39048        var videoBaseTimestamp = baseTimestamp;
39049  
39050        if (typeof videoBaseTimestamp === 'undefined') {
39051          videoBaseTimestamp = segmentInfo.video[0].dts;
39052        }
39053  
39054        segmentInfo.video.forEach(function (info) {
39055          info.dts = handleRollover$1(info.dts, videoBaseTimestamp);
39056          info.pts = handleRollover$1(info.pts, videoBaseTimestamp); // time in seconds
39057  
39058          info.dtsTime = info.dts / ONE_SECOND_IN_TS$1;
39059          info.ptsTime = info.pts / ONE_SECOND_IN_TS$1;
39060        });
39061  
39062        if (segmentInfo.firstKeyFrame) {
39063          var frame = segmentInfo.firstKeyFrame;
39064          frame.dts = handleRollover$1(frame.dts, videoBaseTimestamp);
39065          frame.pts = handleRollover$1(frame.pts, videoBaseTimestamp); // time in seconds
39066  
39067          frame.dtsTime = frame.dts / ONE_SECOND_IN_TS$1;
39068          frame.ptsTime = frame.dts / ONE_SECOND_IN_TS$1;
39069        }
39070      }
39071    };
39072    /**
39073     * inspects the aac data stream for start and end time information
39074     */
39075  
39076  
39077    var inspectAac_ = function inspectAac_(bytes) {
39078      var endLoop = false,
39079          audioCount = 0,
39080          sampleRate = null,
39081          timestamp = null,
39082          frameSize = 0,
39083          byteIndex = 0,
39084          packet;
39085  
39086      while (bytes.length - byteIndex >= 3) {
39087        var type = probe$2.aac.parseType(bytes, byteIndex);
39088  
39089        switch (type) {
39090          case 'timed-metadata':
39091            // Exit early because we don't have enough to parse
39092            // the ID3 tag header
39093            if (bytes.length - byteIndex < 10) {
39094              endLoop = true;
39095              break;
39096            }
39097  
39098            frameSize = probe$2.aac.parseId3TagSize(bytes, byteIndex); // Exit early if we don't have enough in the buffer
39099            // to emit a full packet
39100  
39101            if (frameSize > bytes.length) {
39102              endLoop = true;
39103              break;
39104            }
39105  
39106            if (timestamp === null) {
39107              packet = bytes.subarray(byteIndex, byteIndex + frameSize);
39108              timestamp = probe$2.aac.parseAacTimestamp(packet);
39109            }
39110  
39111            byteIndex += frameSize;
39112            break;
39113  
39114          case 'audio':
39115            // Exit early because we don't have enough to parse
39116            // the ADTS frame header
39117            if (bytes.length - byteIndex < 7) {
39118              endLoop = true;
39119              break;
39120            }
39121  
39122            frameSize = probe$2.aac.parseAdtsSize(bytes, byteIndex); // Exit early if we don't have enough in the buffer
39123            // to emit a full packet
39124  
39125            if (frameSize > bytes.length) {
39126              endLoop = true;
39127              break;
39128            }
39129  
39130            if (sampleRate === null) {
39131              packet = bytes.subarray(byteIndex, byteIndex + frameSize);
39132              sampleRate = probe$2.aac.parseSampleRate(packet);
39133            }
39134  
39135            audioCount++;
39136            byteIndex += frameSize;
39137            break;
39138  
39139          default:
39140            byteIndex++;
39141            break;
39142        }
39143  
39144        if (endLoop) {
39145          return null;
39146        }
39147      }
39148  
39149      if (sampleRate === null || timestamp === null) {
39150        return null;
39151      }
39152  
39153      var audioTimescale = ONE_SECOND_IN_TS$1 / sampleRate;
39154      var result = {
39155        audio: [{
39156          type: 'audio',
39157          dts: timestamp,
vendor: 77,433 bytes, lines 39158-41467
39158          pts: timestamp
39159        }, {
39160          type: 'audio',
39161          dts: timestamp + audioCount * 1024 * audioTimescale,
39162          pts: timestamp + audioCount * 1024 * audioTimescale
39163        }]
39164      };
39165      return result;
39166    };
39167    /**
39168     * inspects the transport stream segment data for start and end time information
39169     * of the audio and video tracks (when present) as well as the first key frame's
39170     * start time.
39171     */
39172  
39173  
39174    var inspectTs_ = function inspectTs_(bytes) {
39175      var pmt = {
39176        pid: null,
39177        table: null
39178      };
39179      var result = {};
39180      parsePsi_(bytes, pmt);
39181  
39182      for (var pid in pmt.table) {
39183        if (pmt.table.hasOwnProperty(pid)) {
39184          var type = pmt.table[pid];
39185  
39186          switch (type) {
39187            case streamTypes.H264_STREAM_TYPE:
39188              result.video = [];
39189              parseVideoPes_(bytes, pmt, result);
39190  
39191              if (result.video.length === 0) {
39192                delete result.video;
39193              }
39194  
39195              break;
39196  
39197            case streamTypes.ADTS_STREAM_TYPE:
39198              result.audio = [];
39199              parseAudioPes_(bytes, pmt, result);
39200  
39201              if (result.audio.length === 0) {
39202                delete result.audio;
39203              }
39204  
39205              break;
39206          }
39207        }
39208      }
39209  
39210      return result;
39211    };
39212    /**
39213     * Inspects segment byte data and returns an object with start and end timing information
39214     *
39215     * @param {Uint8Array} bytes The segment byte data
39216     * @param {Number} baseTimestamp Relative reference timestamp used when adjusting frame
39217     *  timestamps for rollover. This value must be in 90khz clock.
39218     * @return {Object} Object containing start and end frame timing info of segment.
39219     */
39220  
39221  
39222    var inspect = function inspect(bytes, baseTimestamp) {
39223      var isAacData = probe$2.aac.isLikelyAacData(bytes);
39224      var result;
39225  
39226      if (isAacData) {
39227        result = inspectAac_(bytes);
39228      } else {
39229        result = inspectTs_(bytes);
39230      }
39231  
39232      if (!result || !result.audio && !result.video) {
39233        return null;
39234      }
39235  
39236      adjustTimestamp_(result, baseTimestamp);
39237      return result;
39238    };
39239  
39240    var tsInspector = {
39241      inspect: inspect,
39242      parseAudioPes_: parseAudioPes_
39243    };
39244  
39245    /*
39246     * pkcs7.pad
39247     * https://github.com/brightcove/pkcs7
39248     *
39249     * Copyright (c) 2014 Brightcove
39250     * Licensed under the apache2 license.
39251     */
39252    /**
39253     * Returns the subarray of a Uint8Array without PKCS#7 padding.
39254     * @param padded {Uint8Array} unencrypted bytes that have been padded
39255     * @return {Uint8Array} the unpadded bytes
39256     * @see http://tools.ietf.org/html/rfc5652
39257     */
39258  
39259    function unpad(padded) {
39260      return padded.subarray(0, padded.byteLength - padded[padded.byteLength - 1]);
39261    }
39262  
39263    var classCallCheck = function classCallCheck(instance, Constructor) {
39264      if (!(instance instanceof Constructor)) {
39265        throw new TypeError("Cannot call a class as a function");
39266      }
39267    };
39268  
39269    var createClass = function () {
39270      function defineProperties(target, props) {
39271        for (var i = 0; i < props.length; i++) {
39272          var descriptor = props[i];
39273          descriptor.enumerable = descriptor.enumerable || false;
39274          descriptor.configurable = true;
39275          if ("value" in descriptor) descriptor.writable = true;
39276          Object.defineProperty(target, descriptor.key, descriptor);
39277        }
39278      }
39279  
39280      return function (Constructor, protoProps, staticProps) {
39281        if (protoProps) defineProperties(Constructor.prototype, protoProps);
39282        if (staticProps) defineProperties(Constructor, staticProps);
39283        return Constructor;
39284      };
39285    }();
39286  
39287    var inherits$1 = function inherits(subClass, superClass) {
39288      if (typeof superClass !== "function" && superClass !== null) {
39289        throw new TypeError("Super expression must either be null or a function, not " + typeof superClass);
39290      }
39291  
39292      subClass.prototype = Object.create(superClass && superClass.prototype, {
39293        constructor: {
39294          value: subClass,
39295          enumerable: false,
39296          writable: true,
39297          configurable: true
39298        }
39299      });
39300      if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass;
39301    };
39302  
39303    var possibleConstructorReturn = function possibleConstructorReturn(self, call) {
39304      if (!self) {
39305        throw new ReferenceError("this hasn't been initialised - super() hasn't been called");
39306      }
39307  
39308      return call && (typeof call === "object" || typeof call === "function") ? call : self;
39309    };
39310    /**
39311     * @file aes.js
39312     *
39313     * This file contains an adaptation of the AES decryption algorithm
39314     * from the Standford Javascript Cryptography Library. That work is
39315     * covered by the following copyright and permissions notice:
39316     *
39317     * Copyright 2009-2010 Emily Stark, Mike Hamburg, Dan Boneh.
39318     * All rights reserved.
39319     *
39320     * Redistribution and use in source and binary forms, with or without
39321     * modification, are permitted provided that the following conditions are
39322     * met:
39323     *
39324     * 1. Redistributions of source code must retain the above copyright
39325     *    notice, this list of conditions and the following disclaimer.
39326     *
39327     * 2. Redistributions in binary form must reproduce the above
39328     *    copyright notice, this list of conditions and the following
39329     *    disclaimer in the documentation and/or other materials provided
39330     *    with the distribution.
39331     *
39332     * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR
39333     * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
39334     * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
39335     * DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> OR CONTRIBUTORS BE
39336     * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
39337     * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
39338     * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
39339     * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
39340     * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
39341     * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
39342     * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
39343     *
39344     * The views and conclusions contained in the software and documentation
39345     * are those of the authors and should not be interpreted as representing
39346     * official policies, either expressed or implied, of the authors.
39347     */
39348  
39349    /**
39350     * Expand the S-box tables.
39351     *
39352     * @private
39353     */
39354  
39355  
39356    var precompute = function precompute() {
39357      var tables = [[[], [], [], [], []], [[], [], [], [], []]];
39358      var encTable = tables[0];
39359      var decTable = tables[1];
39360      var sbox = encTable[4];
39361      var sboxInv = decTable[4];
39362      var i = void 0;
39363      var x = void 0;
39364      var xInv = void 0;
39365      var d = [];
39366      var th = [];
39367      var x2 = void 0;
39368      var x4 = void 0;
39369      var x8 = void 0;
39370      var s = void 0;
39371      var tEnc = void 0;
39372      var tDec = void 0; // Compute double and third tables
39373  
39374      for (i = 0; i < 256; i++) {
39375        th[(d[i] = i << 1 ^ (i >> 7) * 283) ^ i] = i;
39376      }
39377  
39378      for (x = xInv = 0; !sbox[x]; x ^= x2 || 1, xInv = th[xInv] || 1) {
39379        // Compute sbox
39380        s = xInv ^ xInv << 1 ^ xInv << 2 ^ xInv << 3 ^ xInv << 4;
39381        s = s >> 8 ^ s & 255 ^ 99;
39382        sbox[x] = s;
39383        sboxInv[s] = x; // Compute MixColumns
39384  
39385        x8 = d[x4 = d[x2 = d[x]]];
39386        tDec = x8 * 0x1010101 ^ x4 * 0x10001 ^ x2 * 0x101 ^ x * 0x1010100;
39387        tEnc = d[s] * 0x101 ^ s * 0x1010100;
39388  
39389        for (i = 0; i < 4; i++) {
39390          encTable[i][x] = tEnc = tEnc << 24 ^ tEnc >>> 8;
39391          decTable[i][s] = tDec = tDec << 24 ^ tDec >>> 8;
39392        }
39393      } // Compactify. Considerable speedup on Firefox.
39394  
39395  
39396      for (i = 0; i < 5; i++) {
39397        encTable[i] = encTable[i].slice(0);
39398        decTable[i] = decTable[i].slice(0);
39399      }
39400  
39401      return tables;
39402    };
39403  
39404    var aesTables = null;
39405    /**
39406     * Schedule out an AES key for both encryption and decryption. This
39407     * is a low-level class. Use a cipher mode to do bulk encryption.
39408     *
39409     * @class AES
39410     * @param key {Array} The key as an array of 4, 6 or 8 words.
39411     */
39412  
39413    var AES = function () {
39414      function AES(key) {
39415        classCallCheck(this, AES);
39416        /**
39417         * The expanded S-box and inverse S-box tables. These will be computed
39418         * on the client so that we don't have to send them down the wire.
39419         *
39420         * There are two tables, _tables[0] is for encryption and
39421         * _tables[1] is for decryption.
39422         *
39423         * The first 4 sub-tables are the expanded S-box with MixColumns. The
39424         * last (_tables[01][4]) is the S-box itself.
39425         *
39426         * @private
39427         */
39428        // if we have yet to precompute the S-box tables
39429        // do so now
39430  
39431        if (!aesTables) {
39432          aesTables = precompute();
39433        } // then make a copy of that object for use
39434  
39435  
39436        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()]];
39437        var i = void 0;
39438        var j = void 0;
39439        var tmp = void 0;
39440        var encKey = void 0;
39441        var decKey = void 0;
39442        var sbox = this._tables[0][4];
39443        var decTable = this._tables[1];
39444        var keyLen = key.length;
39445        var rcon = 1;
39446  
39447        if (keyLen !== 4 && keyLen !== 6 && keyLen !== 8) {
39448          throw new Error('Invalid aes key size');
39449        }
39450  
39451        encKey = key.slice(0);
39452        decKey = [];
39453        this._key = [encKey, decKey]; // schedule encryption keys
39454  
39455        for (i = keyLen; i < 4 * keyLen + 28; i++) {
39456          tmp = encKey[i - 1]; // apply sbox
39457  
39458          if (i % keyLen === 0 || keyLen === 8 && i % keyLen === 4) {
39459            tmp = sbox[tmp >>> 24] << 24 ^ sbox[tmp >> 16 & 255] << 16 ^ sbox[tmp >> 8 & 255] << 8 ^ sbox[tmp & 255]; // shift rows and add rcon
39460  
39461            if (i % keyLen === 0) {
39462              tmp = tmp << 8 ^ tmp >>> 24 ^ rcon << 24;
39463              rcon = rcon << 1 ^ (rcon >> 7) * 283;
39464            }
39465          }
39466  
39467          encKey[i] = encKey[i - keyLen] ^ tmp;
39468        } // schedule decryption keys
39469  
39470  
39471        for (j = 0; i; j++, i--) {
39472          tmp = encKey[j & 3 ? i : i - 4];
39473  
39474          if (i <= 4 || j < 4) {
39475            decKey[j] = tmp;
39476          } else {
39477            decKey[j] = decTable[0][sbox[tmp >>> 24]] ^ decTable[1][sbox[tmp >> 16 & 255]] ^ decTable[2][sbox[tmp >> 8 & 255]] ^ decTable[3][sbox[tmp & 255]];
39478          }
39479        }
39480      }
39481      /**
39482       * Decrypt 16 bytes, specified as four 32-bit words.
39483       *
39484       * @param {Number} encrypted0 the first word to decrypt
39485       * @param {Number} encrypted1 the second word to decrypt
39486       * @param {Number} encrypted2 the third word to decrypt
39487       * @param {Number} encrypted3 the fourth word to decrypt
39488       * @param {Int32Array} out the array to write the decrypted words
39489       * into
39490       * @param {Number} offset the offset into the output array to start
39491       * writing results
39492       * @return {Array} The plaintext.
39493       */
39494  
39495  
39496      AES.prototype.decrypt = function decrypt(encrypted0, encrypted1, encrypted2, encrypted3, out, offset) {
39497        var key = this._key[1]; // state variables a,b,c,d are loaded with pre-whitened data
39498  
39499        var a = encrypted0 ^ key[0];
39500        var b = encrypted3 ^ key[1];
39501        var c = encrypted2 ^ key[2];
39502        var d = encrypted1 ^ key[3];
39503        var a2 = void 0;
39504        var b2 = void 0;
39505        var c2 = void 0; // key.length === 2 ?
39506  
39507        var nInnerRounds = key.length / 4 - 2;
39508        var i = void 0;
39509        var kIndex = 4;
39510        var table = this._tables[1]; // load up the tables
39511  
39512        var table0 = table[0];
39513        var table1 = table[1];
39514        var table2 = table[2];
39515        var table3 = table[3];
39516        var sbox = table[4]; // Inner rounds. Cribbed from OpenSSL.
39517  
39518        for (i = 0; i < nInnerRounds; i++) {
39519          a2 = table0[a >>> 24] ^ table1[b >> 16 & 255] ^ table2[c >> 8 & 255] ^ table3[d & 255] ^ key[kIndex];
39520          b2 = table0[b >>> 24] ^ table1[c >> 16 & 255] ^ table2[d >> 8 & 255] ^ table3[a & 255] ^ key[kIndex + 1];
39521          c2 = table0[c >>> 24] ^ table1[d >> 16 & 255] ^ table2[a >> 8 & 255] ^ table3[b & 255] ^ key[kIndex + 2];
39522          d = table0[d >>> 24] ^ table1[a >> 16 & 255] ^ table2[b >> 8 & 255] ^ table3[c & 255] ^ key[kIndex + 3];
39523          kIndex += 4;
39524          a = a2;
39525          b = b2;
39526          c = c2;
39527        } // Last round.
39528  
39529  
39530        for (i = 0; i < 4; i++) {
39531          out[(3 & -i) + offset] = sbox[a >>> 24] << 24 ^ sbox[b >> 16 & 255] << 16 ^ sbox[c >> 8 & 255] << 8 ^ sbox[d & 255] ^ key[kIndex++];
39532          a2 = a;
39533          a = b;
39534          b = c;
39535          c = d;
39536          d = a2;
39537        }
39538      };
39539  
39540      return AES;
39541    }();
39542    /**
39543     * @file stream.js
39544     */
39545  
39546    /**
39547     * A lightweight readable stream implemention that handles event dispatching.
39548     *
39549     * @class Stream
39550     */
39551  
39552  
39553    var Stream$2 = function () {
39554      function Stream() {
39555        classCallCheck(this, Stream);
39556        this.listeners = {};
39557      }
39558      /**
39559       * Add a listener for a specified event type.
39560       *
39561       * @param {String} type the event name
39562       * @param {Function} listener the callback to be invoked when an event of
39563       * the specified type occurs
39564       */
39565  
39566  
39567      Stream.prototype.on = function on(type, listener) {
39568        if (!this.listeners[type]) {
39569          this.listeners[type] = [];
39570        }
39571  
39572        this.listeners[type].push(listener);
39573      };
39574      /**
39575       * Remove a listener for a specified event type.
39576       *
39577       * @param {String} type the event name
39578       * @param {Function} listener  a function previously registered for this
39579       * type of event through `on`
39580       * @return {Boolean} if we could turn it off or not
39581       */
39582  
39583  
39584      Stream.prototype.off = function off(type, listener) {
39585        if (!this.listeners[type]) {
39586          return false;
39587        }
39588  
39589        var index = this.listeners[type].indexOf(listener);
39590        this.listeners[type].splice(index, 1);
39591        return index > -1;
39592      };
39593      /**
39594       * Trigger an event of the specified type on this stream. Any additional
39595       * arguments to this function are passed as parameters to event listeners.
39596       *
39597       * @param {String} type the event name
39598       */
39599  
39600  
39601      Stream.prototype.trigger = function trigger(type) {
39602        var callbacks = this.listeners[type];
39603  
39604        if (!callbacks) {
39605          return;
39606        } // Slicing the arguments on every invocation of this method
39607        // can add a significant amount of overhead. Avoid the
39608        // intermediate object creation for the common case of a
39609        // single callback argument
39610  
39611  
39612        if (arguments.length === 2) {
39613          var length = callbacks.length;
39614  
39615          for (var i = 0; i < length; ++i) {
39616            callbacks[i].call(this, arguments[1]);
39617          }
39618        } else {
39619          var args = Array.prototype.slice.call(arguments, 1);
39620          var _length = callbacks.length;
39621  
39622          for (var _i = 0; _i < _length; ++_i) {
39623            callbacks[_i].apply(this, args);
39624          }
39625        }
39626      };
39627      /**
39628       * Destroys the stream and cleans up.
39629       */
39630  
39631  
39632      Stream.prototype.dispose = function dispose() {
39633        this.listeners = {};
39634      };
39635      /**
39636       * Forwards all `data` events on this stream to the destination stream. The
39637       * destination stream should provide a method `push` to receive the data
39638       * events as they arrive.
39639       *
39640       * @param {Stream} destination the stream that will receive all `data` events
39641       * @see http://nodejs.org/api/stream.html#stream_readable_pipe_destination_options
39642       */
39643  
39644  
39645      Stream.prototype.pipe = function pipe(destination) {
39646        this.on('data', function (data) {
39647          destination.push(data);
39648        });
39649      };
39650  
39651      return Stream;
39652    }();
39653    /**
39654     * @file async-stream.js
39655     */
39656  
39657    /**
39658     * A wrapper around the Stream class to use setTiemout
39659     * and run stream "jobs" Asynchronously
39660     *
39661     * @class AsyncStream
39662     * @extends Stream
39663     */
39664  
39665  
39666    var AsyncStream = function (_Stream) {
39667      inherits$1(AsyncStream, _Stream);
39668  
39669      function AsyncStream() {
39670        classCallCheck(this, AsyncStream);
39671  
39672        var _this = possibleConstructorReturn(this, _Stream.call(this, Stream$2));
39673  
39674        _this.jobs = [];
39675        _this.delay = 1;
39676        _this.timeout_ = null;
39677        return _this;
39678      }
39679      /**
39680       * process an async job
39681       *
39682       * @private
39683       */
39684  
39685  
39686      AsyncStream.prototype.processJob_ = function processJob_() {
39687        this.jobs.shift()();
39688  
39689        if (this.jobs.length) {
39690          this.timeout_ = setTimeout(this.processJob_.bind(this), this.delay);
39691        } else {
39692          this.timeout_ = null;
39693        }
39694      };
39695      /**
39696       * push a job into the stream
39697       *
39698       * @param {Function} job the job to push into the stream
39699       */
39700  
39701  
39702      AsyncStream.prototype.push = function push(job) {
39703        this.jobs.push(job);
39704  
39705        if (!this.timeout_) {
39706          this.timeout_ = setTimeout(this.processJob_.bind(this), this.delay);
39707        }
39708      };
39709  
39710      return AsyncStream;
39711    }(Stream$2);
39712    /**
39713     * @file decrypter.js
39714     *
39715     * An asynchronous implementation of AES-128 CBC decryption with
39716     * PKCS#7 padding.
39717     */
39718  
39719    /**
39720     * Convert network-order (big-endian) bytes into their little-endian
39721     * representation.
39722     */
39723  
39724  
39725    var ntoh = function ntoh(word) {
39726      return word << 24 | (word & 0xff00) << 8 | (word & 0xff0000) >> 8 | word >>> 24;
39727    };
39728    /**
39729     * Decrypt bytes using AES-128 with CBC and PKCS#7 padding.
39730     *
39731     * @param {Uint8Array} encrypted the encrypted bytes
39732     * @param {Uint32Array} key the bytes of the decryption key
39733     * @param {Uint32Array} initVector the initialization vector (IV) to
39734     * use for the first round of CBC.
39735     * @return {Uint8Array} the decrypted bytes
39736     *
39737     * @see http://en.wikipedia.org/wiki/Advanced_Encryption_Standard
39738     * @see http://en.wikipedia.org/wiki/Block_cipher_mode_of_operation#Cipher_Block_Chaining_.28CBC.29
39739     * @see https://tools.ietf.org/html/rfc2315
39740     */
39741  
39742  
39743    var decrypt = function decrypt(encrypted, key, initVector) {
39744      // word-level access to the encrypted bytes
39745      var encrypted32 = new Int32Array(encrypted.buffer, encrypted.byteOffset, encrypted.byteLength >> 2);
39746      var decipher = new AES(Array.prototype.slice.call(key)); // byte and word-level access for the decrypted output
39747  
39748      var decrypted = new Uint8Array(encrypted.byteLength);
39749      var decrypted32 = new Int32Array(decrypted.buffer); // temporary variables for working with the IV, encrypted, and
39750      // decrypted data
39751  
39752      var init0 = void 0;
39753      var init1 = void 0;
39754      var init2 = void 0;
39755      var init3 = void 0;
39756      var encrypted0 = void 0;
39757      var encrypted1 = void 0;
39758      var encrypted2 = void 0;
39759      var encrypted3 = void 0; // iteration variable
39760  
39761      var wordIx = void 0; // pull out the words of the IV to ensure we don't modify the
39762      // passed-in reference and easier access
39763  
39764      init0 = initVector[0];
39765      init1 = initVector[1];
39766      init2 = initVector[2];
39767      init3 = initVector[3]; // decrypt four word sequences, applying cipher-block chaining (CBC)
39768      // to each decrypted block
39769  
39770      for (wordIx = 0; wordIx < encrypted32.length; wordIx += 4) {
39771        // convert big-endian (network order) words into little-endian
39772        // (javascript order)
39773        encrypted0 = ntoh(encrypted32[wordIx]);
39774        encrypted1 = ntoh(encrypted32[wordIx + 1]);
39775        encrypted2 = ntoh(encrypted32[wordIx + 2]);
39776        encrypted3 = ntoh(encrypted32[wordIx + 3]); // decrypt the block
39777  
39778        decipher.decrypt(encrypted0, encrypted1, encrypted2, encrypted3, decrypted32, wordIx); // XOR with the IV, and restore network byte-order to obtain the
39779        // plaintext
39780  
39781        decrypted32[wordIx] = ntoh(decrypted32[wordIx] ^ init0);
39782        decrypted32[wordIx + 1] = ntoh(decrypted32[wordIx + 1] ^ init1);
39783        decrypted32[wordIx + 2] = ntoh(decrypted32[wordIx + 2] ^ init2);
39784        decrypted32[wordIx + 3] = ntoh(decrypted32[wordIx + 3] ^ init3); // setup the IV for the next round
39785  
39786        init0 = encrypted0;
39787        init1 = encrypted1;
39788        init2 = encrypted2;
39789        init3 = encrypted3;
39790      }
39791  
39792      return decrypted;
39793    };
39794    /**
39795     * The `Decrypter` class that manages decryption of AES
39796     * data through `AsyncStream` objects and the `decrypt`
39797     * function
39798     *
39799     * @param {Uint8Array} encrypted the encrypted bytes
39800     * @param {Uint32Array} key the bytes of the decryption key
39801     * @param {Uint32Array} initVector the initialization vector (IV) to
39802     * @param {Function} done the function to run when done
39803     * @class Decrypter
39804     */
39805  
39806  
39807    var Decrypter = function () {
39808      function Decrypter(encrypted, key, initVector, done) {
39809        classCallCheck(this, Decrypter);
39810        var step = Decrypter.STEP;
39811        var encrypted32 = new Int32Array(encrypted.buffer);
39812        var decrypted = new Uint8Array(encrypted.byteLength);
39813        var i = 0;
39814        this.asyncStream_ = new AsyncStream(); // split up the encryption job and do the individual chunks asynchronously
39815  
39816        this.asyncStream_.push(this.decryptChunk_(encrypted32.subarray(i, i + step), key, initVector, decrypted));
39817  
39818        for (i = step; i < encrypted32.length; i += step) {
39819          initVector = new Uint32Array([ntoh(encrypted32[i - 4]), ntoh(encrypted32[i - 3]), ntoh(encrypted32[i - 2]), ntoh(encrypted32[i - 1])]);
39820          this.asyncStream_.push(this.decryptChunk_(encrypted32.subarray(i, i + step), key, initVector, decrypted));
39821        } // invoke the done() callback when everything is finished
39822  
39823  
39824        this.asyncStream_.push(function () {
39825          // remove pkcs#7 padding from the decrypted bytes
39826          done(null, unpad(decrypted));
39827        });
39828      }
39829      /**
39830       * a getter for step the maximum number of bytes to process at one time
39831       *
39832       * @return {Number} the value of step 32000
39833       */
39834  
39835      /**
39836       * @private
39837       */
39838  
39839  
39840      Decrypter.prototype.decryptChunk_ = function decryptChunk_(encrypted, key, initVector, decrypted) {
39841        return function () {
39842          var bytes = decrypt(encrypted, key, initVector);
39843          decrypted.set(bytes, encrypted.byteOffset);
39844        };
39845      };
39846  
39847      createClass(Decrypter, null, [{
39848        key: 'STEP',
39849        get: function get$$1() {
39850          // 4 * 8000;
39851          return 32000;
39852        }
39853      }]);
39854      return Decrypter;
39855    }();
39856  
39857    /**
39858     * @videojs/http-streaming
39859     * @version 1.13.2
39860     * @copyright 2020 Brightcove, Inc
39861     * @license Apache-2.0
39862     */
39863    /**
39864     * @file resolve-url.js - Handling how URLs are resolved and manipulated
39865     */
39866  
39867    var resolveUrl$1 = function resolveUrl(baseURL, relativeURL) {
39868      // return early if we don't need to resolve
39869      if (/^[a-z]+:/i.test(relativeURL)) {
39870        return relativeURL;
39871      } // if the base URL is relative then combine with the current location
39872  
39873  
39874      if (!/\/\//i.test(baseURL)) {
39875        baseURL = urlToolkit.buildAbsoluteURL(window$3.location.href, baseURL);
39876      }
39877  
39878      return urlToolkit.buildAbsoluteURL(baseURL, relativeURL);
39879    };
39880    /**
39881     * Checks whether xhr request was redirected and returns correct url depending
39882     * on `handleManifestRedirects` option
39883     *
39884     * @api private
39885     *
39886     * @param  {String} url - an url being requested
39887     * @param  {XMLHttpRequest} req - xhr request result
39888     *
39889     * @return {String}
39890     */
39891  
39892  
39893    var resolveManifestRedirect = function resolveManifestRedirect(handleManifestRedirect, url, req) {
39894      // To understand how the responseURL below is set and generated:
39895      // - https://fetch.spec.whatwg.org/#concept-response-url
39896      // - https://fetch.spec.whatwg.org/#atomic-http-redirect-handling
39897      if (handleManifestRedirect && req.responseURL && url !== req.responseURL) {
39898        return req.responseURL;
39899      }
39900  
39901      return url;
39902    };
39903  
39904    var classCallCheck$1 = function classCallCheck(instance, Constructor) {
39905      if (!(instance instanceof Constructor)) {
39906        throw new TypeError("Cannot call a class as a function");
39907      }
39908    };
39909  
39910    var createClass$1 = function () {
39911      function defineProperties(target, props) {
39912        for (var i = 0; i < props.length; i++) {
39913          var descriptor = props[i];
39914          descriptor.enumerable = descriptor.enumerable || false;
39915          descriptor.configurable = true;
39916          if ("value" in descriptor) descriptor.writable = true;
39917          Object.defineProperty(target, descriptor.key, descriptor);
39918        }
39919      }
39920  
39921      return function (Constructor, protoProps, staticProps) {
39922        if (protoProps) defineProperties(Constructor.prototype, protoProps);
39923        if (staticProps) defineProperties(Constructor, staticProps);
39924        return Constructor;
39925      };
39926    }();
39927  
39928    var get$1 = function get(object, property, receiver) {
39929      if (object === null) object = Function.prototype;
39930      var desc = Object.getOwnPropertyDescriptor(object, property);
39931  
39932      if (desc === undefined) {
39933        var parent = Object.getPrototypeOf(object);
39934  
39935        if (parent === null) {
39936          return undefined;
39937        } else {
39938          return get(parent, property, receiver);
39939        }
39940      } else if ("value" in desc) {
39941        return desc.value;
39942      } else {
39943        var getter = desc.get;
39944  
39945        if (getter === undefined) {
39946          return undefined;
39947        }
39948  
39949        return getter.call(receiver);
39950      }
39951    };
39952  
39953    var inherits$2 = function inherits(subClass, superClass) {
39954      if (typeof superClass !== "function" && superClass !== null) {
39955        throw new TypeError("Super expression must either be null or a function, not " + typeof superClass);
39956      }
39957  
39958      subClass.prototype = Object.create(superClass && superClass.prototype, {
39959        constructor: {
39960          value: subClass,
39961          enumerable: false,
39962          writable: true,
39963          configurable: true
39964        }
39965      });
39966      if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass;
39967    };
39968  
39969    var possibleConstructorReturn$1 = function possibleConstructorReturn(self, call) {
39970      if (!self) {
39971        throw new ReferenceError("this hasn't been initialised - super() hasn't been called");
39972      }
39973  
39974      return call && (typeof call === "object" || typeof call === "function") ? call : self;
39975    };
39976  
39977    var slicedToArray = function () {
39978      function sliceIterator(arr, i) {
39979        var _arr = [];
39980        var _n = true;
39981        var _d = false;
39982        var _e = undefined;
39983  
39984        try {
39985          for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) {
39986            _arr.push(_s.value);
39987  
39988            if (i && _arr.length === i) break;
39989          }
39990        } catch (err) {
39991          _d = true;
39992          _e = err;
39993        } finally {
39994          try {
39995            if (!_n && _i["return"]) _i["return"]();
39996          } finally {
39997            if (_d) throw _e;
39998          }
39999        }
40000  
40001        return _arr;
40002      }
40003  
40004      return function (arr, i) {
40005        if (Array.isArray(arr)) {
40006          return arr;
40007        } else if (Symbol.iterator in Object(arr)) {
40008          return sliceIterator(arr, i);
40009        } else {
40010          throw new TypeError("Invalid attempt to destructure non-iterable instance");
40011        }
40012      };
40013    }();
40014    /**
40015     * @file playlist-loader.js
40016     *
40017     * A state machine that manages the loading, caching, and updating of
40018     * M3U8 playlists.
40019     *
40020     */
40021  
40022  
40023    var mergeOptions$1 = videojs$1.mergeOptions,
40024        EventTarget$1 = videojs$1.EventTarget,
40025        log$1 = videojs$1.log;
40026    /**
40027     * Loops through all supported media groups in master and calls the provided
40028     * callback for each group
40029     *
40030     * @param {Object} master
40031     *        The parsed master manifest object
40032     * @param {Function} callback
40033     *        Callback to call for each media group
40034     */
40035  
40036    var forEachMediaGroup = function forEachMediaGroup(master, callback) {
40037      ['AUDIO', 'SUBTITLES'].forEach(function (mediaType) {
40038        for (var groupKey in master.mediaGroups[mediaType]) {
40039          for (var labelKey in master.mediaGroups[mediaType][groupKey]) {
40040            var mediaProperties = master.mediaGroups[mediaType][groupKey][labelKey];
40041            callback(mediaProperties, mediaType, groupKey, labelKey);
40042          }
40043        }
40044      });
40045    };
40046    /**
40047      * Returns a new array of segments that is the result of merging
40048      * properties from an older list of segments onto an updated
40049      * list. No properties on the updated playlist will be overridden.
40050      *
40051      * @param {Array} original the outdated list of segments
40052      * @param {Array} update the updated list of segments
40053      * @param {Number=} offset the index of the first update
40054      * segment in the original segment list. For non-live playlists,
40055      * this should always be zero and does not need to be
40056      * specified. For live playlists, it should be the difference
40057      * between the media sequence numbers in the original and updated
40058      * playlists.
40059      * @return a list of merged segment objects
40060      */
40061  
40062  
40063    var updateSegments = function updateSegments(original, update, offset) {
40064      var result = update.slice();
40065      offset = offset || 0;
40066      var length = Math.min(original.length, update.length + offset);
40067  
40068      for (var i = offset; i < length; i++) {
40069        result[i - offset] = mergeOptions$1(original[i], result[i - offset]);
40070      }
40071  
40072      return result;
40073    };
40074  
40075    var resolveSegmentUris = function resolveSegmentUris(segment, baseUri) {
40076      if (!segment.resolvedUri) {
40077        segment.resolvedUri = resolveUrl$1(baseUri, segment.uri);
40078      }
40079  
40080      if (segment.key && !segment.key.resolvedUri) {
40081        segment.key.resolvedUri = resolveUrl$1(baseUri, segment.key.uri);
40082      }
40083  
40084      if (segment.map && !segment.map.resolvedUri) {
40085        segment.map.resolvedUri = resolveUrl$1(baseUri, segment.map.uri);
40086      }
40087    };
40088    /**
40089      * Returns a new master playlist that is the result of merging an
40090      * updated media playlist into the original version. If the
40091      * updated media playlist does not match any of the playlist
40092      * entries in the original master playlist, null is returned.
40093      *
40094      * @param {Object} master a parsed master M3U8 object
40095      * @param {Object} media a parsed media M3U8 object
40096      * @return {Object} a new object that represents the original
40097      * master playlist with the updated media playlist merged in, or
40098      * null if the merge produced no change.
40099      */
40100  
40101  
40102    var updateMaster = function updateMaster(master, media) {
40103      var result = mergeOptions$1(master, {});
40104      var playlist = result.playlists[media.id];
40105  
40106      if (!playlist) {
40107        return null;
40108      } // consider the playlist unchanged if the number of segments is equal, the media
40109      // sequence number is unchanged, and this playlist hasn't become the end of the playlist
40110  
40111  
40112      if (playlist.segments && media.segments && playlist.segments.length === media.segments.length && playlist.endList === media.endList && playlist.mediaSequence === media.mediaSequence) {
40113        return null;
40114      }
40115  
40116      var mergedPlaylist = mergeOptions$1(playlist, media); // if the update could overlap existing segment information, merge the two segment lists
40117  
40118      if (playlist.segments) {
40119        mergedPlaylist.segments = updateSegments(playlist.segments, media.segments, media.mediaSequence - playlist.mediaSequence);
40120      } // resolve any segment URIs to prevent us from having to do it later
40121  
40122  
40123      mergedPlaylist.segments.forEach(function (segment) {
40124        resolveSegmentUris(segment, mergedPlaylist.resolvedUri);
40125      }); // TODO Right now in the playlists array there are two references to each playlist, one
40126      // that is referenced by index, and one by URI. The index reference may no longer be
40127      // necessary.
40128  
40129      for (var i = 0; i < result.playlists.length; i++) {
40130        if (result.playlists[i].id === media.id) {
40131          result.playlists[i] = mergedPlaylist;
40132        }
40133      }
40134  
40135      result.playlists[media.id] = mergedPlaylist; // URI reference added for backwards compatibility
40136  
40137      result.playlists[media.uri] = mergedPlaylist;
40138      return result;
40139    };
40140  
40141    var createPlaylistID = function createPlaylistID(index, uri) {
40142      return index + '-' + uri;
40143    };
40144  
40145    var setupMediaPlaylists = function setupMediaPlaylists(master) {
40146      // setup by-URI lookups and resolve media playlist URIs
40147      var i = master.playlists.length;
40148  
40149      while (i--) {
40150        var playlist = master.playlists[i];
40151        playlist.resolvedUri = resolveUrl$1(master.uri, playlist.uri);
40152        playlist.id = createPlaylistID(i, playlist.uri);
40153        master.playlists[playlist.id] = playlist; // URI reference added for backwards compatibility
40154  
40155        master.playlists[playlist.uri] = playlist;
40156  
40157        if (!playlist.attributes) {
40158          // Although the spec states an #EXT-X-STREAM-INF tag MUST have a
40159          // BANDWIDTH attribute, we can play the stream without it. This means a poorly
40160          // formatted master playlist may not have an attribute list. An attributes
40161          // property is added here to prevent undefined references when we encounter
40162          // this scenario.
40163          playlist.attributes = {};
40164          log$1.warn('Invalid playlist STREAM-INF detected. Missing BANDWIDTH attribute.');
40165        }
40166      }
40167    };
40168  
40169    var resolveMediaGroupUris = function resolveMediaGroupUris(master) {
40170      forEachMediaGroup(master, function (properties) {
40171        if (properties.uri) {
40172          properties.resolvedUri = resolveUrl$1(master.uri, properties.uri);
40173        }
40174      });
40175    };
40176    /**
40177     * Calculates the time to wait before refreshing a live playlist
40178     *
40179     * @param {Object} media
40180     *        The current media
40181     * @param {Boolean} update
40182     *        True if there were any updates from the last refresh, false otherwise
40183     * @return {Number}
40184     *         The time in ms to wait before refreshing the live playlist
40185     */
40186  
40187  
40188    var refreshDelay = function refreshDelay(media, update) {
40189      var lastSegment = media.segments[media.segments.length - 1];
40190      var delay = void 0;
40191  
40192      if (update && lastSegment && lastSegment.duration) {
40193        delay = lastSegment.duration * 1000;
40194      } else {
40195        // if the playlist is unchanged since the last reload or last segment duration
40196        // cannot be determined, try again after half the target duration
40197        delay = (media.targetDuration || 10) * 500;
40198      }
40199  
40200      return delay;
40201    };
40202    /**
40203     * Load a playlist from a remote location
40204     *
40205     * @class PlaylistLoader
40206     * @extends Stream
40207     * @param {String} srcUrl the url to start with
40208     * @param {Boolean} withCredentials the withCredentials xhr option
40209     * @constructor
40210     */
40211  
40212  
40213    var PlaylistLoader = function (_EventTarget) {
40214      inherits$2(PlaylistLoader, _EventTarget);
40215  
40216      function PlaylistLoader(srcUrl, hls) {
40217        var options = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
40218        classCallCheck$1(this, PlaylistLoader);
40219  
40220        var _this = possibleConstructorReturn$1(this, (PlaylistLoader.__proto__ || Object.getPrototypeOf(PlaylistLoader)).call(this));
40221  
40222        var _options$withCredenti = options.withCredentials,
40223            withCredentials = _options$withCredenti === undefined ? false : _options$withCredenti,
40224            _options$handleManife = options.handleManifestRedirects,
40225            handleManifestRedirects = _options$handleManife === undefined ? false : _options$handleManife;
40226        _this.srcUrl = srcUrl;
40227        _this.hls_ = hls;
40228        _this.withCredentials = withCredentials;
40229        _this.handleManifestRedirects = handleManifestRedirects;
40230        var hlsOptions = hls.options_;
40231        _this.customTagParsers = hlsOptions && hlsOptions.customTagParsers || [];
40232        _this.customTagMappers = hlsOptions && hlsOptions.customTagMappers || [];
40233  
40234        if (!_this.srcUrl) {
40235          throw new Error('A non-empty playlist URL is required');
40236        } // initialize the loader state
40237  
40238  
40239        _this.state = 'HAVE_NOTHING'; // live playlist staleness timeout
40240  
40241        _this.on('mediaupdatetimeout', function () {
40242          if (_this.state !== 'HAVE_METADATA') {
40243            // only refresh the media playlist if no other activity is going on
40244            return;
40245          }
40246  
40247          _this.state = 'HAVE_CURRENT_METADATA';
40248          _this.request = _this.hls_.xhr({
40249            uri: resolveUrl$1(_this.master.uri, _this.media().uri),
40250            withCredentials: _this.withCredentials
40251          }, function (error, req) {
40252            // disposed
40253            if (!_this.request) {
40254              return;
40255            }
40256  
40257            if (error) {
40258              return _this.playlistRequestError(_this.request, _this.media(), 'HAVE_METADATA');
40259            }
40260  
40261            _this.haveMetadata(_this.request, _this.media().uri, _this.media().id);
40262          });
40263        });
40264  
40265        return _this;
40266      }
40267  
40268      createClass$1(PlaylistLoader, [{
40269        key: 'playlistRequestError',
40270        value: function playlistRequestError(xhr, playlist, startingState) {
40271          var uri = playlist.uri,
40272              id = playlist.id; // any in-flight request is now finished
40273  
40274          this.request = null;
40275  
40276          if (startingState) {
40277            this.state = startingState;
40278          }
40279  
40280          this.error = {
40281            playlist: this.master.playlists[id],
40282            status: xhr.status,
40283            message: 'HLS playlist request error at URL: ' + uri + '.',
40284            responseText: xhr.responseText,
40285            code: xhr.status >= 500 ? 4 : 2
40286          };
40287          this.trigger('error');
40288        } // update the playlist loader's state in response to a new or
40289        // updated playlist.
40290  
40291      }, {
40292        key: 'haveMetadata',
40293        value: function haveMetadata(xhr, url, id) {
40294          var _this2 = this; // any in-flight request is now finished
40295  
40296  
40297          this.request = null;
40298          this.state = 'HAVE_METADATA';
40299          var parser = new Parser(); // adding custom tag parsers
40300  
40301          this.customTagParsers.forEach(function (customParser) {
40302            return parser.addParser(customParser);
40303          }); // adding custom tag mappers
40304  
40305          this.customTagMappers.forEach(function (mapper) {
40306            return parser.addTagMapper(mapper);
40307          });
40308          parser.push(xhr.responseText);
40309          parser.end();
40310          parser.manifest.uri = url;
40311          parser.manifest.id = id; // m3u8-parser does not attach an attributes property to media playlists so make
40312          // sure that the property is attached to avoid undefined reference errors
40313  
40314          parser.manifest.attributes = parser.manifest.attributes || {}; // merge this playlist into the master
40315  
40316          var update = updateMaster(this.master, parser.manifest);
40317          this.targetDuration = parser.manifest.targetDuration;
40318  
40319          if (update) {
40320            this.master = update;
40321            this.media_ = this.master.playlists[id];
40322          } else {
40323            this.trigger('playlistunchanged');
40324          } // refresh live playlists after a target duration passes
40325  
40326  
40327          if (!this.media().endList) {
40328            window$3.clearTimeout(this.mediaUpdateTimeout);
40329            this.mediaUpdateTimeout = window$3.setTimeout(function () {
40330              _this2.trigger('mediaupdatetimeout');
40331            }, refreshDelay(this.media(), !!update));
40332          }
40333  
40334          this.trigger('loadedplaylist');
40335        }
40336        /**
40337         * Abort any outstanding work and clean up.
40338         */
40339  
40340      }, {
40341        key: 'dispose',
40342        value: function dispose() {
40343          this.trigger('dispose');
40344          this.stopRequest();
40345          window$3.clearTimeout(this.mediaUpdateTimeout);
40346          window$3.clearTimeout(this.finalRenditionTimeout);
40347          this.off();
40348        }
40349      }, {
40350        key: 'stopRequest',
40351        value: function stopRequest() {
40352          if (this.request) {
40353            var oldRequest = this.request;
40354            this.request = null;
40355            oldRequest.onreadystatechange = null;
40356            oldRequest.abort();
40357          }
40358        }
40359        /**
40360         * When called without any arguments, returns the currently
40361         * active media playlist. When called with a single argument,
40362         * triggers the playlist loader to asynchronously switch to the
40363         * specified media playlist. Calling this method while the
40364         * loader is in the HAVE_NOTHING causes an error to be emitted
40365         * but otherwise has no effect.
40366         *
40367         * @param {Object=} playlist the parsed media playlist
40368         * object to switch to
40369         * @param {Boolean=} is this the last available playlist
40370         *
40371         * @return {Playlist} the current loaded media
40372         */
40373  
40374      }, {
40375        key: 'media',
40376        value: function media(playlist, isFinalRendition) {
40377          var _this3 = this; // getter
40378  
40379  
40380          if (!playlist) {
40381            return this.media_;
40382          } // setter
40383  
40384  
40385          if (this.state === 'HAVE_NOTHING') {
40386            throw new Error('Cannot switch media playlist from ' + this.state);
40387          } // find the playlist object if the target playlist has been
40388          // specified by URI
40389  
40390  
40391          if (typeof playlist === 'string') {
40392            if (!this.master.playlists[playlist]) {
40393              throw new Error('Unknown playlist URI: ' + playlist);
40394            }
40395  
40396            playlist = this.master.playlists[playlist];
40397          }
40398  
40399          window$3.clearTimeout(this.finalRenditionTimeout);
40400  
40401          if (isFinalRendition) {
40402            var delay = playlist.targetDuration / 2 * 1000 || 5 * 1000;
40403            this.finalRenditionTimeout = window$3.setTimeout(this.media.bind(this, playlist, false), delay);
40404            return;
40405          }
40406  
40407          var startingState = this.state;
40408          var mediaChange = !this.media_ || playlist.id !== this.media_.id; // switch to fully loaded playlists immediately
40409  
40410          if (this.master.playlists[playlist.id].endList) {
40411            // abort outstanding playlist requests
40412            if (this.request) {
40413              this.request.onreadystatechange = null;
40414              this.request.abort();
40415              this.request = null;
40416            }
40417  
40418            this.state = 'HAVE_METADATA';
40419            this.media_ = playlist; // trigger media change if the active media has been updated
40420  
40421            if (mediaChange) {
40422              this.trigger('mediachanging');
40423              this.trigger('mediachange');
40424            }
40425  
40426            return;
40427          } // switching to the active playlist is a no-op
40428  
40429  
40430          if (!mediaChange) {
40431            return;
40432          }
40433  
40434          this.state = 'SWITCHING_MEDIA'; // there is already an outstanding playlist request
40435  
40436          if (this.request) {
40437            if (playlist.resolvedUri === this.request.url) {
40438              // requesting to switch to the same playlist multiple times
40439              // has no effect after the first
40440              return;
40441            }
40442  
40443            this.request.onreadystatechange = null;
40444            this.request.abort();
40445            this.request = null;
40446          } // request the new playlist
40447  
40448  
40449          if (this.media_) {
40450            this.trigger('mediachanging');
40451          }
40452  
40453          this.request = this.hls_.xhr({
40454            uri: playlist.resolvedUri,
40455            withCredentials: this.withCredentials
40456          }, function (error, req) {
40457            // disposed
40458            if (!_this3.request) {
40459              return;
40460            }
40461  
40462            playlist.resolvedUri = resolveManifestRedirect(_this3.handleManifestRedirects, playlist.resolvedUri, req);
40463  
40464            if (error) {
40465              return _this3.playlistRequestError(_this3.request, playlist, startingState);
40466            }
40467  
40468            _this3.haveMetadata(req, playlist.uri, playlist.id); // fire loadedmetadata the first time a media playlist is loaded
40469  
40470  
40471            if (startingState === 'HAVE_MASTER') {
40472              _this3.trigger('loadedmetadata');
40473            } else {
40474              _this3.trigger('mediachange');
40475            }
40476          });
40477        }
40478        /**
40479         * pause loading of the playlist
40480         */
40481  
40482      }, {
40483        key: 'pause',
40484        value: function pause() {
40485          this.stopRequest();
40486          window$3.clearTimeout(this.mediaUpdateTimeout);
40487  
40488          if (this.state === 'HAVE_NOTHING') {
40489            // If we pause the loader before any data has been retrieved, its as if we never
40490            // started, so reset to an unstarted state.
40491            this.started = false;
40492          } // Need to restore state now that no activity is happening
40493  
40494  
40495          if (this.state === 'SWITCHING_MEDIA') {
40496            // if the loader was in the process of switching media, it should either return to
40497            // HAVE_MASTER or HAVE_METADATA depending on if the loader has loaded a media
40498            // playlist yet. This is determined by the existence of loader.media_
40499            if (this.media_) {
40500              this.state = 'HAVE_METADATA';
40501            } else {
40502              this.state = 'HAVE_MASTER';
40503            }
40504          } else if (this.state === 'HAVE_CURRENT_METADATA') {
40505            this.state = 'HAVE_METADATA';
40506          }
40507        }
40508        /**
40509         * start loading of the playlist
40510         */
40511  
40512      }, {
40513        key: 'load',
40514        value: function load(isFinalRendition) {
40515          var _this4 = this;
40516  
40517          window$3.clearTimeout(this.mediaUpdateTimeout);
40518          var media = this.media();
40519  
40520          if (isFinalRendition) {
40521            var delay = media ? media.targetDuration / 2 * 1000 : 5 * 1000;
40522            this.mediaUpdateTimeout = window$3.setTimeout(function () {
40523              return _this4.load();
40524            }, delay);
40525            return;
40526          }
40527  
40528          if (!this.started) {
40529            this.start();
40530            return;
40531          }
40532  
40533          if (media && !media.endList) {
40534            this.trigger('mediaupdatetimeout');
40535          } else {
40536            this.trigger('loadedplaylist');
40537          }
40538        }
40539        /**
40540         * start loading of the playlist
40541         */
40542  
40543      }, {
40544        key: 'start',
40545        value: function start() {
40546          var _this5 = this;
40547  
40548          this.started = true; // request the specified URL
40549  
40550          this.request = this.hls_.xhr({
40551            uri: this.srcUrl,
40552            withCredentials: this.withCredentials
40553          }, function (error, req) {
40554            // disposed
40555            if (!_this5.request) {
40556              return;
40557            } // clear the loader's request reference
40558  
40559  
40560            _this5.request = null;
40561  
40562            if (error) {
40563              _this5.error = {
40564                status: req.status,
40565                message: 'HLS playlist request error at URL: ' + _this5.srcUrl + '.',
40566                responseText: req.responseText,
40567                // MEDIA_ERR_NETWORK
40568                code: 2
40569              };
40570  
40571              if (_this5.state === 'HAVE_NOTHING') {
40572                _this5.started = false;
40573              }
40574  
40575              return _this5.trigger('error');
40576            }
40577  
40578            var parser = new Parser(); // adding custom tag parsers
40579  
40580            _this5.customTagParsers.forEach(function (customParser) {
40581              return parser.addParser(customParser);
40582            }); // adding custom tag mappers
40583  
40584  
40585            _this5.customTagMappers.forEach(function (mapper) {
40586              return parser.addTagMapper(mapper);
40587            });
40588  
40589            parser.push(req.responseText);
40590            parser.end();
40591            _this5.state = 'HAVE_MASTER';
40592            _this5.srcUrl = resolveManifestRedirect(_this5.handleManifestRedirects, _this5.srcUrl, req);
40593            parser.manifest.uri = _this5.srcUrl; // loaded a master playlist
40594  
40595            if (parser.manifest.playlists) {
40596              _this5.master = parser.manifest;
40597              setupMediaPlaylists(_this5.master);
40598              resolveMediaGroupUris(_this5.master);
40599  
40600              _this5.trigger('loadedplaylist');
40601  
40602              if (!_this5.request) {
40603                // no media playlist was specifically selected so start
40604                // from the first listed one
40605                _this5.media(parser.manifest.playlists[0]);
40606              }
40607  
40608              return;
40609            }
40610  
40611            var id = createPlaylistID(0, _this5.srcUrl); // loaded a media playlist
40612            // infer a master playlist if none was previously requested
40613  
40614            _this5.master = {
40615              mediaGroups: {
40616                'AUDIO': {},
40617                'VIDEO': {},
40618                'CLOSED-CAPTIONS': {},
40619                'SUBTITLES': {}
40620              },
40621              uri: window$3.location.href,
40622              playlists: [{
40623                uri: _this5.srcUrl,
40624                id: id,
40625                resolvedUri: _this5.srcUrl,
40626                // m3u8-parser does not attach an attributes property to media playlists so make
40627                // sure that the property is attached to avoid undefined reference errors
40628                attributes: {}
40629              }]
40630            };
40631            _this5.master.playlists[id] = _this5.master.playlists[0]; // URI reference added for backwards compatibility
40632  
40633            _this5.master.playlists[_this5.srcUrl] = _this5.master.playlists[0];
40634  
40635            _this5.haveMetadata(req, _this5.srcUrl, id);
40636  
40637            return _this5.trigger('loadedmetadata');
40638          });
40639        }
40640      }]);
40641      return PlaylistLoader;
40642    }(EventTarget$1);
40643    /**
40644     * @file playlist.js
40645     *
40646     * Playlist related utilities.
40647     */
40648  
40649  
40650    var createTimeRange = videojs$1.createTimeRange;
40651    /**
40652     * walk backward until we find a duration we can use
40653     * or return a failure
40654     *
40655     * @param {Playlist} playlist the playlist to walk through
40656     * @param {Number} endSequence the mediaSequence to stop walking on
40657     */
40658  
40659    var backwardDuration = function backwardDuration(playlist, endSequence) {
40660      var result = 0;
40661      var i = endSequence - playlist.mediaSequence; // if a start time is available for segment immediately following
40662      // the interval, use it
40663  
40664      var segment = playlist.segments[i]; // Walk backward until we find the latest segment with timeline
40665      // information that is earlier than endSequence
40666  
40667      if (segment) {
40668        if (typeof segment.start !== 'undefined') {
40669          return {
40670            result: segment.start,
40671            precise: true
40672          };
40673        }
40674  
40675        if (typeof segment.end !== 'undefined') {
40676          return {
40677            result: segment.end - segment.duration,
40678            precise: true
40679          };
40680        }
40681      }
40682  
40683      while (i--) {
40684        segment = playlist.segments[i];
40685  
40686        if (typeof segment.end !== 'undefined') {
40687          return {
40688            result: result + segment.end,
40689            precise: true
40690          };
40691        }
40692  
40693        result += segment.duration;
40694  
40695        if (typeof segment.start !== 'undefined') {
40696          return {
40697            result: result + segment.start,
40698            precise: true
40699          };
40700        }
40701      }
40702  
40703      return {
40704        result: result,
40705        precise: false
40706      };
40707    };
40708    /**
40709     * walk forward until we find a duration we can use
40710     * or return a failure
40711     *
40712     * @param {Playlist} playlist the playlist to walk through
40713     * @param {Number} endSequence the mediaSequence to stop walking on
40714     */
40715  
40716  
40717    var forwardDuration = function forwardDuration(playlist, endSequence) {
40718      var result = 0;
40719      var segment = void 0;
40720      var i = endSequence - playlist.mediaSequence; // Walk forward until we find the earliest segment with timeline
40721      // information
40722  
40723      for (; i < playlist.segments.length; i++) {
40724        segment = playlist.segments[i];
40725  
40726        if (typeof segment.start !== 'undefined') {
40727          return {
40728            result: segment.start - result,
40729            precise: true
40730          };
40731        }
40732  
40733        result += segment.duration;
40734  
40735        if (typeof segment.end !== 'undefined') {
40736          return {
40737            result: segment.end - result,
40738            precise: true
40739          };
40740        }
40741      } // indicate we didn't find a useful duration estimate
40742  
40743  
40744      return {
40745        result: -1,
40746        precise: false
40747      };
40748    };
40749    /**
40750      * Calculate the media duration from the segments associated with a
40751      * playlist. The duration of a subinterval of the available segments
40752      * may be calculated by specifying an end index.
40753      *
40754      * @param {Object} playlist a media playlist object
40755      * @param {Number=} endSequence an exclusive upper boundary
40756      * for the playlist.  Defaults to playlist length.
40757      * @param {Number} expired the amount of time that has dropped
40758      * off the front of the playlist in a live scenario
40759      * @return {Number} the duration between the first available segment
40760      * and end index.
40761      */
40762  
40763  
40764    var intervalDuration = function intervalDuration(playlist, endSequence, expired) {
40765      var backward = void 0;
40766      var forward = void 0;
40767  
40768      if (typeof endSequence === 'undefined') {
40769        endSequence = playlist.mediaSequence + playlist.segments.length;
40770      }
40771  
40772      if (endSequence < playlist.mediaSequence) {
40773        return 0;
40774      } // do a backward walk to estimate the duration
40775  
40776  
40777      backward = backwardDuration(playlist, endSequence);
40778  
40779      if (backward.precise) {
40780        // if we were able to base our duration estimate on timing
40781        // information provided directly from the Media Source, return
40782        // it
40783        return backward.result;
40784      } // walk forward to see if a precise duration estimate can be made
40785      // that way
40786  
40787  
40788      forward = forwardDuration(playlist, endSequence);
40789  
40790      if (forward.precise) {
40791        // we found a segment that has been buffered and so it's
40792        // position is known precisely
40793        return forward.result;
40794      } // return the less-precise, playlist-based duration estimate
40795  
40796  
40797      return backward.result + expired;
40798    };
40799    /**
40800      * Calculates the duration of a playlist. If a start and end index
40801      * are specified, the duration will be for the subset of the media
40802      * timeline between those two indices. The total duration for live
40803      * playlists is always Infinity.
40804      *
40805      * @param {Object} playlist a media playlist object
40806      * @param {Number=} endSequence an exclusive upper
40807      * boundary for the playlist. Defaults to the playlist media
40808      * sequence number plus its length.
40809      * @param {Number=} expired the amount of time that has
40810      * dropped off the front of the playlist in a live scenario
40811      * @return {Number} the duration between the start index and end
40812      * index.
40813      */
40814  
40815  
40816    var duration = function duration(playlist, endSequence, expired) {
40817      if (!playlist) {
40818        return 0;
40819      }
40820  
40821      if (typeof expired !== 'number') {
40822        expired = 0;
40823      } // if a slice of the total duration is not requested, use
40824      // playlist-level duration indicators when they're present
40825  
40826  
40827      if (typeof endSequence === 'undefined') {
40828        // if present, use the duration specified in the playlist
40829        if (playlist.totalDuration) {
40830          return playlist.totalDuration;
40831        } // duration should be Infinity for live playlists
40832  
40833  
40834        if (!playlist.endList) {
40835          return window$3.Infinity;
40836        }
40837      } // calculate the total duration based on the segment durations
40838  
40839  
40840      return intervalDuration(playlist, endSequence, expired);
40841    };
40842    /**
40843      * Calculate the time between two indexes in the current playlist
40844      * neight the start- nor the end-index need to be within the current
40845      * playlist in which case, the targetDuration of the playlist is used
40846      * to approximate the durations of the segments
40847      *
40848      * @param {Object} playlist a media playlist object
40849      * @param {Number} startIndex
40850      * @param {Number} endIndex
40851      * @return {Number} the number of seconds between startIndex and endIndex
40852      */
40853  
40854  
40855    var sumDurations = function sumDurations(playlist, startIndex, endIndex) {
40856      var durations = 0;
40857  
40858      if (startIndex > endIndex) {
40859        var _ref = [endIndex, startIndex];
40860        startIndex = _ref[0];
40861        endIndex = _ref[1];
40862      }
40863  
40864      if (startIndex < 0) {
40865        for (var i = startIndex; i < Math.min(0, endIndex); i++) {
40866          durations += playlist.targetDuration;
40867        }
40868  
40869        startIndex = 0;
40870      }
40871  
40872      for (var _i = startIndex; _i < endIndex; _i++) {
40873        durations += playlist.segments[_i].duration;
40874      }
40875  
40876      return durations;
40877    };
40878    /**
40879     * Determines the media index of the segment corresponding to the safe edge of the live
40880     * window which is the duration of the last segment plus 2 target durations from the end
40881     * of the playlist.
40882     *
40883     * A liveEdgePadding can be provided which will be used instead of calculating the safe live edge.
40884     * This corresponds to suggestedPresentationDelay in DASH manifests.
40885     *
40886     * @param {Object} playlist
40887     *        a media playlist object
40888     * @param {Number} [liveEdgePadding]
40889     *        A number in seconds indicating how far from the end we want to be.
40890     *        If provided, this value is used instead of calculating the safe live index from the target durations.
40891     *        Corresponds to suggestedPresentationDelay in DASH manifests.
40892     * @return {Number}
40893     *         The media index of the segment at the safe live point. 0 if there is no "safe"
40894     *         point.
40895     * @function safeLiveIndex
40896     */
40897  
40898  
40899    var safeLiveIndex = function safeLiveIndex(playlist, liveEdgePadding) {
40900      if (!playlist.segments.length) {
40901        return 0;
40902      }
40903  
40904      var i = playlist.segments.length;
40905      var lastSegmentDuration = playlist.segments[i - 1].duration || playlist.targetDuration;
40906      var safeDistance = typeof liveEdgePadding === 'number' ? liveEdgePadding : lastSegmentDuration + playlist.targetDuration * 2;
40907  
40908      if (safeDistance === 0) {
40909        return i;
40910      }
40911  
40912      var distanceFromEnd = 0;
40913  
40914      while (i--) {
40915        distanceFromEnd += playlist.segments[i].duration;
40916  
40917        if (distanceFromEnd >= safeDistance) {
40918          break;
40919        }
40920      }
40921  
40922      return Math.max(0, i);
40923    };
40924    /**
40925     * Calculates the playlist end time
40926     *
40927     * @param {Object} playlist a media playlist object
40928     * @param {Number=} expired the amount of time that has
40929     *                  dropped off the front of the playlist in a live scenario
40930     * @param {Boolean|false} useSafeLiveEnd a boolean value indicating whether or not the
40931     *                        playlist end calculation should consider the safe live end
40932     *                        (truncate the playlist end by three segments). This is normally
40933     *                        used for calculating the end of the playlist's seekable range.
40934     *                        This takes into account the value of liveEdgePadding.
40935     *                        Setting liveEdgePadding to 0 is equivalent to setting this to false.
40936     * @param {Number} liveEdgePadding a number indicating how far from the end of the playlist we should be in seconds.
40937     *                 If this is provided, it is used in the safe live end calculation.
40938     *                 Setting useSafeLiveEnd=false or liveEdgePadding=0 are equivalent.
40939     *                 Corresponds to suggestedPresentationDelay in DASH manifests.
40940     * @returns {Number} the end time of playlist
40941     * @function playlistEnd
40942     */
40943  
40944  
40945    var playlistEnd = function playlistEnd(playlist, expired, useSafeLiveEnd, liveEdgePadding) {
40946      if (!playlist || !playlist.segments) {
40947        return null;
40948      }
40949  
40950      if (playlist.endList) {
40951        return duration(playlist);
40952      }
40953  
40954      if (expired === null) {
40955        return null;
40956      }
40957  
40958      expired = expired || 0;
40959      var endSequence = useSafeLiveEnd ? safeLiveIndex(playlist, liveEdgePadding) : playlist.segments.length;
40960      return intervalDuration(playlist, playlist.mediaSequence + endSequence, expired);
40961    };
40962    /**
40963      * Calculates the interval of time that is currently seekable in a
40964      * playlist. The returned time ranges are relative to the earliest
40965      * moment in the specified playlist that is still available. A full
40966      * seekable implementation for live streams would need to offset
40967      * these values by the duration of content that has expired from the
40968      * stream.
40969      *
40970      * @param {Object} playlist a media playlist object
40971      * dropped off the front of the playlist in a live scenario
40972      * @param {Number=} expired the amount of time that has
40973      * dropped off the front of the playlist in a live scenario
40974      * @param {Number} liveEdgePadding how far from the end of the playlist we should be in seconds.
40975      *        Corresponds to suggestedPresentationDelay in DASH manifests.
40976      * @return {TimeRanges} the periods of time that are valid targets
40977      * for seeking
40978      */
40979  
40980  
40981    var seekable = function seekable(playlist, expired, liveEdgePadding) {
40982      var useSafeLiveEnd = true;
40983      var seekableStart = expired || 0;
40984      var seekableEnd = playlistEnd(playlist, expired, useSafeLiveEnd, liveEdgePadding);
40985  
40986      if (seekableEnd === null) {
40987        return createTimeRange();
40988      }
40989  
40990      return createTimeRange(seekableStart, seekableEnd);
40991    };
40992  
40993    var isWholeNumber = function isWholeNumber(num) {
40994      return num - Math.floor(num) === 0;
40995    };
40996  
40997    var roundSignificantDigit = function roundSignificantDigit(increment, num) {
40998      // If we have a whole number, just add 1 to it
40999      if (isWholeNumber(num)) {
41000        return num + increment * 0.1;
41001      }
41002  
41003      var numDecimalDigits = num.toString().split('.')[1].length;
41004  
41005      for (var i = 1; i <= numDecimalDigits; i++) {
41006        var scale = Math.pow(10, i);
41007        var temp = num * scale;
41008  
41009        if (isWholeNumber(temp) || i === numDecimalDigits) {
41010          return (temp + increment) / scale;
41011        }
41012      }
41013    };
41014  
41015    var ceilLeastSignificantDigit = roundSignificantDigit.bind(null, 1);
41016    var floorLeastSignificantDigit = roundSignificantDigit.bind(null, -1);
41017    /**
41018     * Determine the index and estimated starting time of the segment that
41019     * contains a specified playback position in a media playlist.
41020     *
41021     * @param {Object} playlist the media playlist to query
41022     * @param {Number} currentTime The number of seconds since the earliest
41023     * possible position to determine the containing segment for
41024     * @param {Number} startIndex
41025     * @param {Number} startTime
41026     * @return {Object}
41027     */
41028  
41029    var getMediaInfoForTime = function getMediaInfoForTime(playlist, currentTime, startIndex, startTime) {
41030      var i = void 0;
41031      var segment = void 0;
41032      var numSegments = playlist.segments.length;
41033      var time = currentTime - startTime;
41034  
41035      if (time < 0) {
41036        // Walk backward from startIndex in the playlist, adding durations
41037        // until we find a segment that contains `time` and return it
41038        if (startIndex > 0) {
41039          for (i = startIndex - 1; i >= 0; i--) {
41040            segment = playlist.segments[i];
41041            time += floorLeastSignificantDigit(segment.duration);
41042  
41043            if (time > 0) {
41044              return {
41045                mediaIndex: i,
41046                startTime: startTime - sumDurations(playlist, startIndex, i)
41047              };
41048            }
41049          }
41050        } // We were unable to find a good segment within the playlist
41051        // so select the first segment
41052  
41053  
41054        return {
41055          mediaIndex: 0,
41056          startTime: currentTime
41057        };
41058      } // When startIndex is negative, we first walk forward to first segment
41059      // adding target durations. If we "run out of time" before getting to
41060      // the first segment, return the first segment
41061  
41062  
41063      if (startIndex < 0) {
41064        for (i = startIndex; i < 0; i++) {
41065          time -= playlist.targetDuration;
41066  
41067          if (time < 0) {
41068            return {
41069              mediaIndex: 0,
41070              startTime: currentTime
41071            };
41072          }
41073        }
41074  
41075        startIndex = 0;
41076      } // Walk forward from startIndex in the playlist, subtracting durations
41077      // until we find a segment that contains `time` and return it
41078  
41079  
41080      for (i = startIndex; i < numSegments; i++) {
41081        segment = playlist.segments[i];
41082        time -= ceilLeastSignificantDigit(segment.duration);
41083  
41084        if (time < 0) {
41085          return {
41086            mediaIndex: i,
41087            startTime: startTime + sumDurations(playlist, startIndex, i)
41088          };
41089        }
41090      } // We are out of possible candidates so load the last one...
41091  
41092  
41093      return {
41094        mediaIndex: numSegments - 1,
41095        startTime: currentTime
41096      };
41097    };
41098    /**
41099     * Check whether the playlist is blacklisted or not.
41100     *
41101     * @param {Object} playlist the media playlist object
41102     * @return {boolean} whether the playlist is blacklisted or not
41103     * @function isBlacklisted
41104     */
41105  
41106  
41107    var isBlacklisted = function isBlacklisted(playlist) {
41108      return playlist.excludeUntil && playlist.excludeUntil > Date.now();
41109    };
41110    /**
41111     * Check whether the playlist is compatible with current playback configuration or has
41112     * been blacklisted permanently for being incompatible.
41113     *
41114     * @param {Object} playlist the media playlist object
41115     * @return {boolean} whether the playlist is incompatible or not
41116     * @function isIncompatible
41117     */
41118  
41119  
41120    var isIncompatible = function isIncompatible(playlist) {
41121      return playlist.excludeUntil && playlist.excludeUntil === Infinity;
41122    };
41123    /**
41124     * Check whether the playlist is enabled or not.
41125     *
41126     * @param {Object} playlist the media playlist object
41127     * @return {boolean} whether the playlist is enabled or not
41128     * @function isEnabled
41129     */
41130  
41131  
41132    var isEnabled = function isEnabled(playlist) {
41133      var blacklisted = isBlacklisted(playlist);
41134      return !playlist.disabled && !blacklisted;
41135    };
41136    /**
41137     * Check whether the playlist has been manually disabled through the representations api.
41138     *
41139     * @param {Object} playlist the media playlist object
41140     * @return {boolean} whether the playlist is disabled manually or not
41141     * @function isDisabled
41142     */
41143  
41144  
41145    var isDisabled = function isDisabled(playlist) {
41146      return playlist.disabled;
41147    };
41148    /**
41149     * Returns whether the current playlist is an AES encrypted HLS stream
41150     *
41151     * @return {Boolean} true if it's an AES encrypted HLS stream
41152     */
41153  
41154  
41155    var isAes = function isAes(media) {
41156      for (var i = 0; i < media.segments.length; i++) {
41157        if (media.segments[i].key) {
41158          return true;
41159        }
41160      }
41161  
41162      return false;
41163    };
41164    /**
41165     * Returns whether the current playlist contains fMP4
41166     *
41167     * @return {Boolean} true if the playlist contains fMP4
41168     */
41169  
41170  
41171    var isFmp4 = function isFmp4(media) {
41172      for (var i = 0; i < media.segments.length; i++) {
41173        if (media.segments[i].map) {
41174          return true;
41175        }
41176      }
41177  
41178      return false;
41179    };
41180    /**
41181     * Checks if the playlist has a value for the specified attribute
41182     *
41183     * @param {String} attr
41184     *        Attribute to check for
41185     * @param {Object} playlist
41186     *        The media playlist object
41187     * @return {Boolean}
41188     *         Whether the playlist contains a value for the attribute or not
41189     * @function hasAttribute
41190     */
41191  
41192  
41193    var hasAttribute = function hasAttribute(attr, playlist) {
41194      return playlist.attributes && playlist.attributes[attr];
41195    };
41196    /**
41197     * Estimates the time required to complete a segment download from the specified playlist
41198     *
41199     * @param {Number} segmentDuration
41200     *        Duration of requested segment
41201     * @param {Number} bandwidth
41202     *        Current measured bandwidth of the player
41203     * @param {Object} playlist
41204     *        The media playlist object
41205     * @param {Number=} bytesReceived
41206     *        Number of bytes already received for the request. Defaults to 0
41207     * @return {Number|NaN}
41208     *         The estimated time to request the segment. NaN if bandwidth information for
41209     *         the given playlist is unavailable
41210     * @function estimateSegmentRequestTime
41211     */
41212  
41213  
41214    var estimateSegmentRequestTime = function estimateSegmentRequestTime(segmentDuration, bandwidth, playlist) {
41215      var bytesReceived = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : 0;
41216  
41217      if (!hasAttribute('BANDWIDTH', playlist)) {
41218        return NaN;
41219      }
41220  
41221      var size = segmentDuration * playlist.attributes.BANDWIDTH;
41222      return (size - bytesReceived * 8) / bandwidth;
41223    };
41224    /*
41225     * Returns whether the current playlist is the lowest rendition
41226     *
41227     * @return {Boolean} true if on lowest rendition
41228     */
41229  
41230  
41231    var isLowestEnabledRendition = function isLowestEnabledRendition(master, media) {
41232      if (master.playlists.length === 1) {
41233        return true;
41234      }
41235  
41236      var currentBandwidth = media.attributes.BANDWIDTH || Number.MAX_VALUE;
41237      return master.playlists.filter(function (playlist) {
41238        if (!isEnabled(playlist)) {
41239          return false;
41240        }
41241  
41242        return (playlist.attributes.BANDWIDTH || 0) < currentBandwidth;
41243      }).length === 0;
41244    }; // exports
41245  
41246  
41247    var Playlist = {
41248      duration: duration,
41249      seekable: seekable,
41250      safeLiveIndex: safeLiveIndex,
41251      getMediaInfoForTime: getMediaInfoForTime,
41252      isEnabled: isEnabled,
41253      isDisabled: isDisabled,
41254      isBlacklisted: isBlacklisted,
41255      isIncompatible: isIncompatible,
41256      playlistEnd: playlistEnd,
41257      isAes: isAes,
41258      isFmp4: isFmp4,
41259      hasAttribute: hasAttribute,
41260      estimateSegmentRequestTime: estimateSegmentRequestTime,
41261      isLowestEnabledRendition: isLowestEnabledRendition
41262    };
41263    /**
41264     * @file xhr.js
41265     */
41266  
41267    var videojsXHR = videojs$1.xhr,
41268        mergeOptions$1$1 = videojs$1.mergeOptions;
41269  
41270    var xhrFactory = function xhrFactory() {
41271      var xhr = function XhrFunction(options, callback) {
41272        // Add a default timeout for all hls requests
41273        options = mergeOptions$1$1({
41274          timeout: 45e3
41275        }, options); // Allow an optional user-specified function to modify the option
41276        // object before we construct the xhr request
41277  
41278        var beforeRequest = XhrFunction.beforeRequest || videojs$1.Hls.xhr.beforeRequest;
41279  
41280        if (beforeRequest && typeof beforeRequest === 'function') {
41281          var newOptions = beforeRequest(options);
41282  
41283          if (newOptions) {
41284            options = newOptions;
41285          }
41286        }
41287  
41288        var request = videojsXHR(options, function (error, response) {
41289          var reqResponse = request.response;
41290  
41291          if (!error && reqResponse) {
41292            request.responseTime = Date.now();
41293            request.roundTripTime = request.responseTime - request.requestTime;
41294            request.bytesReceived = reqResponse.byteLength || reqResponse.length;
41295  
41296            if (!request.bandwidth) {
41297              request.bandwidth = Math.floor(request.bytesReceived / request.roundTripTime * 8 * 1000);
41298            }
41299          }
41300  
41301          if (response.headers) {
41302            request.responseHeaders = response.headers;
41303          } // videojs.xhr now uses a specific code on the error
41304          // object to signal that a request has timed out instead
41305          // of setting a boolean on the request object
41306  
41307  
41308          if (error && error.code === 'ETIMEDOUT') {
41309            request.timedout = true;
41310          } // videojs.xhr no longer considers status codes outside of 200 and 0
41311          // (for file uris) to be errors, but the old XHR did, so emulate that
41312          // behavior. Status 206 may be used in response to byterange requests.
41313  
41314  
41315          if (!error && !request.aborted && response.statusCode !== 200 && response.statusCode !== 206 && response.statusCode !== 0) {
41316            error = new Error('XHR Failed with a response of: ' + (request && (reqResponse || request.responseText)));
41317          }
41318  
41319          callback(error, request);
41320        });
41321        var originalAbort = request.abort;
41322  
41323        request.abort = function () {
41324          request.aborted = true;
41325          return originalAbort.apply(request, arguments);
41326        };
41327  
41328        request.uri = options.uri;
41329        request.requestTime = Date.now();
41330        return request;
41331      };
41332  
41333      return xhr;
41334    };
41335    /**
41336     * Turns segment byterange into a string suitable for use in
41337     * HTTP Range requests
41338     *
41339     * @param {Object} byterange - an object with two values defining the start and end
41340     *                             of a byte-range
41341     */
41342  
41343  
41344    var byterangeStr = function byterangeStr(byterange) {
41345      var byterangeStart = void 0;
41346      var byterangeEnd = void 0; // `byterangeEnd` is one less than `offset + length` because the HTTP range
41347      // header uses inclusive ranges
41348  
41349      byterangeEnd = byterange.offset + byterange.length - 1;
41350      byterangeStart = byterange.offset;
41351      return 'bytes=' + byterangeStart + '-' + byterangeEnd;
41352    };
41353    /**
41354     * Defines headers for use in the xhr request for a particular segment.
41355     *
41356     * @param {Object} segment - a simplified copy of the segmentInfo object
41357     *                           from SegmentLoader
41358     */
41359  
41360  
41361    var segmentXhrHeaders = function segmentXhrHeaders(segment) {
41362      var headers = {};
41363  
41364      if (segment.byterange) {
41365        headers.Range = byterangeStr(segment.byterange);
41366      }
41367  
41368      return headers;
41369    };
41370    /**
41371     * @file bin-utils.js
41372     */
41373  
41374    /**
41375     * convert a TimeRange to text
41376     *
41377     * @param {TimeRange} range the timerange to use for conversion
41378     * @param {Number} i the iterator on the range to convert
41379     */
41380  
41381  
41382    var textRange = function textRange(range, i) {
41383      return range.start(i) + '-' + range.end(i);
41384    };
41385    /**
41386     * format a number as hex string
41387     *
41388     * @param {Number} e The number
41389     * @param {Number} i the iterator
41390     */
41391  
41392  
41393    var formatHexString = function formatHexString(e, i) {
41394      var value = e.toString(16);
41395      return '00'.substring(0, 2 - value.length) + value + (i % 2 ? ' ' : '');
41396    };
41397  
41398    var formatAsciiString = function formatAsciiString(e) {
41399      if (e >= 0x20 && e < 0x7e) {
41400        return String.fromCharCode(e);
41401      }
41402  
41403      return '.';
41404    };
41405    /**
41406     * Creates an object for sending to a web worker modifying properties that are TypedArrays
41407     * into a new object with seperated properties for the buffer, byteOffset, and byteLength.
41408     *
41409     * @param {Object} message
41410     *        Object of properties and values to send to the web worker
41411     * @return {Object}
41412     *         Modified message with TypedArray values expanded
41413     * @function createTransferableMessage
41414     */
41415  
41416  
41417    var createTransferableMessage = function createTransferableMessage(message) {
41418      var transferable = {};
41419      Object.keys(message).forEach(function (key) {
41420        var value = message[key];
41421  
41422        if (ArrayBuffer.isView(value)) {
41423          transferable[key] = {
41424            bytes: value.buffer,
41425            byteOffset: value.byteOffset,
41426            byteLength: value.byteLength
41427          };
41428        } else {
41429          transferable[key] = value;
41430        }
41431      });
41432      return transferable;
41433    };
41434    /**
41435     * Returns a unique string identifier for a media initialization
41436     * segment.
41437     */
41438  
41439  
41440    var initSegmentId = function initSegmentId(initSegment) {
41441      var byterange = initSegment.byterange || {
41442        length: Infinity,
41443        offset: 0
41444      };
41445      return [byterange.length, byterange.offset, initSegment.resolvedUri].join(',');
41446    };
41447    /**
41448     * Returns a unique string identifier for a media segment key.
41449     */
41450  
41451  
41452    var segmentKeyId = function segmentKeyId(key) {
41453      return key.resolvedUri;
41454    };
41455    /**
41456     * utils to help dump binary data to the console
41457     */
41458  
41459  
41460    var hexDump = function hexDump(data) {
41461      var bytes = Array.prototype.slice.call(data);
41462      var step = 16;
41463      var result = '';
41464      var hex = void 0;
41465      var ascii = void 0;
41466  
41467      for (var j = 0;
41467 j < bytes.length / step; j++) {
41468        hex = bytes.slice(j * step, j * step + step).map(formatHexString).join('');
41469        ascii = bytes.slice(j * step, j * step + step).map(formatAsciiString).join('');
41470        result += hex + ' ' + ascii + '\n';
41471      }
41472  
41473      return result;
41474    };
41475  
41476    var tagDump = function tagDump(_ref) {
41477      var bytes = _ref.bytes;
41478      return hexDump(bytes);
41479    };
41480  
41481    var textRanges = function textRanges(ranges) {
41482      var result = '';
41483      var i = void 0;
41484  
41485      for (i = 0; i < ranges.length; i++) {
41486        result += textRange(ranges, i) + ' ';
41487      }
41488  
41489      return result;
41490    };
41491  
41492    var utils$1 = /*#__PURE__*/Object.freeze({
41493      createTransferableMessage: createTransferableMessage,
41494      initSegmentId: initSegmentId,
41495      segmentKeyId: segmentKeyId,
41496      hexDump: hexDump,
41497      tagDump: tagDump,
41498      textRanges: textRanges
41499    }); // TODO handle fmp4 case where the timing info is accurate and doesn't involve transmux
41500    // Add 25% to the segment duration to account for small discrepencies in segment timing.
41501    // 25% was arbitrarily chosen, and may need to be refined over time.
41502  
41503    var SEGMENT_END_FUDGE_PERCENT = 0.25;
41504    /**
41505     * Converts a player time (any time that can be gotten/set from player.currentTime(),
41506     * e.g., any time within player.seekable().start(0) to player.seekable().end(0)) to a
41507     * program time (any time referencing the real world (e.g., EXT-X-PROGRAM-DATE-TIME)).
41508     *
41509     * The containing segment is required as the EXT-X-PROGRAM-DATE-TIME serves as an "anchor
41510     * point" (a point where we have a mapping from program time to player time, with player
41511     * time being the post transmux start of the segment).
41512     *
41513     * For more details, see [this doc](../../docs/program-time-from-player-time.md).
41514     *
41515     * @param {Number} playerTime the player time
41516     * @param {Object} segment the segment which contains the player time
41517     * @return {Date} program time
41518     */
41519  
41520    var playerTimeToProgramTime = function playerTimeToProgramTime(playerTime, segment) {
41521      if (!segment.dateTimeObject) {
41522        // Can't convert without an "anchor point" for the program time (i.e., a time that can
41523        // be used to map the start of a segment with a real world time).
41524        return null;
41525      }
41526  
41527      var transmuxerPrependedSeconds = segment.videoTimingInfo.transmuxerPrependedSeconds;
41528      var transmuxedStart = segment.videoTimingInfo.transmuxedPresentationStart; // get the start of the content from before old content is prepended
41529  
41530      var startOfSegment = transmuxedStart + transmuxerPrependedSeconds;
41531      var offsetFromSegmentStart = playerTime - startOfSegment;
41532      return new Date(segment.dateTimeObject.getTime() + offsetFromSegmentStart * 1000);
41533    };
41534  
41535    var originalSegmentVideoDuration = function originalSegmentVideoDuration(videoTimingInfo) {
41536      return videoTimingInfo.transmuxedPresentationEnd - videoTimingInfo.transmuxedPresentationStart - videoTimingInfo.transmuxerPrependedSeconds;
41537    };
41538    /**
41539     * Finds a segment that contains the time requested given as an ISO-8601 string. The
41540     * returned segment might be an estimate or an accurate match.
41541     *
41542     * @param {String} programTime The ISO-8601 programTime to find a match for
41543     * @param {Object} playlist A playlist object to search within
41544     */
41545  
41546  
41547    var findSegmentForProgramTime = function findSegmentForProgramTime(programTime, playlist) {
41548      // Assumptions:
41549      //  - verifyProgramDateTimeTags has already been run
41550      //  - live streams have been started
41551      var dateTimeObject = void 0;
41552  
41553      try {
41554        dateTimeObject = new Date(programTime);
41555      } catch (e) {
41556        return null;
41557      }
41558  
41559      if (!playlist || !playlist.segments || playlist.segments.length === 0) {
41560        return null;
41561      }
41562  
41563      var segment = playlist.segments[0];
41564  
41565      if (dateTimeObject < segment.dateTimeObject) {
41566        // Requested time is before stream start.
41567        return null;
41568      }
41569  
41570      for (var i = 0; i < playlist.segments.length - 1; i++) {
41571        segment = playlist.segments[i];
41572        var nextSegmentStart = playlist.segments[i + 1].dateTimeObject;
41573  
41574        if (dateTimeObject < nextSegmentStart) {
41575          break;
41576        }
41577      }
41578  
41579      var lastSegment = playlist.segments[playlist.segments.length - 1];
41580      var lastSegmentStart = lastSegment.dateTimeObject;
41581      var lastSegmentDuration = lastSegment.videoTimingInfo ? originalSegmentVideoDuration(lastSegment.videoTimingInfo) : lastSegment.duration + lastSegment.duration * SEGMENT_END_FUDGE_PERCENT;
41582      var lastSegmentEnd = new Date(lastSegmentStart.getTime() + lastSegmentDuration * 1000);
41583  
41584      if (dateTimeObject > lastSegmentEnd) {
41585        // Beyond the end of the stream, or our best guess of the end of the stream.
41586        return null;
41587      }
41588  
41589      if (dateTimeObject > lastSegmentStart) {
41590        segment = lastSegment;
41591      }
41592  
41593      return {
41594        segment: segment,
41595        estimatedStart: segment.videoTimingInfo ? segment.videoTimingInfo.transmuxedPresentationStart : Playlist.duration(playlist, playlist.mediaSequence + playlist.segments.indexOf(segment)),
41596        // Although, given that all segments have accurate date time objects, the segment
41597        // selected should be accurate, unless the video has been transmuxed at some point
41598        // (determined by the presence of the videoTimingInfo object), the segment's "player
41599        // time" (the start time in the player) can't be considered accurate.
41600        type: segment.videoTimingInfo ? 'accurate' : 'estimate'
41601      };
41602    };
41603    /**
41604     * Finds a segment that contains the given player time(in seconds).
41605     *
41606     * @param {Number} time The player time to find a match for
41607     * @param {Object} playlist A playlist object to search within
41608     */
41609  
41610  
41611    var findSegmentForPlayerTime = function findSegmentForPlayerTime(time, playlist) {
41612      // Assumptions:
41613      // - there will always be a segment.duration
41614      // - we can start from zero
41615      // - segments are in time order
41616      if (!playlist || !playlist.segments || playlist.segments.length === 0) {
41617        return null;
41618      }
41619  
41620      var segmentEnd = 0;
41621      var segment = void 0;
41622  
41623      for (var i = 0; i < playlist.segments.length; i++) {
41624        segment = playlist.segments[i]; // videoTimingInfo is set after the segment is downloaded and transmuxed, and
41625        // should contain the most accurate values we have for the segment's player times.
41626        //
41627        // Use the accurate transmuxedPresentationEnd value if it is available, otherwise fall
41628        // back to an estimate based on the manifest derived (inaccurate) segment.duration, to
41629        // calculate an end value.
41630  
41631        segmentEnd = segment.videoTimingInfo ? segment.videoTimingInfo.transmuxedPresentationEnd : segmentEnd + segment.duration;
41632  
41633        if (time <= segmentEnd) {
41634          break;
41635        }
41636      }
41637  
41638      var lastSegment = playlist.segments[playlist.segments.length - 1];
41639  
41640      if (lastSegment.videoTimingInfo && lastSegment.videoTimingInfo.transmuxedPresentationEnd < time) {
41641        // The time requested is beyond the stream end.
41642        return null;
41643      }
41644  
41645      if (time > segmentEnd) {
41646        // The time is within or beyond the last segment.
41647        //
41648        // Check to see if the time is beyond a reasonable guess of the end of the stream.
41649        if (time > segmentEnd + lastSegment.duration * SEGMENT_END_FUDGE_PERCENT) {
41650          // Technically, because the duration value is only an estimate, the time may still
41651          // exist in the last segment, however, there isn't enough information to make even
41652          // a reasonable estimate.
41653          return null;
41654        }
41655  
41656        segment = lastSegment;
41657      }
41658  
41659      return {
41660        segment: segment,
41661        estimatedStart: segment.videoTimingInfo ? segment.videoTimingInfo.transmuxedPresentationStart : segmentEnd - segment.duration,
41662        // Because videoTimingInfo is only set after transmux, it is the only way to get
41663        // accurate timing values.
41664        type: segment.videoTimingInfo ? 'accurate' : 'estimate'
41665      };
41666    };
41667    /**
41668     * Gives the offset of the comparisonTimestamp from the programTime timestamp in seconds.
41669     * If the offset returned is positive, the programTime occurs after the
41670     * comparisonTimestamp.
41671     * If the offset is negative, the programTime occurs before the comparisonTimestamp.
41672     *
41673     * @param {String} comparisonTimeStamp An ISO-8601 timestamp to compare against
41674     * @param {String} programTime The programTime as an ISO-8601 string
41675     * @return {Number} offset
41676     */
41677  
41678  
41679    var getOffsetFromTimestamp = function getOffsetFromTimestamp(comparisonTimeStamp, programTime) {
41680      var segmentDateTime = void 0;
41681      var programDateTime = void 0;
41682  
41683      try {
41684        segmentDateTime = new Date(comparisonTimeStamp);
41685        programDateTime = new Date(programTime);
41686      } catch (e) {// TODO handle error
41687      }
41688  
41689      var segmentTimeEpoch = segmentDateTime.getTime();
41690      var programTimeEpoch = programDateTime.getTime();
41691      return (programTimeEpoch - segmentTimeEpoch) / 1000;
41692    };
41693    /**
41694     * Checks that all segments in this playlist have programDateTime tags.
41695     *
41696     * @param {Object} playlist A playlist object
41697     */
41698  
41699  
41700    var verifyProgramDateTimeTags = function verifyProgramDateTimeTags(playlist) {
41701      if (!playlist.segments || playlist.segments.length === 0) {
41702        return false;
41703      }
41704  
41705      for (var i = 0; i < playlist.segments.length; i++) {
41706        var segment = playlist.segments[i];
41707  
41708        if (!segment.dateTimeObject) {
41709          return false;
41710        }
41711      }
41712  
41713      return true;
41714    };
41715    /**
41716     * Returns the programTime of the media given a playlist and a playerTime.
41717     * The playlist must have programDateTime tags for a programDateTime tag to be returned.
41718     * If the segments containing the time requested have not been buffered yet, an estimate
41719     * may be returned to the callback.
41720     *
41721     * @param {Object} args
41722     * @param {Object} args.playlist A playlist object to search within
41723     * @param {Number} time A playerTime in seconds
41724     * @param {Function} callback(err, programTime)
41725     * @returns {String} err.message A detailed error message
41726     * @returns {Object} programTime
41727     * @returns {Number} programTime.mediaSeconds The streamTime in seconds
41728     * @returns {String} programTime.programDateTime The programTime as an ISO-8601 String
41729     */
41730  
41731  
41732    var getProgramTime = function getProgramTime(_ref) {
41733      var playlist = _ref.playlist,
41734          _ref$time = _ref.time,
41735          time = _ref$time === undefined ? undefined : _ref$time,
41736          callback = _ref.callback;
41737  
41738      if (!callback) {
41739        throw new Error('getProgramTime: callback must be provided');
41740      }
41741  
41742      if (!playlist || time === undefined) {
41743        return callback({
41744          message: 'getProgramTime: playlist and time must be provided'
41745        });
41746      }
41747  
41748      var matchedSegment = findSegmentForPlayerTime(time, playlist);
41749  
41750      if (!matchedSegment) {
41751        return callback({
41752          message: 'valid programTime was not found'
41753        });
41754      }
41755  
41756      if (matchedSegment.type === 'estimate') {
41757        return callback({
41758          message: 'Accurate programTime could not be determined.' + ' Please seek to e.seekTime and try again',
41759          seekTime: matchedSegment.estimatedStart
41760        });
41761      }
41762  
41763      var programTimeObject = {
41764        mediaSeconds: time
41765      };
41766      var programTime = playerTimeToProgramTime(time, matchedSegment.segment);
41767  
41768      if (programTime) {
41769        programTimeObject.programDateTime = programTime.toISOString();
41770      }
41771  
41772      return callback(null, programTimeObject);
41773    };
41774    /**
41775     * Seeks in the player to a time that matches the given programTime ISO-8601 string.
41776     *
41777     * @param {Object} args
41778     * @param {String} args.programTime A programTime to seek to as an ISO-8601 String
41779     * @param {Object} args.playlist A playlist to look within
41780     * @param {Number} args.retryCount The number of times to try for an accurate seek. Default is 2.
41781     * @param {Function} args.seekTo A method to perform a seek
41782     * @param {Boolean} args.pauseAfterSeek Whether to end in a paused state after seeking. Default is true.
41783     * @param {Object} args.tech The tech to seek on
41784     * @param {Function} args.callback(err, newTime) A callback to return the new time to
41785     * @returns {String} err.message A detailed error message
41786     * @returns {Number} newTime The exact time that was seeked to in seconds
41787     */
41788  
41789  
41790    var seekToProgramTime = function seekToProgramTime(_ref2) {
41791      var programTime = _ref2.programTime,
41792          playlist = _ref2.playlist,
41793          _ref2$retryCount = _ref2.retryCount,
41794          retryCount = _ref2$retryCount === undefined ? 2 : _ref2$retryCount,
41795          seekTo = _ref2.seekTo,
41796          _ref2$pauseAfterSeek = _ref2.pauseAfterSeek,
41797          pauseAfterSeek = _ref2$pauseAfterSeek === undefined ? true : _ref2$pauseAfterSeek,
41798          tech = _ref2.tech,
41799          callback = _ref2.callback;
41800  
41801      if (!callback) {
41802        throw new Error('seekToProgramTime: callback must be provided');
41803      }
41804  
41805      if (typeof programTime === 'undefined' || !playlist || !seekTo) {
41806        return callback({
41807          message: 'seekToProgramTime: programTime, seekTo and playlist must be provided'
41808        });
41809      }
41810  
41811      if (!playlist.endList && !tech.hasStarted_) {
41812        return callback({
41813          message: 'player must be playing a live stream to start buffering'
41814        });
41815      }
41816  
41817      if (!verifyProgramDateTimeTags(playlist)) {
41818        return callback({
41819          message: 'programDateTime tags must be provided in the manifest ' + playlist.resolvedUri
41820        });
41821      }
41822  
41823      var matchedSegment = findSegmentForProgramTime(programTime, playlist); // no match
41824  
41825      if (!matchedSegment) {
41826        return callback({
41827          message: programTime + ' was not found in the stream'
41828        });
41829      }
41830  
41831      var segment = matchedSegment.segment;
41832      var mediaOffset = getOffsetFromTimestamp(segment.dateTimeObject, programTime);
41833  
41834      if (matchedSegment.type === 'estimate') {
41835        // we've run out of retries
41836        if (retryCount === 0) {
41837          return callback({
41838            message: programTime + ' is not buffered yet. Try again'
41839          });
41840        }
41841  
41842        seekTo(matchedSegment.estimatedStart + mediaOffset);
41843        tech.one('seeked', function () {
41844          seekToProgramTime({
41845            programTime: programTime,
41846            playlist: playlist,
41847            retryCount: retryCount - 1,
41848            seekTo: seekTo,
41849            pauseAfterSeek: pauseAfterSeek,
41850            tech: tech,
41851            callback: callback
41852          });
41853        });
41854        return;
41855      } // Since the segment.start value is determined from the buffered end or ending time
41856      // of the prior segment, the seekToTime doesn't need to account for any transmuxer
41857      // modifications.
41858  
41859  
41860      var seekToTime = segment.start + mediaOffset;
41861  
41862      var seekedCallback = function seekedCallback() {
41863        return callback(null, tech.currentTime());
41864      }; // listen for seeked event
41865  
41866  
41867      tech.one('seeked', seekedCallback); // pause before seeking as video.js will restore this state
41868  
41869      if (pauseAfterSeek) {
41870        tech.pause();
41871      }
41872  
41873      seekTo(seekToTime);
41874    };
41875    /**
41876     * ranges
41877     *
41878     * Utilities for working with TimeRanges.
41879     *
41880     */
41881    // Fudge factor to account for TimeRanges rounding
41882  
41883  
41884    var TIME_FUDGE_FACTOR = 1 / 30; // Comparisons between time values such as current time and the end of the buffered range
41885    // can be misleading because of precision differences or when the current media has poorly
41886    // aligned audio and video, which can cause values to be slightly off from what you would
41887    // expect. This value is what we consider to be safe to use in such comparisons to account
41888    // for these scenarios.
41889  
41890    var SAFE_TIME_DELTA = TIME_FUDGE_FACTOR * 3;
41891  
41892    var filterRanges = function filterRanges(timeRanges, predicate) {
41893      var results = [];
41894      var i = void 0;
41895  
41896      if (timeRanges && timeRanges.length) {
41897        // Search for ranges that match the predicate
41898        for (i = 0; i < timeRanges.length; i++) {
41899          if (predicate(timeRanges.start(i), timeRanges.end(i))) {
41900            results.push([timeRanges.start(i), timeRanges.end(i)]);
41901          }
41902        }
41903      }
41904  
41905      return videojs$1.createTimeRanges(results);
41906    };
41907    /**
41908     * Attempts to find the buffered TimeRange that contains the specified
41909     * time.
41910     * @param {TimeRanges} buffered - the TimeRanges object to query
41911     * @param {number} time  - the time to filter on.
41912     * @returns {TimeRanges} a new TimeRanges object
41913     */
41914  
41915  
41916    var findRange = function findRange(buffered, time) {
41917      return filterRanges(buffered, function (start, end) {
41918        return start - SAFE_TIME_DELTA <= time && end + SAFE_TIME_DELTA >= time;
41919      });
41920    };
41921    /**
41922     * Returns the TimeRanges that begin later than the specified time.
41923     * @param {TimeRanges} timeRanges - the TimeRanges object to query
41924     * @param {number} time - the time to filter on.
41925     * @returns {TimeRanges} a new TimeRanges object.
41926     */
41927  
41928  
41929    var findNextRange = function findNextRange(timeRanges, time) {
41930      return filterRanges(timeRanges, function (start) {
41931        return start - TIME_FUDGE_FACTOR >= time;
41932      });
41933    };
41934    /**
41935     * Returns gaps within a list of TimeRanges
41936     * @param {TimeRanges} buffered - the TimeRanges object
41937     * @return {TimeRanges} a TimeRanges object of gaps
41938     */
41939  
41940  
41941    var findGaps = function findGaps(buffered) {
41942      if (buffered.length < 2) {
41943        return videojs$1.createTimeRanges();
41944      }
41945  
41946      var ranges = [];
41947  
41948      for (var i = 1; i < buffered.length; i++) {
41949        var start = buffered.end(i - 1);
41950        var end = buffered.start(i);
41951        ranges.push([start, end]);
41952      }
41953  
41954      return videojs$1.createTimeRanges(ranges);
41955    };
41956    /**
41957     * Gets a human readable string for a TimeRange
41958     *
41959     * @param {TimeRange} range
41960     * @returns {String} a human readable string
41961     */
41962  
41963  
41964    var printableRange = function printableRange(range) {
41965      var strArr = [];
41966  
41967      if (!range || !range.length) {
41968        return '';
41969      }
41970  
41971      for (var i = 0; i < range.length; i++) {
41972        strArr.push(range.start(i) + ' => ' + range.end(i));
41973      }
41974  
41975      return strArr.join(', ');
41976    };
41977    /**
41978     * Calculates the amount of time left in seconds until the player hits the end of the
41979     * buffer and causes a rebuffer
41980     *
41981     * @param {TimeRange} buffered
41982     *        The state of the buffer
41983     * @param {Numnber} currentTime
41984     *        The current time of the player
41985     * @param {Number} playbackRate
41986     *        The current playback rate of the player. Defaults to 1.
41987     * @return {Number}
41988     *         Time until the player has to start rebuffering in seconds.
41989     * @function timeUntilRebuffer
41990     */
41991  
41992  
41993    var timeUntilRebuffer = function timeUntilRebuffer(buffered, currentTime) {
41994      var playbackRate = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 1;
41995      var bufferedEnd = buffered.length ? buffered.end(buffered.length - 1) : 0;
41996      return (bufferedEnd - currentTime) / playbackRate;
41997    };
41998    /**
41999     * Converts a TimeRanges object into an array representation
42000     * @param {TimeRanges} timeRanges
42001     * @returns {Array}
42002     */
42003  
42004  
42005    var timeRangesToArray = function timeRangesToArray(timeRanges) {
42006      var timeRangesList = [];
42007  
42008      for (var i = 0; i < timeRanges.length; i++) {
42009        timeRangesList.push({
42010          start: timeRanges.start(i),
42011          end: timeRanges.end(i)
42012        });
42013      }
42014  
42015      return timeRangesList;
42016    };
42017    /**
42018     * @file create-text-tracks-if-necessary.js
42019     */
42020  
42021    /**
42022     * Create text tracks on video.js if they exist on a segment.
42023     *
42024     * @param {Object} sourceBuffer the VSB or FSB
42025     * @param {Object}
vendor: 17,660 bytes, lines 42025-42554
42025 mediaSource the HTML media source
42026     * @param {Object} segment the segment that may contain the text track
42027     * @private
42028     */
42029  
42030  
42031    var createTextTracksIfNecessary = function createTextTracksIfNecessary(sourceBuffer, mediaSource, segment) {
42032      var player = mediaSource.player_; // create an in-band caption track if one is present in the segment
42033  
42034      if (segment.captions && segment.captions.length) {
42035        if (!sourceBuffer.inbandTextTracks_) {
42036          sourceBuffer.inbandTextTracks_ = {};
42037        }
42038  
42039        for (var trackId in segment.captionStreams) {
42040          if (!sourceBuffer.inbandTextTracks_[trackId]) {
42041            player.tech_.trigger({
42042              type: 'usage',
42043              name: 'hls-608'
42044            });
42045            var track = player.textTracks().getTrackById(trackId);
42046  
42047            if (track) {
42048              // Resuse an existing track with a CC# id because this was
42049              // very likely created by videojs-contrib-hls from information
42050              // in the m3u8 for us to use
42051              sourceBuffer.inbandTextTracks_[trackId] = track;
42052            } else {
42053              // Otherwise, create a track with the default `CC#` label and
42054              // without a language
42055              sourceBuffer.inbandTextTracks_[trackId] = player.addRemoteTextTrack({
42056                kind: 'captions',
42057                id: trackId,
42058                label: trackId
42059              }, false).track;
42060            }
42061          }
42062        }
42063      }
42064  
42065      if (segment.metadata && segment.metadata.length && !sourceBuffer.metadataTrack_) {
42066        sourceBuffer.metadataTrack_ = player.addRemoteTextTrack({
42067          kind: 'metadata',
42068          label: 'Timed Metadata'
42069        }, false).track;
42070        sourceBuffer.metadataTrack_.inBandMetadataTrackDispatchType = segment.metadata.dispatchType;
42071      }
42072    };
42073    /**
42074     * @file remove-cues-from-track.js
42075     */
42076  
42077    /**
42078     * Remove cues from a track on video.js.
42079     *
42080     * @param {Double} start start of where we should remove the cue
42081     * @param {Double} end end of where the we should remove the cue
42082     * @param {Object} track the text track to remove the cues from
42083     * @private
42084     */
42085  
42086  
42087    var removeCuesFromTrack = function removeCuesFromTrack(start, end, track) {
42088      var i = void 0;
42089      var cue = void 0;
42090  
42091      if (!track) {
42092        return;
42093      }
42094  
42095      if (!track.cues) {
42096        return;
42097      }
42098  
42099      i = track.cues.length;
42100  
42101      while (i--) {
42102        cue = track.cues[i]; // Remove any overlapping cue
42103  
42104        if (cue.startTime <= end && cue.endTime >= start) {
42105          track.removeCue(cue);
42106        }
42107      }
42108    };
42109    /**
42110     * @file add-text-track-data.js
42111     */
42112  
42113    /**
42114     * Define properties on a cue for backwards compatability,
42115     * but warn the user that the way that they are using it
42116     * is depricated and will be removed at a later date.
42117     *
42118     * @param {Cue} cue the cue to add the properties on
42119     * @private
42120     */
42121  
42122  
42123    var deprecateOldCue = function deprecateOldCue(cue) {
42124      Object.defineProperties(cue.frame, {
42125        id: {
42126          get: function get() {
42127            videojs$1.log.warn('cue.frame.id is deprecated. Use cue.value.key instead.');
42128            return cue.value.key;
42129          }
42130        },
42131        value: {
42132          get: function get() {
42133            videojs$1.log.warn('cue.frame.value is deprecated. Use cue.value.data instead.');
42134            return cue.value.data;
42135          }
42136        },
42137        privateData: {
42138          get: function get() {
42139            videojs$1.log.warn('cue.frame.privateData is deprecated. Use cue.value.data instead.');
42140            return cue.value.data;
42141          }
42142        }
42143      });
42144    };
42145  
42146    var durationOfVideo = function durationOfVideo(duration) {
42147      var dur = void 0;
42148  
42149      if (isNaN(duration) || Math.abs(duration) === Infinity) {
42150        dur = Number.MAX_VALUE;
42151      } else {
42152        dur = duration;
42153      }
42154  
42155      return dur;
42156    };
42157    /**
42158     * Add text track data to a source handler given the captions and
42159     * metadata from the buffer.
42160     *
42161     * @param {Object} sourceHandler the virtual source buffer
42162     * @param {Array} captionArray an array of caption data
42163     * @param {Array} metadataArray an array of meta data
42164     * @private
42165     */
42166  
42167  
42168    var addTextTrackData = function addTextTrackData(sourceHandler, captionArray, metadataArray) {
42169      var Cue = window$3.WebKitDataCue || window$3.VTTCue;
42170  
42171      if (captionArray) {
42172        captionArray.forEach(function (caption) {
42173          var track = caption.stream;
42174          this.inbandTextTracks_[track].addCue(new Cue(caption.startTime + this.timestampOffset, caption.endTime + this.timestampOffset, caption.text));
42175        }, sourceHandler);
42176      }
42177  
42178      if (metadataArray) {
42179        var videoDuration = durationOfVideo(sourceHandler.mediaSource_.duration);
42180        metadataArray.forEach(function (metadata) {
42181          var time = metadata.cueTime + this.timestampOffset; // if time isn't a finite number between 0 and Infinity, like NaN,
42182          // ignore this bit of metadata.
42183          // This likely occurs when you have an non-timed ID3 tag like TIT2,
42184          // which is the "Title/Songname/Content description" frame
42185  
42186          if (typeof time !== 'number' || window$3.isNaN(time) || time < 0 || !(time < Infinity)) {
42187            return;
42188          }
42189  
42190          metadata.frames.forEach(function (frame) {
42191            var cue = new Cue(time, time, frame.value || frame.url || frame.data || '');
42192            cue.frame = frame;
42193            cue.value = frame;
42194            deprecateOldCue(cue);
42195            this.metadataTrack_.addCue(cue);
42196          }, this);
42197        }, sourceHandler); // Updating the metadeta cues so that
42198        // the endTime of each cue is the startTime of the next cue
42199        // the endTime of last cue is the duration of the video
42200  
42201        if (sourceHandler.metadataTrack_ && sourceHandler.metadataTrack_.cues && sourceHandler.metadataTrack_.cues.length) {
42202          var cues = sourceHandler.metadataTrack_.cues;
42203          var cuesArray = []; // Create a copy of the TextTrackCueList...
42204          // ...disregarding cues with a falsey value
42205  
42206          for (var i = 0; i < cues.length; i++) {
42207            if (cues[i]) {
42208              cuesArray.push(cues[i]);
42209            }
42210          } // Group cues by their startTime value
42211  
42212  
42213          var cuesGroupedByStartTime = cuesArray.reduce(function (obj, cue) {
42214            var timeSlot = obj[cue.startTime] || [];
42215            timeSlot.push(cue);
42216            obj[cue.startTime] = timeSlot;
42217            return obj;
42218          }, {}); // Sort startTimes by ascending order
42219  
42220          var sortedStartTimes = Object.keys(cuesGroupedByStartTime).sort(function (a, b) {
42221            return Number(a) - Number(b);
42222          }); // Map each cue group's endTime to the next group's startTime
42223  
42224          sortedStartTimes.forEach(function (startTime, idx) {
42225            var cueGroup = cuesGroupedByStartTime[startTime];
42226            var nextTime = Number(sortedStartTimes[idx + 1]) || videoDuration; // Map each cue's endTime the next group's startTime
42227  
42228            cueGroup.forEach(function (cue) {
42229              cue.endTime = nextTime;
42230            });
42231          });
42232        }
42233      }
42234    };
42235  
42236    var win = typeof window !== 'undefined' ? window : {},
42237        TARGET = typeof Symbol === 'undefined' ? '__target' : Symbol(),
42238        SCRIPT_TYPE = 'application/javascript',
42239        BlobBuilder = win.BlobBuilder || win.WebKitBlobBuilder || win.MozBlobBuilder || win.MSBlobBuilder,
42240        URL = win.URL || win.webkitURL || URL && URL.msURL,
42241        Worker = win.Worker;
42242    /**
42243     * Returns a wrapper around Web Worker code that is constructible.
42244     *
42245     * @function shimWorker
42246     *
42247     * @param { String }    filename    The name of the file
42248     * @param { Function }  fn          Function wrapping the code of the worker
42249     */
42250  
42251    function shimWorker(filename, fn) {
42252      return function ShimWorker(forceFallback) {
42253        var o = this;
42254  
42255        if (!fn) {
42256          return new Worker(filename);
42257        } else if (Worker && !forceFallback) {
42258          // Convert the function's inner code to a string to construct the worker
42259          var source = fn.toString().replace(/^function.+?{/, '').slice(0, -1),
42260              objURL = createSourceObject(source);
42261          this[TARGET] = new Worker(objURL);
42262          wrapTerminate(this[TARGET], objURL);
42263          return this[TARGET];
42264        } else {
42265          var selfShim = {
42266            postMessage: function postMessage(m) {
42267              if (o.onmessage) {
42268                setTimeout(function () {
42269                  o.onmessage({
42270                    data: m,
42271                    target: selfShim
42272                  });
42273                });
42274              }
42275            }
42276          };
42277          fn.call(selfShim);
42278  
42279          this.postMessage = function (m) {
42280            setTimeout(function () {
42281              selfShim.onmessage({
42282                data: m,
42283                target: o
42284              });
42285            });
42286          };
42287  
42288          this.isThisThread = true;
42289        }
42290      };
42291    } // Test Worker capabilities
42292  
42293  
42294    if (Worker) {
42295      var testWorker,
42296          objURL = createSourceObject('self.onmessage = function () {}'),
42297          testArray = new Uint8Array(1);
42298  
42299      try {
42300        testWorker = new Worker(objURL); // Native browser on some Samsung devices throws for transferables, let's detect it
42301  
42302        testWorker.postMessage(testArray, [testArray.buffer]);
42303      } catch (e) {
42304        Worker = null;
42305      } finally {
42306        URL.revokeObjectURL(objURL);
42307  
42308        if (testWorker) {
42309          testWorker.terminate();
42310        }
42311      }
42312    }
42313  
42314    function createSourceObject(str) {
42315      try {
42316        return URL.createObjectURL(new Blob([str], {
42317          type: SCRIPT_TYPE
42318        }));
42319      } catch (e) {
42320        var blob = new BlobBuilder();
42321        blob.append(str);
42322        return URL.createObjectURL(blob.getBlob(type));
42323      }
42324    }
42325  
42326    function wrapTerminate(worker, objURL) {
42327      if (!worker || !objURL) return;
42328      var term = worker.terminate;
42329      worker.objURL = objURL;
42330  
42331      worker.terminate = function () {
42332        if (worker.objURL) URL.revokeObjectURL(worker.objURL);
42333        term.call(worker);
42334      };
42335    }
42336  
42337    var TransmuxWorker = new shimWorker("./transmuxer-worker.worker.js", function (window, document$$1) {
42338      var self = this;
42339  
42340      var transmuxerWorker = function () {
42341        /**
42342         * mux.js
42343         *
42344         * Copyright (c) Brightcove
42345         * Licensed Apache-2.0 https://github.com/videojs/mux.js/blob/master/LICENSE
42346         *
42347         * A lightweight readable stream implemention that handles event dispatching.
42348         * Objects that inherit from streams should call init in their constructors.
42349         */
42350        var Stream = function Stream() {
42351          this.init = function () {
42352            var listeners = {};
42353            /**
42354             * Add a listener for a specified event type.
42355             * @param type {string} the event name
42356             * @param listener {function} the callback to be invoked when an event of
42357             * the specified type occurs
42358             */
42359  
42360            this.on = function (type, listener) {
42361              if (!listeners[type]) {
42362                listeners[type] = [];
42363              }
42364  
42365              listeners[type] = listeners[type].concat(listener);
42366            };
42367            /**
42368             * Remove a listener for a specified event type.
42369             * @param type {string} the event name
42370             * @param listener {function} a function previously registered for this
42371             * type of event through `on`
42372             */
42373  
42374  
42375            this.off = function (type, listener) {
42376              var index;
42377  
42378              if (!listeners[type]) {
42379                return false;
42380              }
42381  
42382              index = listeners[type].indexOf(listener);
42383              listeners[type] = listeners[type].slice();
42384              listeners[type].splice(index, 1);
42385              return index > -1;
42386            };
42387            /**
42388             * Trigger an event of the specified type on this stream. Any additional
42389             * arguments to this function are passed as parameters to event listeners.
42390             * @param type {string} the event name
42391             */
42392  
42393  
42394            this.trigger = function (type) {
42395              var callbacks, i, length, args;
42396              callbacks = listeners[type];
42397  
42398              if (!callbacks) {
42399                return;
42400              } // Slicing the arguments on every invocation of this method
42401              // can add a significant amount of overhead. Avoid the
42402              // intermediate object creation for the common case of a
42403              // single callback argument
42404  
42405  
42406              if (arguments.length === 2) {
42407                length = callbacks.length;
42408  
42409                for (i = 0; i < length; ++i) {
42410                  callbacks[i].call(this, arguments[1]);
42411                }
42412              } else {
42413                args = [];
42414                i = arguments.length;
42415  
42416                for (i = 1; i < arguments.length; ++i) {
42417                  args.push(arguments[i]);
42418                }
42419  
42420                length = callbacks.length;
42421  
42422                for (i = 0; i < length; ++i) {
42423                  callbacks[i].apply(this, args);
42424                }
42425              }
42426            };
42427            /**
42428             * Destroys the stream and cleans up.
42429             */
42430  
42431  
42432            this.dispose = function () {
42433              listeners = {};
42434            };
42435          };
42436        };
42437        /**
42438         * Forwards all `data` events on this stream to the destination stream. The
42439         * destination stream should provide a method `push` to receive the data
42440         * events as they arrive.
42441         * @param destination {stream} the stream that will receive all `data` events
42442         * @param autoFlush {boolean} if false, we will not call `flush` on the destination
42443         *                            when the current stream emits a 'done' event
42444         * @see http://nodejs.org/api/stream.html#stream_readable_pipe_destination_options
42445         */
42446  
42447  
42448        Stream.prototype.pipe = function (destination) {
42449          this.on('data', function (data) {
42450            destination.push(data);
42451          });
42452          this.on('done', function (flushSource) {
42453            destination.flush(flushSource);
42454          });
42455          this.on('partialdone', function (flushSource) {
42456            destination.partialFlush(flushSource);
42457          });
42458          this.on('endedtimeline', function (flushSource) {
42459            destination.endTimeline(flushSource);
42460          });
42461          this.on('reset', function (flushSource) {
42462            destination.reset(flushSource);
42463          });
42464          return destination;
42465        }; // Default stream functions that are expected to be overridden to perform
42466        // actual work. These are provided by the prototype as a sort of no-op
42467        // implementation so that we don't have to check for their existence in the
42468        // `pipe` function above.
42469  
42470  
42471        Stream.prototype.push = function (data) {
42472          this.trigger('data', data);
42473        };
42474  
42475        Stream.prototype.flush = function (flushSource) {
42476          this.trigger('done', flushSource);
42477        };
42478  
42479        Stream.prototype.partialFlush = function (flushSource) {
42480          this.trigger('partialdone', flushSource);
42481        };
42482  
42483        Stream.prototype.endTimeline = function (flushSource) {
42484          this.trigger('endedtimeline', flushSource);
42485        };
42486  
42487        Stream.prototype.reset = function (flushSource) {
42488          this.trigger('reset', flushSource);
42489        };
42490  
42491        var stream = Stream;
42492        /**
42493         * mux.js
42494         *
42495         * Copyright (c) Brightcove
42496         * Licensed Apache-2.0 https://github.com/videojs/mux.js/blob/master/LICENSE
42497         *
42498         * Functions that generate fragmented MP4s suitable for use with Media
42499         * Source Extensions.
42500         */
42501  
42502        var UINT32_MAX = Math.pow(2, 32) - 1;
42503        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
42504  
42505        (function () {
42506          var i;
42507          types = {
42508            avc1: [],
42509            // codingname
42510            avcC: [],
42511            btrt: [],
42512            dinf: [],
42513            dref: [],
42514            esds: [],
42515            ftyp: [],
42516            hdlr: [],
42517            mdat: [],
42518            mdhd: [],
42519            mdia: [],
42520            mfhd: [],
42521            minf: [],
42522            moof: [],
42523            moov: [],
42524            mp4a: [],
42525            // codingname
42526            mvex: [],
42527            mvhd: [],
42528            pasp: [],
42529            sdtp: [],
42530            smhd: [],
42531            stbl: [],
42532            stco: [],
42533            stsc: [],
42534            stsd: [],
42535            stsz: [],
42536            stts: [],
42537            styp: [],
42538            tfdt: [],
42539            tfhd: [],
42540            traf: [],
42541            trak: [],
42542            trun: [],
42543            trex: [],
42544            tkhd: [],
42545            vmhd: []
42546          }; // In environments where Uint8Array is undefined (e.g., IE8), skip set up so that we
42547          // don't throw an error
42548  
42549          if (typeof Uint8Array === 'undefined') {
42550            return;
42551          }
42552  
42553          for (i in types) {
42554            if (types.hasOwnProperty(i)) {
vendor: 16,384 bytes, lines 42555-42923
42555              types[i] = [i.charCodeAt(0), i.charCodeAt(1), i.charCodeAt(2), i.charCodeAt(3)];
42556            }
42557          }
42558  
42559          MAJOR_BRAND = new Uint8Array(['i'.charCodeAt(0), 's'.charCodeAt(0), 'o'.charCodeAt(0), 'm'.charCodeAt(0)]);
42560          AVC1_BRAND = new Uint8Array(['a'.charCodeAt(0), 'v'.charCodeAt(0), 'c'.charCodeAt(0), '1'.charCodeAt(0)]);
42561          MINOR_VERSION = new Uint8Array([0, 0, 0, 1]);
42562          VIDEO_HDLR = new Uint8Array([0x00, // version 0
42563          0x00, 0x00, 0x00, // flags
42564          0x00, 0x00, 0x00, 0x00, // pre_defined
42565          0x76, 0x69, 0x64, 0x65, // handler_type: 'vide'
42566          0x00, 0x00, 0x00, 0x00, // reserved
42567          0x00, 0x00, 0x00, 0x00, // reserved
42568          0x00, 0x00, 0x00, 0x00, // reserved
42569          0x56, 0x69, 0x64, 0x65, 0x6f, 0x48, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x72, 0x00 // name: 'VideoHandler'
42570          ]);
42571          AUDIO_HDLR = new Uint8Array([0x00, // version 0
42572          0x00, 0x00, 0x00, // flags
42573          0x00, 0x00, 0x00, 0x00, // pre_defined
42574          0x73, 0x6f, 0x75, 0x6e, // handler_type: 'soun'
42575          0x00, 0x00, 0x00, 0x00, // reserved
42576          0x00, 0x00, 0x00, 0x00, // reserved
42577          0x00, 0x00, 0x00, 0x00, // reserved
42578          0x53, 0x6f, 0x75, 0x6e, 0x64, 0x48, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x72, 0x00 // name: 'SoundHandler'
42579          ]);
42580          HDLR_TYPES = {
42581            video: VIDEO_HDLR,
42582            audio: AUDIO_HDLR
42583          };
42584          DREF = new Uint8Array([0x00, // version 0
42585          0x00, 0x00, 0x00, // flags
42586          0x00, 0x00, 0x00, 0x01, // entry_count
42587          0x00, 0x00, 0x00, 0x0c, // entry_size
42588          0x75, 0x72, 0x6c, 0x20, // 'url' type
42589          0x00, // version 0
42590          0x00, 0x00, 0x01 // entry_flags
42591          ]);
42592          SMHD = new Uint8Array([0x00, // version
42593          0x00, 0x00, 0x00, // flags
42594          0x00, 0x00, // balance, 0 means centered
42595          0x00, 0x00 // reserved
42596          ]);
42597          STCO = new Uint8Array([0x00, // version
42598          0x00, 0x00, 0x00, // flags
42599          0x00, 0x00, 0x00, 0x00 // entry_count
42600          ]);
42601          STSC = STCO;
42602          STSZ = new Uint8Array([0x00, // version
42603          0x00, 0x00, 0x00, // flags
42604          0x00, 0x00, 0x00, 0x00, // sample_size
42605          0x00, 0x00, 0x00, 0x00 // sample_count
42606          ]);
42607          STTS = STCO;
42608          VMHD = new Uint8Array([0x00, // version
42609          0x00, 0x00, 0x01, // flags
42610          0x00, 0x00, // graphicsmode
42611          0x00, 0x00, 0x00, 0x00, 0x00, 0x00 // opcolor
42612          ]);
42613        })();
42614  
42615        box = function box(type) {
42616          var payload = [],
42617              size = 0,
42618              i,
42619              result,
42620              view;
42621  
42622          for (i = 1; i < arguments.length; i++) {
42623            payload.push(arguments[i]);
42624          }
42625  
42626          i = payload.length; // calculate the total size we need to allocate
42627  
42628          while (i--) {
42629            size += payload[i].byteLength;
42630          }
42631  
42632          result = new Uint8Array(size + 8);
42633          view = new DataView(result.buffer, result.byteOffset, result.byteLength);
42634          view.setUint32(0, result.byteLength);
42635          result.set(type, 4); // copy the payload into the result
42636  
42637          for (i = 0, size = 8; i < payload.length; i++) {
42638            result.set(payload[i], size);
42639            size += payload[i].byteLength;
42640          }
42641  
42642          return result;
42643        };
42644  
42645        dinf = function dinf() {
42646          return box(types.dinf, box(types.dref, DREF));
42647        };
42648  
42649        esds = function esds(track) {
42650          return box(types.esds, new Uint8Array([0x00, // version
42651          0x00, 0x00, 0x00, // flags
42652          // ES_Descriptor
42653          0x03, // tag, ES_DescrTag
42654          0x19, // length
42655          0x00, 0x00, // ES_ID
42656          0x00, // streamDependenceFlag, URL_flag, reserved, streamPriority
42657          // DecoderConfigDescriptor
42658          0x04, // tag, DecoderConfigDescrTag
42659          0x11, // length
42660          0x40, // object type
42661          0x15, // streamType
42662          0x00, 0x06, 0x00, // bufferSizeDB
42663          0x00, 0x00, 0xda, 0xc0, // maxBitrate
42664          0x00, 0x00, 0xda, 0xc0, // avgBitrate
42665          // DecoderSpecificInfo
42666          0x05, // tag, DecoderSpecificInfoTag
42667          0x02, // length
42668          // ISO/IEC 14496-3, AudioSpecificConfig
42669          // for samplingFrequencyIndex see ISO/IEC 13818-7:2006, 8.1.3.2.2, Table 35
42670          track.audioobjecttype << 3 | track.samplingfrequencyindex >>> 1, track.samplingfrequencyindex << 7 | track.channelcount << 3, 0x06, 0x01, 0x02 // GASpecificConfig
42671          ]));
42672        };
42673  
42674        ftyp = function ftyp() {
42675          return box(types.ftyp, MAJOR_BRAND, MINOR_VERSION, MAJOR_BRAND, AVC1_BRAND);
42676        };
42677  
42678        hdlr = function hdlr(type) {
42679          return box(types.hdlr, HDLR_TYPES[type]);
42680        };
42681  
42682        mdat = function mdat(data) {
42683          return box(types.mdat, data);
42684        };
42685  
42686        mdhd = function mdhd(track) {
42687          var result = new Uint8Array([0x00, // version 0
42688          0x00, 0x00, 0x00, // flags
42689          0x00, 0x00, 0x00, 0x02, // creation_time
42690          0x00, 0x00, 0x00, 0x03, // modification_time
42691          0x00, 0x01, 0x5f, 0x90, // timescale, 90,000 "ticks" per second
42692          track.duration >>> 24 & 0xFF, track.duration >>> 16 & 0xFF, track.duration >>> 8 & 0xFF, track.duration & 0xFF, // duration
42693          0x55, 0xc4, // 'und' language (undetermined)
42694          0x00, 0x00]); // Use the sample rate from the track metadata, when it is
42695          // defined. The sample rate can be parsed out of an ADTS header, for
42696          // instance.
42697  
42698          if (track.samplerate) {
42699            result[12] = track.samplerate >>> 24 & 0xFF;
42700            result[13] = track.samplerate >>> 16 & 0xFF;
42701            result[14] = track.samplerate >>> 8 & 0xFF;
42702            result[15] = track.samplerate & 0xFF;
42703          }
42704  
42705          return box(types.mdhd, result);
42706        };
42707  
42708        mdia = function mdia(track) {
42709          return box(types.mdia, mdhd(track), hdlr(track.type), minf(track));
42710        };
42711  
42712        mfhd = function mfhd(sequenceNumber) {
42713          return box(types.mfhd, new Uint8Array([0x00, 0x00, 0x00, 0x00, // flags
42714          (sequenceNumber & 0xFF000000) >> 24, (sequenceNumber & 0xFF0000) >> 16, (sequenceNumber & 0xFF00) >> 8, sequenceNumber & 0xFF // sequence_number
42715          ]));
42716        };
42717  
42718        minf = function minf(track) {
42719          return box(types.minf, track.type === 'video' ? box(types.vmhd, VMHD) : box(types.smhd, SMHD), dinf(), stbl(track));
42720        };
42721  
42722        moof = function moof(sequenceNumber, tracks) {
42723          var trackFragments = [],
42724              i = tracks.length; // build traf boxes for each track fragment
42725  
42726          while (i--) {
42727            trackFragments[i] = traf(tracks[i]);
42728          }
42729  
42730          return box.apply(null, [types.moof, mfhd(sequenceNumber)].concat(trackFragments));
42731        };
42732        /**
42733         * Returns a movie box.
42734         * @param tracks {array} the tracks associated with this movie
42735         * @see ISO/IEC 14496-12:2012(E), section 8.2.1
42736         */
42737  
42738  
42739        moov = function moov(tracks) {
42740          var i = tracks.length,
42741              boxes = [];
42742  
42743          while (i--) {
42744            boxes[i] = trak(tracks[i]);
42745          }
42746  
42747          return box.apply(null, [types.moov, mvhd(0xffffffff)].concat(boxes).concat(mvex(tracks)));
42748        };
42749  
42750        mvex = function mvex(tracks) {
42751          var i = tracks.length,
42752              boxes = [];
42753  
42754          while (i--) {
42755            boxes[i] = trex(tracks[i]);
42756          }
42757  
42758          return box.apply(null, [types.mvex].concat(boxes));
42759        };
42760  
42761        mvhd = function mvhd(duration) {
42762          var bytes = new Uint8Array([0x00, // version 0
42763          0x00, 0x00, 0x00, // flags
42764          0x00, 0x00, 0x00, 0x01, // creation_time
42765          0x00, 0x00, 0x00, 0x02, // modification_time
42766          0x00, 0x01, 0x5f, 0x90, // timescale, 90,000 "ticks" per second
42767          (duration & 0xFF000000) >> 24, (duration & 0xFF0000) >> 16, (duration & 0xFF00) >> 8, duration & 0xFF, // duration
42768          0x00, 0x01, 0x00, 0x00, // 1.0 rate
42769          0x01, 0x00, // 1.0 volume
42770          0x00, 0x00, // reserved
42771          0x00, 0x00, 0x00, 0x00, // reserved
42772          0x00, 0x00, 0x00, 0x00, // reserved
42773          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
42774          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
42775          0xff, 0xff, 0xff, 0xff // next_track_ID
42776          ]);
42777          return box(types.mvhd, bytes);
42778        };
42779  
42780        sdtp = function sdtp(track) {
42781          var samples = track.samples || [],
42782              bytes = new Uint8Array(4 + samples.length),
42783              flags,
42784              i; // leave the full box header (4 bytes) all zero
42785          // write the sample table
42786  
42787          for (i = 0; i < samples.length; i++) {
42788            flags = samples[i].flags;
42789            bytes[i + 4] = flags.dependsOn << 4 | flags.isDependedOn << 2 | flags.hasRedundancy;
42790          }
42791  
42792          return box(types.sdtp, bytes);
42793        };
42794  
42795        stbl = function stbl(track) {
42796          return box(types.stbl, stsd(track), box(types.stts, STTS), box(types.stsc, STSC), box(types.stsz, STSZ), box(types.stco, STCO));
42797        };
42798  
42799        (function () {
42800          var videoSample, audioSample;
42801  
42802          stsd = function stsd(track) {
42803            return box(types.stsd, new Uint8Array([0x00, // version 0
42804            0x00, 0x00, 0x00, // flags
42805            0x00, 0x00, 0x00, 0x01]), track.type === 'video' ? videoSample(track) : audioSample(track));
42806          };
42807  
42808          videoSample = function videoSample(track) {
42809            var sps = track.sps || [],
42810                pps = track.pps || [],
42811                sequenceParameterSets = [],
42812                pictureParameterSets = [],
42813                i,
42814                avc1Box; // assemble the SPSs
42815  
42816            for (i = 0; i < sps.length; i++) {
42817              sequenceParameterSets.push((sps[i].byteLength & 0xFF00) >>> 8);
42818              sequenceParameterSets.push(sps[i].byteLength & 0xFF); // sequenceParameterSetLength
42819  
42820              sequenceParameterSets = sequenceParameterSets.concat(Array.prototype.slice.call(sps[i])); // SPS
42821            } // assemble the PPSs
42822  
42823  
42824            for (i = 0; i < pps.length; i++) {
42825              pictureParameterSets.push((pps[i].byteLength & 0xFF00) >>> 8);
42826              pictureParameterSets.push(pps[i].byteLength & 0xFF);
42827              pictureParameterSets = pictureParameterSets.concat(Array.prototype.slice.call(pps[i]));
42828            }
42829  
42830            avc1Box = [types.avc1, new Uint8Array([0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // reserved
42831            0x00, 0x01, // data_reference_index
42832            0x00, 0x00, // pre_defined
42833            0x00, 0x00, // reserved
42834            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // pre_defined
42835            (track.width & 0xff00) >> 8, track.width & 0xff, // width
42836            (track.height & 0xff00) >> 8, track.height & 0xff, // height
42837            0x00, 0x48, 0x00, 0x00, // horizresolution
42838            0x00, 0x48, 0x00, 0x00, // vertresolution
42839            0x00, 0x00, 0x00, 0x00, // reserved
42840            0x00, 0x01, // frame_count
42841            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
42842            0x00, 0x18, // depth = 24
42843            0x11, 0x11 // pre_defined = -1
42844            ]), box(types.avcC, new Uint8Array([0x01, // configurationVersion
42845            track.profileIdc, // AVCProfileIndication
42846            track.profileCompatibility, // profile_compatibility
42847            track.levelIdc, // AVCLevelIndication
42848            0xff // lengthSizeMinusOne, hard-coded to 4 bytes
42849            ].concat([sps.length], // numOfSequenceParameterSets
42850            sequenceParameterSets, // "SPS"
42851            [pps.length], // numOfPictureParameterSets
42852            pictureParameterSets // "PPS"
42853            ))), box(types.btrt, new Uint8Array([0x00, 0x1c, 0x9c, 0x80, // bufferSizeDB
42854            0x00, 0x2d, 0xc6, 0xc0, // maxBitrate
42855            0x00, 0x2d, 0xc6, 0xc0 // avgBitrate
42856            ]))];
42857  
42858            if (track.sarRatio) {
42859              var hSpacing = track.sarRatio[0],
42860                  vSpacing = track.sarRatio[1];
42861              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])));
42862            }
42863  
42864            return box.apply(null, avc1Box);
42865          };
42866  
42867          audioSample = function audioSample(track) {
42868            return box(types.mp4a, new Uint8Array([// SampleEntry, ISO/IEC 14496-12
42869            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // reserved
42870            0x00, 0x01, // data_reference_index
42871            // AudioSampleEntry, ISO/IEC 14496-12
42872            0x00, 0x00, 0x00, 0x00, // reserved
42873            0x00, 0x00, 0x00, 0x00, // reserved
42874            (track.channelcount & 0xff00) >> 8, track.channelcount & 0xff, // channelcount
42875            (track.samplesize & 0xff00) >> 8, track.samplesize & 0xff, // samplesize
42876            0x00, 0x00, // pre_defined
42877            0x00, 0x00, // reserved
42878            (track.samplerate & 0xff00) >> 8, track.samplerate & 0xff, 0x00, 0x00 // samplerate, 16.16
42879            // MP4AudioSampleEntry, ISO/IEC 14496-14
42880            ]), esds(track));
42881          };
42882        })();
42883  
42884        tkhd = function tkhd(track) {
42885          var result = new Uint8Array([0x00, // version 0
42886          0x00, 0x00, 0x07, // flags
42887          0x00, 0x00, 0x00, 0x00, // creation_time
42888          0x00, 0x00, 0x00, 0x00, // modification_time
42889          (track.id & 0xFF000000) >> 24, (track.id & 0xFF0000) >> 16, (track.id & 0xFF00) >> 8, track.id & 0xFF, // track_ID
42890          0x00, 0x00, 0x00, 0x00, // reserved
42891          (track.duration & 0xFF000000) >> 24, (track.duration & 0xFF0000) >> 16, (track.duration & 0xFF00) >> 8, track.duration & 0xFF, // duration
42892          0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // reserved
42893          0x00, 0x00, // layer
42894          0x00, 0x00, // alternate_group
42895          0x01, 0x00, // non-audio track volume
42896          0x00, 0x00, // reserved
42897          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
42898          (track.width & 0xFF00) >> 8, track.width & 0xFF, 0x00, 0x00, // width
42899          (track.height & 0xFF00) >> 8, track.height & 0xFF, 0x00, 0x00 // height
42900          ]);
42901          return box(types.tkhd, result);
42902        };
42903        /**
42904         * Generate a track fragment (traf) box. A traf box collects metadata
42905         * about tracks in a movie fragment (moof) box.
42906         */
42907  
42908  
42909        traf = function traf(track) {
42910          var trackFragmentHeader, trackFragmentDecodeTime, trackFragmentRun, sampleDependencyTable, dataOffset, upperWordBaseMediaDecodeTime, lowerWordBaseMediaDecodeTime;
42911          trackFragmentHeader = box(types.tfhd, new Uint8Array([0x00, // version 0
42912          0x00, 0x00, 0x3a, // flags
42913          (track.id & 0xFF000000) >> 24, (track.id & 0xFF0000) >> 16, (track.id & 0xFF00) >> 8, track.id & 0xFF, // track_ID
42914          0x00, 0x00, 0x00, 0x01, // sample_description_index
42915          0x00, 0x00, 0x00, 0x00, // default_sample_duration
42916          0x00, 0x00, 0x00, 0x00, // default_sample_size
42917          0x00, 0x00, 0x00, 0x00 // default_sample_flags
42918          ]));
42919          upperWordBaseMediaDecodeTime = Math.floor(track.baseMediaDecodeTime / (UINT32_MAX + 1));
42920          lowerWordBaseMediaDecodeTime = Math.floor(track.baseMediaDecodeTime % (UINT32_MAX + 1));
42921          trackFragmentDecodeTime = box(types.tfdt, new Uint8Array([0x01, // version 1
42922          0x00, 0x00, 0x00, // flags
42923  
vendor: 9,302 bytes, lines 42923-43128
42923        // baseMediaDecodeTime
42924          upperWordBaseMediaDecodeTime >>> 24 & 0xFF, upperWordBaseMediaDecodeTime >>> 16 & 0xFF, upperWordBaseMediaDecodeTime >>> 8 & 0xFF, upperWordBaseMediaDecodeTime & 0xFF, lowerWordBaseMediaDecodeTime >>> 24 & 0xFF, lowerWordBaseMediaDecodeTime >>> 16 & 0xFF, lowerWordBaseMediaDecodeTime >>> 8 & 0xFF, lowerWordBaseMediaDecodeTime & 0xFF])); // the data offset specifies the number of bytes from the start of
42925          // the containing moof to the first payload byte of the associated
42926          // mdat
42927  
42928          dataOffset = 32 + // tfhd
42929          20 + // tfdt
42930          8 + // traf header
42931          16 + // mfhd
42932          8 + // moof header
42933          8; // mdat header
42934          // audio tracks require less metadata
42935  
42936          if (track.type === 'audio') {
42937            trackFragmentRun = trun(track, dataOffset);
42938            return box(types.traf, trackFragmentHeader, trackFragmentDecodeTime, trackFragmentRun);
42939          } // video tracks should contain an independent and disposable samples
42940          // box (sdtp)
42941          // generate one and adjust offsets to match
42942  
42943  
42944          sampleDependencyTable = sdtp(track);
42945          trackFragmentRun = trun(track, sampleDependencyTable.length + dataOffset);
42946          return box(types.traf, trackFragmentHeader, trackFragmentDecodeTime, trackFragmentRun, sampleDependencyTable);
42947        };
42948        /**
42949         * Generate a track box.
42950         * @param track {object} a track definition
42951         * @return {Uint8Array} the track box
42952         */
42953  
42954  
42955        trak = function trak(track) {
42956          track.duration = track.duration || 0xffffffff;
42957          return box(types.trak, tkhd(track), mdia(track));
42958        };
42959  
42960        trex = function trex(track) {
42961          var result = new Uint8Array([0x00, // version 0
42962          0x00, 0x00, 0x00, // flags
42963          (track.id & 0xFF000000) >> 24, (track.id & 0xFF0000) >> 16, (track.id & 0xFF00) >> 8, track.id & 0xFF, // track_ID
42964          0x00, 0x00, 0x00, 0x01, // default_sample_description_index
42965          0x00, 0x00, 0x00, 0x00, // default_sample_duration
42966          0x00, 0x00, 0x00, 0x00, // default_sample_size
42967          0x00, 0x01, 0x00, 0x01 // default_sample_flags
42968          ]); // the last two bytes of default_sample_flags is the sample
42969          // degradation priority, a hint about the importance of this sample
42970          // relative to others. Lower the degradation priority for all sample
42971          // types other than video.
42972  
42973          if (track.type !== 'video') {
42974            result[result.length - 1] = 0x00;
42975          }
42976  
42977          return box(types.trex, result);
42978        };
42979  
42980        (function () {
42981          var audioTrun, videoTrun, trunHeader; // This method assumes all samples are uniform. That is, if a
42982          // duration is present for the first sample, it will be present for
42983          // all subsequent samples.
42984          // see ISO/IEC 14496-12:2012, Section 8.8.8.1
42985  
42986          trunHeader = function trunHeader(samples, offset) {
42987            var durationPresent = 0,
42988                sizePresent = 0,
42989                flagsPresent = 0,
42990                compositionTimeOffset = 0; // trun flag constants
42991  
42992            if (samples.length) {
42993              if (samples[0].duration !== undefined) {
42994                durationPresent = 0x1;
42995              }
42996  
42997              if (samples[0].size !== undefined) {
42998                sizePresent = 0x2;
42999              }
43000  
43001              if (samples[0].flags !== undefined) {
43002                flagsPresent = 0x4;
43003              }
43004  
43005              if (samples[0].compositionTimeOffset !== undefined) {
43006                compositionTimeOffset = 0x8;
43007              }
43008            }
43009  
43010            return [0x00, // version 0
43011            0x00, durationPresent | sizePresent | flagsPresent | compositionTimeOffset, 0x01, // flags
43012            (samples.length & 0xFF000000) >>> 24, (samples.length & 0xFF0000) >>> 16, (samples.length & 0xFF00) >>> 8, samples.length & 0xFF, // sample_count
43013            (offset & 0xFF000000) >>> 24, (offset & 0xFF0000) >>> 16, (offset & 0xFF00) >>> 8, offset & 0xFF // data_offset
43014            ];
43015          };
43016  
43017          videoTrun = function videoTrun(track, offset) {
43018            var bytes, samples, sample, i;
43019            samples = track.samples || [];
43020            offset += 8 + 12 + 16 * samples.length;
43021            bytes = trunHeader(samples, offset);
43022  
43023            for (i = 0; i < samples.length; i++) {
43024              sample = samples[i];
43025              bytes = bytes.concat([(sample.duration & 0xFF000000) >>> 24, (sample.duration & 0xFF0000) >>> 16, (sample.duration & 0xFF00) >>> 8, sample.duration & 0xFF, // sample_duration
43026              (sample.size & 0xFF000000) >>> 24, (sample.size & 0xFF0000) >>> 16, (sample.size & 0xFF00) >>> 8, sample.size & 0xFF, // sample_size
43027              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
43028              (sample.compositionTimeOffset & 0xFF000000) >>> 24, (sample.compositionTimeOffset & 0xFF0000) >>> 16, (sample.compositionTimeOffset & 0xFF00) >>> 8, sample.compositionTimeOffset & 0xFF // sample_composition_time_offset
43029              ]);
43030            }
43031  
43032            return box(types.trun, new Uint8Array(bytes));
43033          };
43034  
43035          audioTrun = function audioTrun(track, offset) {
43036            var bytes, samples, sample, i;
43037            samples = track.samples || [];
43038            offset += 8 + 12 + 8 * samples.length;
43039            bytes = trunHeader(samples, offset);
43040  
43041            for (i = 0; i < samples.length; i++) {
43042              sample = samples[i];
43043              bytes = bytes.concat([(sample.duration & 0xFF000000) >>> 24, (sample.duration & 0xFF0000) >>> 16, (sample.duration & 0xFF00) >>> 8, sample.duration & 0xFF, // sample_duration
43044              (sample.size & 0xFF000000) >>> 24, (sample.size & 0xFF0000) >>> 16, (sample.size & 0xFF00) >>> 8, sample.size & 0xFF]); // sample_size
43045            }
43046  
43047            return box(types.trun, new Uint8Array(bytes));
43048          };
43049  
43050          trun = function trun(track, offset) {
43051            if (track.type === 'audio') {
43052              return audioTrun(track, offset);
43053            }
43054  
43055            return videoTrun(track, offset);
43056          };
43057        })();
43058  
43059        var mp4Generator = {
43060          ftyp: ftyp,
43061          mdat: mdat,
43062          moof: moof,
43063          moov: moov,
43064          initSegment: function initSegment(tracks) {
43065            var fileType = ftyp(),
43066                movie = moov(tracks),
43067                result;
43068            result = new Uint8Array(fileType.byteLength + movie.byteLength);
43069            result.set(fileType);
43070            result.set(movie, fileType.byteLength);
43071            return result;
43072          }
43073        };
43074        /**
43075         * mux.js
43076         *
43077         * Copyright (c) Brightcove
43078         * Licensed Apache-2.0 https://github.com/videojs/mux.js/blob/master/LICENSE
43079         */
43080        // Convert an array of nal units into an array of frames with each frame being
43081        // composed of the nal units that make up that frame
43082        // Also keep track of cummulative data about the frame from the nal units such
43083        // as the frame duration, starting pts, etc.
43084  
43085        var groupNalsIntoFrames = function groupNalsIntoFrames(nalUnits) {
43086          var i,
43087              currentNal,
43088              currentFrame = [],
43089              frames = []; // TODO added for LHLS, make sure this is OK
43090  
43091          frames.byteLength = 0;
43092          frames.nalCount = 0;
43093          frames.duration = 0;
43094          currentFrame.byteLength = 0;
43095  
43096          for (i = 0; i < nalUnits.length; i++) {
43097            currentNal = nalUnits[i]; // Split on 'aud'-type nal units
43098  
43099            if (currentNal.nalUnitType === 'access_unit_delimiter_rbsp') {
43100              // Since the very first nal unit is expected to be an AUD
43101              // only push to the frames array when currentFrame is not empty
43102              if (currentFrame.length) {
43103                currentFrame.duration = currentNal.dts - currentFrame.dts; // TODO added for LHLS, make sure this is OK
43104  
43105                frames.byteLength += currentFrame.byteLength;
43106                frames.nalCount += currentFrame.length;
43107                frames.duration += currentFrame.duration;
43108                frames.push(currentFrame);
43109              }
43110  
43111              currentFrame = [currentNal];
43112              currentFrame.byteLength = currentNal.data.byteLength;
43113              currentFrame.pts = currentNal.pts;
43114              currentFrame.dts = currentNal.dts;
43115            } else {
43116              // Specifically flag key frames for ease of use later
43117              if (currentNal.nalUnitType === 'slice_layer_without_partitioning_rbsp_idr') {
43118                currentFrame.keyFrame = true;
43119              }
43120  
43121              currentFrame.duration = currentNal.dts - currentFrame.dts;
43122              currentFrame.byteLength += currentNal.data.byteLength;
43123              currentFrame.push(currentNal);
43124            }
43125          } // For the last frame, use the duration of the previous frame if we
43126          // have nothing better to go on
43127  
43128  
vendor: 4,818 bytes, lines 43129-43257
43129          if (frames.length && (!currentFrame.duration || currentFrame.duration <= 0)) {
43130            currentFrame.duration = frames[frames.length - 1].duration;
43131          } // Push the final frame
43132          // TODO added for LHLS, make sure this is OK
43133  
43134  
43135          frames.byteLength += currentFrame.byteLength;
43136          frames.nalCount += currentFrame.length;
43137          frames.duration += currentFrame.duration;
43138          frames.push(currentFrame);
43139          return frames;
43140        }; // Convert an array of frames into an array of Gop with each Gop being composed
43141        // of the frames that make up that Gop
43142        // Also keep track of cummulative data about the Gop from the frames such as the
43143        // Gop duration, starting pts, etc.
43144  
43145  
43146        var groupFramesIntoGops = function groupFramesIntoGops(frames) {
43147          var i,
43148              currentFrame,
43149              currentGop = [],
43150              gops = []; // We must pre-set some of the values on the Gop since we
43151          // keep running totals of these values
43152  
43153          currentGop.byteLength = 0;
43154          currentGop.nalCount = 0;
43155          currentGop.duration = 0;
43156          currentGop.pts = frames[0].pts;
43157          currentGop.dts = frames[0].dts; // store some metadata about all the Gops
43158  
43159          gops.byteLength = 0;
43160          gops.nalCount = 0;
43161          gops.duration = 0;
43162          gops.pts = frames[0].pts;
43163          gops.dts = frames[0].dts;
43164  
43165          for (i = 0; i < frames.length; i++) {
43166            currentFrame = frames[i];
43167  
43168            if (currentFrame.keyFrame) {
43169              // Since the very first frame is expected to be an keyframe
43170              // only push to the gops array when currentGop is not empty
43171              if (currentGop.length) {
43172                gops.push(currentGop);
43173                gops.byteLength += currentGop.byteLength;
43174                gops.nalCount += currentGop.nalCount;
43175                gops.duration += currentGop.duration;
43176              }
43177  
43178              currentGop = [currentFrame];
43179              currentGop.nalCount = currentFrame.length;
43180              currentGop.byteLength = currentFrame.byteLength;
43181              currentGop.pts = currentFrame.pts;
43182              currentGop.dts = currentFrame.dts;
43183              currentGop.duration = currentFrame.duration;
43184            } else {
43185              currentGop.duration += currentFrame.duration;
43186              currentGop.nalCount += currentFrame.length;
43187              currentGop.byteLength += currentFrame.byteLength;
43188              currentGop.push(currentFrame);
43189            }
43190          }
43191  
43192          if (gops.length && currentGop.duration <= 0) {
43193            currentGop.duration = gops[gops.length - 1].duration;
43194          }
43195  
43196          gops.byteLength += currentGop.byteLength;
43197          gops.nalCount += currentGop.nalCount;
43198          gops.duration += currentGop.duration; // push the final Gop
43199  
43200          gops.push(currentGop);
43201          return gops;
43202        };
43203        /*
43204         * Search for the first keyframe in the GOPs and throw away all frames
43205         * until that keyframe. Then extend the duration of the pulled keyframe
43206         * and pull the PTS and DTS of the keyframe so that it covers the time
43207         * range of the frames that were disposed.
43208         *
43209         * @param {Array} gops video GOPs
43210         * @returns {Array} modified video GOPs
43211         */
43212  
43213  
43214        var extendFirstKeyFrame = function extendFirstKeyFrame(gops) {
43215          var currentGop;
43216  
43217          if (!gops[0][0].keyFrame && gops.length > 1) {
43218            // Remove the first GOP
43219            currentGop = gops.shift();
43220            gops.byteLength -= currentGop.byteLength;
43221            gops.nalCount -= currentGop.nalCount; // Extend the first frame of what is now the
43222            // first gop to cover the time period of the
43223            // frames we just removed
43224  
43225            gops[0][0].dts = currentGop.dts;
43226            gops[0][0].pts = currentGop.pts;
43227            gops[0][0].duration += currentGop.duration;
43228          }
43229  
43230          return gops;
43231        };
43232        /**
43233         * Default sample object
43234         * see ISO/IEC 14496-12:2012, section 8.6.4.3
43235         */
43236  
43237  
43238        var createDefaultSample = function createDefaultSample() {
43239          return {
43240            size: 0,
43241            flags: {
43242              isLeading: 0,
43243              dependsOn: 1,
43244              isDependedOn: 0,
43245              hasRedundancy: 0,
43246              degradationPriority: 0,
43247              isNonSyncSample: 1
43248            }
43249          };
43250        };
43251        /*
43252         * Collates information from a video frame into an object for eventual
43253         * entry into an MP4 sample table.
43254         *
43255         * @param {Object} frame the video frame
43256         * @param {Number} dataOffset the byte offset to position the sample
43257         * @return {Object}
vendor: 7,934 bytes, lines 43257-43467
43257 object containing sample table info for a frame
43258         */
43259  
43260  
43261        var sampleForFrame = function sampleForFrame(frame, dataOffset) {
43262          var sample = createDefaultSample();
43263          sample.dataOffset = dataOffset;
43264          sample.compositionTimeOffset = frame.pts - frame.dts;
43265          sample.duration = frame.duration;
43266          sample.size = 4 * frame.length; // Space for nal unit size
43267  
43268          sample.size += frame.byteLength;
43269  
43270          if (frame.keyFrame) {
43271            sample.flags.dependsOn = 2;
43272            sample.flags.isNonSyncSample = 0;
43273          }
43274  
43275          return sample;
43276        }; // generate the track's sample table from an array of gops
43277  
43278  
43279        var generateSampleTable = function generateSampleTable(gops, baseDataOffset) {
43280          var h,
43281              i,
43282              sample,
43283              currentGop,
43284              currentFrame,
43285              dataOffset = baseDataOffset || 0,
43286              samples = [];
43287  
43288          for (h = 0; h < gops.length; h++) {
43289            currentGop = gops[h];
43290  
43291            for (i = 0; i < currentGop.length; i++) {
43292              currentFrame = currentGop[i];
43293              sample = sampleForFrame(currentFrame, dataOffset);
43294              dataOffset += sample.size;
43295              samples.push(sample);
43296            }
43297          }
43298  
43299          return samples;
43300        }; // generate the track's raw mdat data from an array of gops
43301  
43302  
43303        var concatenateNalData = function concatenateNalData(gops) {
43304          var h,
43305              i,
43306              j,
43307              currentGop,
43308              currentFrame,
43309              currentNal,
43310              dataOffset = 0,
43311              nalsByteLength = gops.byteLength,
43312              numberOfNals = gops.nalCount,
43313              totalByteLength = nalsByteLength + 4 * numberOfNals,
43314              data = new Uint8Array(totalByteLength),
43315              view = new DataView(data.buffer); // For each Gop..
43316  
43317          for (h = 0; h < gops.length; h++) {
43318            currentGop = gops[h]; // For each Frame..
43319  
43320            for (i = 0; i < currentGop.length; i++) {
43321              currentFrame = currentGop[i]; // For each NAL..
43322  
43323              for (j = 0; j < currentFrame.length; j++) {
43324                currentNal = currentFrame[j];
43325                view.setUint32(dataOffset, currentNal.data.byteLength);
43326                dataOffset += 4;
43327                data.set(currentNal.data, dataOffset);
43328                dataOffset += currentNal.data.byteLength;
43329              }
43330            }
43331          }
43332  
43333          return data;
43334        }; // generate the track's sample table from a frame
43335  
43336  
43337        var generateSampleTableForFrame = function generateSampleTableForFrame(frame, baseDataOffset) {
43338          var sample,
43339              dataOffset = baseDataOffset || 0,
43340              samples = [];
43341          sample = sampleForFrame(frame, dataOffset);
43342          samples.push(sample);
43343          return samples;
43344        }; // generate the track's raw mdat data from a frame
43345  
43346  
43347        var concatenateNalDataForFrame = function concatenateNalDataForFrame(frame) {
43348          var i,
43349              currentNal,
43350              dataOffset = 0,
43351              nalsByteLength = frame.byteLength,
43352              numberOfNals = frame.length,
43353              totalByteLength = nalsByteLength + 4 * numberOfNals,
43354              data = new Uint8Array(totalByteLength),
43355              view = new DataView(data.buffer); // For each NAL..
43356  
43357          for (i = 0; i < frame.length; i++) {
43358            currentNal = frame[i];
43359            view.setUint32(dataOffset, currentNal.data.byteLength);
43360            dataOffset += 4;
43361            data.set(currentNal.data, dataOffset);
43362            dataOffset += currentNal.data.byteLength;
43363          }
43364  
43365          return data;
43366        };
43367  
43368        var frameUtils = {
43369          groupNalsIntoFrames: groupNalsIntoFrames,
43370          groupFramesIntoGops: groupFramesIntoGops,
43371          extendFirstKeyFrame: extendFirstKeyFrame,
43372          generateSampleTable: generateSampleTable,
43373          concatenateNalData: concatenateNalData,
43374          generateSampleTableForFrame: generateSampleTableForFrame,
43375          concatenateNalDataForFrame: concatenateNalDataForFrame
43376        };
43377        /**
43378         * mux.js
43379         *
43380         * Copyright (c) Brightcove
43381         * Licensed Apache-2.0 https://github.com/videojs/mux.js/blob/master/LICENSE
43382         */
43383  
43384        var highPrefix = [33, 16, 5, 32, 164, 27];
43385        var lowPrefix = [33, 65, 108, 84, 1, 2, 4, 8, 168, 2, 4, 8, 17, 191, 252];
43386  
43387        var zeroFill = function zeroFill(count) {
43388          var a = [];
43389  
43390          while (count--) {
43391            a.push(0);
43392          }
43393  
43394          return a;
43395        };
43396  
43397        var makeTable = function makeTable(metaTable) {
43398          return Object.keys(metaTable).reduce(function (obj, key) {
43399            obj[key] = new Uint8Array(metaTable[key].reduce(function (arr, part) {
43400              return arr.concat(part);
43401            }, []));
43402            return obj;
43403          }, {});
43404        }; // Frames-of-silence to use for filling in missing AAC frames
43405  
43406  
43407        var coneOfSilence = {
43408          96000: [highPrefix, [227, 64], zeroFill(154), [56]],
43409          88200: [highPrefix, [231], zeroFill(170), [56]],
43410          64000: [highPrefix, [248, 192], zeroFill(240), [56]],
43411          48000: [highPrefix, [255, 192], zeroFill(268), [55, 148, 128], zeroFill(54), [112]],
43412          44100: [highPrefix, [255, 192], zeroFill(268), [55, 163, 128], zeroFill(84), [112]],
43413          32000: [highPrefix, [255, 192], zeroFill(268), [55, 234], zeroFill(226), [112]],
43414          24000: [highPrefix, [255, 192], zeroFill(268), [55, 255, 128], zeroFill(268), [111, 112], zeroFill(126), [224]],
43415          16000: [highPrefix, [255, 192], zeroFill(268), [55, 255, 128], zeroFill(268), [111, 255], zeroFill(269), [223, 108], zeroFill(195), [1, 192]],
43416          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]],
43417          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]],
43418          8000: [lowPrefix, zeroFill(268), [3, 121, 16], zeroFill(47), [7]]
43419        };
43420        var silence = makeTable(coneOfSilence);
43421        /**
43422         * mux.js
43423         *
43424         * Copyright (c) Brightcove
43425         * Licensed Apache-2.0 https://github.com/videojs/mux.js/blob/master/LICENSE
43426         */
43427  
43428        var ONE_SECOND_IN_TS = 90000,
43429            // 90kHz clock
43430        secondsToVideoTs,
43431            secondsToAudioTs,
43432            videoTsToSeconds,
43433            audioTsToSeconds,
43434            audioTsToVideoTs,
43435            videoTsToAudioTs,
43436            metadataTsToSeconds;
43437  
43438        secondsToVideoTs = function secondsToVideoTs(seconds) {
43439          return seconds * ONE_SECOND_IN_TS;
43440        };
43441  
43442        secondsToAudioTs = function secondsToAudioTs(seconds, sampleRate) {
43443          return seconds * sampleRate;
43444        };
43445  
43446        videoTsToSeconds = function videoTsToSeconds(timestamp) {
43447          return timestamp / ONE_SECOND_IN_TS;
43448        };
43449  
43450        audioTsToSeconds = function audioTsToSeconds(timestamp, sampleRate) {
43451          return timestamp / sampleRate;
43452        };
43453  
43454        audioTsToVideoTs = function audioTsToVideoTs(timestamp, sampleRate) {
43455          return secondsToVideoTs(audioTsToSeconds(timestamp, sampleRate));
43456        };
43457  
43458        videoTsToAudioTs = function videoTsToAudioTs(timestamp, sampleRate) {
43459          return secondsToAudioTs(videoTsToSeconds(timestamp), sampleRate);
43460        };
43461        /**
43462         * Adjust ID3 tag or caption timing information by the timeline pts values
43463         * (if keepOriginalTimestamps is false) and convert to seconds
43464         */
43465  
43466  
43467        metadataTsToSeconds = function metadataTsToSeconds(timestamp, timelineStartPts, 
vendor: 8,925 bytes, lines 43467-43706
43467keepOriginalTimestamps) {
43468          return videoTsToSeconds(keepOriginalTimestamps ? timestamp : timestamp - timelineStartPts);
43469        };
43470  
43471        var clock = {
43472          ONE_SECOND_IN_TS: ONE_SECOND_IN_TS,
43473          secondsToVideoTs: secondsToVideoTs,
43474          secondsToAudioTs: secondsToAudioTs,
43475          videoTsToSeconds: videoTsToSeconds,
43476          audioTsToSeconds: audioTsToSeconds,
43477          audioTsToVideoTs: audioTsToVideoTs,
43478          videoTsToAudioTs: videoTsToAudioTs,
43479          metadataTsToSeconds: metadataTsToSeconds
43480        };
43481        /**
43482         * mux.js
43483         *
43484         * Copyright (c) Brightcove
43485         * Licensed Apache-2.0 https://github.com/videojs/mux.js/blob/master/LICENSE
43486         */
43487  
43488        /**
43489         * Sum the `byteLength` properties of the data in each AAC frame
43490         */
43491  
43492        var sumFrameByteLengths = function sumFrameByteLengths(array) {
43493          var i,
43494              currentObj,
43495              sum = 0; // sum the byteLength's all each nal unit in the frame
43496  
43497          for (i = 0; i < array.length; i++) {
43498            currentObj = array[i];
43499            sum += currentObj.data.byteLength;
43500          }
43501  
43502          return sum;
43503        }; // Possibly pad (prefix) the audio track with silence if appending this track
43504        // would lead to the introduction of a gap in the audio buffer
43505  
43506  
43507        var prefixWithSilence = function prefixWithSilence(track, frames, audioAppendStartTs, videoBaseMediaDecodeTime) {
43508          var baseMediaDecodeTimeTs,
43509              frameDuration = 0,
43510              audioGapDuration = 0,
43511              audioFillFrameCount = 0,
43512              audioFillDuration = 0,
43513              silentFrame,
43514              i,
43515              firstFrame;
43516  
43517          if (!frames.length) {
43518            return;
43519          }
43520  
43521          baseMediaDecodeTimeTs = clock.audioTsToVideoTs(track.baseMediaDecodeTime, track.samplerate); // determine frame clock duration based on sample rate, round up to avoid overfills
43522  
43523          frameDuration = Math.ceil(clock.ONE_SECOND_IN_TS / (track.samplerate / 1024));
43524  
43525          if (audioAppendStartTs && videoBaseMediaDecodeTime) {
43526            // insert the shortest possible amount (audio gap or audio to video gap)
43527            audioGapDuration = baseMediaDecodeTimeTs - Math.max(audioAppendStartTs, videoBaseMediaDecodeTime); // number of full frames in the audio gap
43528  
43529            audioFillFrameCount = Math.floor(audioGapDuration / frameDuration);
43530            audioFillDuration = audioFillFrameCount * frameDuration;
43531          } // don't attempt to fill gaps smaller than a single frame or larger
43532          // than a half second
43533  
43534  
43535          if (audioFillFrameCount < 1 || audioFillDuration > clock.ONE_SECOND_IN_TS / 2) {
43536            return;
43537          }
43538  
43539          silentFrame = silence[track.samplerate];
43540  
43541          if (!silentFrame) {
43542            // we don't have a silent frame pregenerated for the sample rate, so use a frame
43543            // from the content instead
43544            silentFrame = frames[0].data;
43545          }
43546  
43547          for (i = 0; i < audioFillFrameCount; i++) {
43548            firstFrame = frames[0];
43549            frames.splice(0, 0, {
43550              data: silentFrame,
43551              dts: firstFrame.dts - frameDuration,
43552              pts: firstFrame.pts - frameDuration
43553            });
43554          }
43555  
43556          track.baseMediaDecodeTime -= Math.floor(clock.videoTsToAudioTs(audioFillDuration, track.samplerate));
43557        }; // If the audio segment extends before the earliest allowed dts
43558        // value, remove AAC frames until starts at or after the earliest
43559        // allowed DTS so that we don't end up with a negative baseMedia-
43560        // DecodeTime for the audio track
43561  
43562  
43563        var trimAdtsFramesByEarliestDts = function trimAdtsFramesByEarliestDts(adtsFrames, track, earliestAllowedDts) {
43564          if (track.minSegmentDts >= earliestAllowedDts) {
43565            return adtsFrames;
43566          } // We will need to recalculate the earliest segment Dts
43567  
43568  
43569          track.minSegmentDts = Infinity;
43570          return adtsFrames.filter(function (currentFrame) {
43571            // If this is an allowed frame, keep it and record it's Dts
43572            if (currentFrame.dts >= earliestAllowedDts) {
43573              track.minSegmentDts = Math.min(track.minSegmentDts, currentFrame.dts);
43574              track.minSegmentPts = track.minSegmentDts;
43575              return true;
43576            } // Otherwise, discard it
43577  
43578  
43579            return false;
43580          });
43581        }; // generate the track's raw mdat data from an array of frames
43582  
43583  
43584        var generateSampleTable$1 = function generateSampleTable(frames) {
43585          var i,
43586              currentFrame,
43587              samples = [];
43588  
43589          for (i = 0; i < frames.length; i++) {
43590            currentFrame = frames[i];
43591            samples.push({
43592              size: currentFrame.data.byteLength,
43593              duration: 1024 // For AAC audio, all samples contain 1024 samples
43594  
43595            });
43596          }
43597  
43598          return samples;
43599        }; // generate the track's sample table from an array of frames
43600  
43601  
43602        var concatenateFrameData = function concatenateFrameData(frames) {
43603          var i,
43604              currentFrame,
43605              dataOffset = 0,
43606              data = new Uint8Array(sumFrameByteLengths(frames));
43607  
43608          for (i = 0; i < frames.length; i++) {
43609            currentFrame = frames[i];
43610            data.set(currentFrame.data, dataOffset);
43611            dataOffset += currentFrame.data.byteLength;
43612          }
43613  
43614          return data;
43615        };
43616  
43617        var audioFrameUtils = {
43618          prefixWithSilence: prefixWithSilence,
43619          trimAdtsFramesByEarliestDts: trimAdtsFramesByEarliestDts,
43620          generateSampleTable: generateSampleTable$1,
43621          concatenateFrameData: concatenateFrameData
43622        };
43623        /**
43624         * mux.js
43625         *
43626         * Copyright (c) Brightcove
43627         * Licensed Apache-2.0 https://github.com/videojs/mux.js/blob/master/LICENSE
43628         */
43629  
43630        var ONE_SECOND_IN_TS$1 = clock.ONE_SECOND_IN_TS;
43631        /**
43632         * Store information about the start and end of the track and the
43633         * duration for each frame/sample we process in order to calculate
43634         * the baseMediaDecodeTime
43635         */
43636  
43637        var collectDtsInfo = function collectDtsInfo(track, data) {
43638          if (typeof data.pts === 'number') {
43639            if (track.timelineStartInfo.pts === undefined) {
43640              track.timelineStartInfo.pts = data.pts;
43641            }
43642  
43643            if (track.minSegmentPts === undefined) {
43644              track.minSegmentPts = data.pts;
43645            } else {
43646              track.minSegmentPts = Math.min(track.minSegmentPts, data.pts);
43647            }
43648  
43649            if (track.maxSegmentPts === undefined) {
43650              track.maxSegmentPts = data.pts;
43651            } else {
43652              track.maxSegmentPts = Math.max(track.maxSegmentPts, data.pts);
43653            }
43654          }
43655  
43656          if (typeof data.dts === 'number') {
43657            if (track.timelineStartInfo.dts === undefined) {
43658              track.timelineStartInfo.dts = data.dts;
43659            }
43660  
43661            if (track.minSegmentDts === undefined) {
43662              track.minSegmentDts = data.dts;
43663            } else {
43664              track.minSegmentDts = Math.min(track.minSegmentDts, data.dts);
43665            }
43666  
43667            if (track.maxSegmentDts === undefined) {
43668              track.maxSegmentDts = data.dts;
43669            } else {
43670              track.maxSegmentDts = Math.max(track.maxSegmentDts, data.dts);
43671            }
43672          }
43673        };
43674        /**
43675         * Clear values used to calculate the baseMediaDecodeTime between
43676         * tracks
43677         */
43678  
43679  
43680        var clearDtsInfo = function clearDtsInfo(track) {
43681          delete track.minSegmentDts;
43682          delete track.maxSegmentDts;
43683          delete track.minSegmentPts;
43684          delete track.maxSegmentPts;
43685        };
43686        /**
43687         * Calculate the track's baseMediaDecodeTime based on the earliest
43688         * DTS the transmuxer has ever seen and the minimum DTS for the
43689         * current track
43690         * @param track {object} track metadata configuration
43691         * @param keepOriginalTimestamps {boolean} If true, keep the timestamps
43692         *        in the source; false to adjust the first segment to start at 0.
43693         */
43694  
43695  
43696        var calculateTrackBaseMediaDecodeTime = function calculateTrackBaseMediaDecodeTime(track, keepOriginalTimestamps) {
43697          var baseMediaDecodeTime,
43698              scale,
43699              minSegmentDts = track.minSegmentDts; // Optionally adjust the time so the first segment starts at zero.
43700  
43701          if (!keepOriginalTimestamps) {
43702            minSegmentDts -= track.timelineStartInfo.dts;
43703          } // track.timelineStartInfo.baseMediaDecodeTime is the location, in time, where
43704          // we want the start of the first segment to be placed
43705  
43706  
vendor: 9,973 bytes, lines 43707-43972
43707          baseMediaDecodeTime = track.timelineStartInfo.baseMediaDecodeTime; // Add to that the distance this segment is from the very first
43708  
43709          baseMediaDecodeTime += minSegmentDts; // baseMediaDecodeTime must not become negative
43710  
43711          baseMediaDecodeTime = Math.max(0, baseMediaDecodeTime);
43712  
43713          if (track.type === 'audio') {
43714            // Audio has a different clock equal to the sampling_rate so we need to
43715            // scale the PTS values into the clock rate of the track
43716            scale = track.samplerate / ONE_SECOND_IN_TS$1;
43717            baseMediaDecodeTime *= scale;
43718            baseMediaDecodeTime = Math.floor(baseMediaDecodeTime);
43719          }
43720  
43721          return baseMediaDecodeTime;
43722        };
43723  
43724        var trackDecodeInfo = {
43725          clearDtsInfo: clearDtsInfo,
43726          calculateTrackBaseMediaDecodeTime: calculateTrackBaseMediaDecodeTime,
43727          collectDtsInfo: collectDtsInfo
43728        };
43729        /**
43730         * mux.js
43731         *
43732         * Copyright (c) Brightcove
43733         * Licensed Apache-2.0 https://github.com/videojs/mux.js/blob/master/LICENSE
43734         *
43735         * Reads in-band caption information from a video elementary
43736         * stream. Captions must follow the CEA-708 standard for injection
43737         * into an MPEG-2 transport streams.
43738         * @see https://en.wikipedia.org/wiki/CEA-708
43739         * @see https://www.gpo.gov/fdsys/pkg/CFR-2007-title47-vol1/pdf/CFR-2007-title47-vol1-sec15-119.pdf
43740         */
43741        // Supplemental enhancement information (SEI) NAL units have a
43742        // payload type field to indicate how they are to be
43743        // interpreted. CEAS-708 caption content is always transmitted with
43744        // payload type 0x04.
43745  
43746        var USER_DATA_REGISTERED_ITU_T_T35 = 4,
43747            RBSP_TRAILING_BITS = 128;
43748        /**
43749          * Parse a supplemental enhancement information (SEI) NAL unit.
43750          * Stops parsing once a message of type ITU T T35 has been found.
43751          *
43752          * @param bytes {Uint8Array} the bytes of a SEI NAL unit
43753          * @return {object} the parsed SEI payload
43754          * @see Rec. ITU-T H.264, 7.3.2.3.1
43755          */
43756  
43757        var parseSei = function parseSei(bytes) {
43758          var i = 0,
43759              result = {
43760            payloadType: -1,
43761            payloadSize: 0
43762          },
43763              payloadType = 0,
43764              payloadSize = 0; // go through the sei_rbsp parsing each each individual sei_message
43765  
43766          while (i < bytes.byteLength) {
43767            // stop once we have hit the end of the sei_rbsp
43768            if (bytes[i] === RBSP_TRAILING_BITS) {
43769              break;
43770            } // Parse payload type
43771  
43772  
43773            while (bytes[i] === 0xFF) {
43774              payloadType += 255;
43775              i++;
43776            }
43777  
43778            payloadType += bytes[i++]; // Parse payload size
43779  
43780            while (bytes[i] === 0xFF) {
43781              payloadSize += 255;
43782              i++;
43783            }
43784  
43785            payloadSize += bytes[i++]; // this sei_message is a 608/708 caption so save it and break
43786            // there can only ever be one caption message in a frame's sei
43787  
43788            if (!result.payload && payloadType === USER_DATA_REGISTERED_ITU_T_T35) {
43789              result.payloadType = payloadType;
43790              result.payloadSize = payloadSize;
43791              result.payload = bytes.subarray(i, i + payloadSize);
43792              break;
43793            } // skip the payload and parse the next message
43794  
43795  
43796            i += payloadSize;
43797            payloadType = 0;
43798            payloadSize = 0;
43799          }
43800  
43801          return result;
43802        }; // see ANSI/SCTE 128-1 (2013), section 8.1
43803  
43804  
43805        var parseUserData = function parseUserData(sei) {
43806          // itu_t_t35_contry_code must be 181 (United States) for
43807          // captions
43808          if (sei.payload[0] !== 181) {
43809            return null;
43810          } // itu_t_t35_provider_code should be 49 (ATSC) for captions
43811  
43812  
43813          if ((sei.payload[1] << 8 | sei.payload[2]) !== 49) {
43814            return null;
43815          } // the user_identifier should be "GA94" to indicate ATSC1 data
43816  
43817  
43818          if (String.fromCharCode(sei.payload[3], sei.payload[4], sei.payload[5], sei.payload[6]) !== 'GA94') {
43819            return null;
43820          } // finally, user_data_type_code should be 0x03 for caption data
43821  
43822  
43823          if (sei.payload[7] !== 0x03) {
43824            return null;
43825          } // return the user_data_type_structure and strip the trailing
43826          // marker bits
43827  
43828  
43829          return sei.payload.subarray(8, sei.payload.length - 1);
43830        }; // see CEA-708-D, section 4.4
43831  
43832  
43833        var parseCaptionPackets = function parseCaptionPackets(pts, userData) {
43834          var results = [],
43835              i,
43836              count,
43837              offset,
43838              data; // if this is just filler, return immediately
43839  
43840          if (!(userData[0] & 0x40)) {
43841            return results;
43842          } // parse out the cc_data_1 and cc_data_2 fields
43843  
43844  
43845          count = userData[0] & 0x1f;
43846  
43847          for (i = 0; i < count; i++) {
43848            offset = i * 3;
43849            data = {
43850              type: userData[offset + 2] & 0x03,
43851              pts: pts
43852            }; // capture cc data when cc_valid is 1
43853  
43854            if (userData[offset + 2] & 0x04) {
43855              data.ccData = userData[offset + 3] << 8 | userData[offset + 4];
43856              results.push(data);
43857            }
43858          }
43859  
43860          return results;
43861        };
43862  
43863        var discardEmulationPreventionBytes = function discardEmulationPreventionBytes(data) {
43864          var length = data.byteLength,
43865              emulationPreventionBytesPositions = [],
43866              i = 1,
43867              newLength,
43868              newData; // Find all `Emulation Prevention Bytes`
43869  
43870          while (i < length - 2) {
43871            if (data[i] === 0 && data[i + 1] === 0 && data[i + 2] === 0x03) {
43872              emulationPreventionBytesPositions.push(i + 2);
43873              i += 2;
43874            } else {
43875              i++;
43876            }
43877          } // If no Emulation Prevention Bytes were found just return the original
43878          // array
43879  
43880  
43881          if (emulationPreventionBytesPositions.length === 0) {
43882            return data;
43883          } // Create a new array to hold the NAL unit data
43884  
43885  
43886          newLength = length - emulationPreventionBytesPositions.length;
43887          newData = new Uint8Array(newLength);
43888          var sourceIndex = 0;
43889  
43890          for (i = 0; i < newLength; sourceIndex++, i++) {
43891            if (sourceIndex === emulationPreventionBytesPositions[0]) {
43892              // Skip this byte
43893              sourceIndex++; // Remove this position index
43894  
43895              emulationPreventionBytesPositions.shift();
43896            }
43897  
43898            newData[i] = data[sourceIndex];
43899          }
43900  
43901          return newData;
43902        }; // exports
43903  
43904  
43905        var captionPacketParser = {
43906          parseSei: parseSei,
43907          parseUserData: parseUserData,
43908          parseCaptionPackets: parseCaptionPackets,
43909          discardEmulationPreventionBytes: discardEmulationPreventionBytes,
43910          USER_DATA_REGISTERED_ITU_T_T35: USER_DATA_REGISTERED_ITU_T_T35
43911        }; // -----------------
43912        // Link To Transport
43913        // -----------------
43914  
43915        var CaptionStream = function CaptionStream() {
43916          CaptionStream.prototype.init.call(this);
43917          this.captionPackets_ = [];
43918          this.ccStreams_ = [new Cea608Stream(0, 0), // eslint-disable-line no-use-before-define
43919          new Cea608Stream(0, 1), // eslint-disable-line no-use-before-define
43920          new Cea608Stream(1, 0), // eslint-disable-line no-use-before-define
43921          new Cea608Stream(1, 1) // eslint-disable-line no-use-before-define
43922          ];
43923          this.reset(); // forward data and done events from CCs to this CaptionStream
43924  
43925          this.ccStreams_.forEach(function (cc) {
43926            cc.on('data', this.trigger.bind(this, 'data'));
43927            cc.on('partialdone', this.trigger.bind(this, 'partialdone'));
43928            cc.on('done', this.trigger.bind(this, 'done'));
43929          }, this);
43930        };
43931  
43932        CaptionStream.prototype = new stream();
43933  
43934        CaptionStream.prototype.push = function (event) {
43935          var sei, userData, newCaptionPackets; // only examine SEI NALs
43936  
43937          if (event.nalUnitType !== 'sei_rbsp') {
43938            return;
43939          } // parse the sei
43940  
43941  
43942          sei = captionPacketParser.parseSei(event.escapedRBSP); // ignore everything but user_data_registered_itu_t_t35
43943  
43944          if (sei.payloadType !== captionPacketParser.USER_DATA_REGISTERED_ITU_T_T35) {
43945            return;
43946          } // parse out the user data payload
43947  
43948  
43949          userData = captionPacketParser.parseUserData(sei); // ignore unrecognized userData
43950  
43951          if (!userData) {
43952            return;
43953          } // Sometimes, the same segment # will be downloaded twice. To stop the
43954          // caption data from being processed twice, we track the latest dts we've
43955          // received and ignore everything with a dts before that. However, since
43956          // data for a specific dts can be split across packets on either side of
43957          // a segment boundary, we need to make sure we *don't* ignore the packets
43958          // from the *next* segment that have dts === this.latestDts_. By constantly
43959          // tracking the number of packets received with dts === this.latestDts_, we
43960          // know how many should be ignored once we start receiving duplicates.
43961  
43962  
43963          if (event.dts < this.latestDts_) {
43964            // We've started getting older data, so set the flag.
43965            this.ignoreNextEqualDts_ = true;
43966            return;
43967          } else if (event.dts === this.latestDts_ && this.ignoreNextEqualDts_) {
43968            this.numSameDts_--;
43969  
43970            if (!this.numSameDts_) {
43971              // We've received the last duplicate packet, time to start processing again
43972              this.ignoreNextEqualDts_ = false;
vendor: 6,564 bytes, lines 43973-44159
43973            }
43974  
43975            return;
43976          } // parse out CC data packets and save them for later
43977  
43978  
43979          newCaptionPackets = captionPacketParser.parseCaptionPackets(event.pts, userData);
43980          this.captionPackets_ = this.captionPackets_.concat(newCaptionPackets);
43981  
43982          if (this.latestDts_ !== event.dts) {
43983            this.numSameDts_ = 0;
43984          }
43985  
43986          this.numSameDts_++;
43987          this.latestDts_ = event.dts;
43988        };
43989  
43990        CaptionStream.prototype.flushCCStreams = function (flushType) {
43991          this.ccStreams_.forEach(function (cc) {
43992            return flushType === 'flush' ? cc.flush() : cc.partialFlush();
43993          }, this);
43994        };
43995  
43996        CaptionStream.prototype.flushStream = function (flushType) {
43997          // make sure we actually parsed captions before proceeding
43998          if (!this.captionPackets_.length) {
43999            this.flushCCStreams(flushType);
44000            return;
44001          } // In Chrome, the Array#sort function is not stable so add a
44002          // presortIndex that we can use to ensure we get a stable-sort
44003  
44004  
44005          this.captionPackets_.forEach(function (elem, idx) {
44006            elem.presortIndex = idx;
44007          }); // sort caption byte-pairs based on their PTS values
44008  
44009          this.captionPackets_.sort(function (a, b) {
44010            if (a.pts === b.pts) {
44011              return a.presortIndex - b.presortIndex;
44012            }
44013  
44014            return a.pts - b.pts;
44015          });
44016          this.captionPackets_.forEach(function (packet) {
44017            if (packet.type < 2) {
44018              // Dispatch packet to the right Cea608Stream
44019              this.dispatchCea608Packet(packet);
44020            } // this is where an 'else' would go for a dispatching packets
44021            // to a theoretical Cea708Stream that handles SERVICEn data
44022  
44023          }, this);
44024          this.captionPackets_.length = 0;
44025          this.flushCCStreams(flushType);
44026        };
44027  
44028        CaptionStream.prototype.flush = function () {
44029          return this.flushStream('flush');
44030        }; // Only called if handling partial data
44031  
44032  
44033        CaptionStream.prototype.partialFlush = function () {
44034          return this.flushStream('partialFlush');
44035        };
44036  
44037        CaptionStream.prototype.reset = function () {
44038          this.latestDts_ = null;
44039          this.ignoreNextEqualDts_ = false;
44040          this.numSameDts_ = 0;
44041          this.activeCea608Channel_ = [null, null];
44042          this.ccStreams_.forEach(function (ccStream) {
44043            ccStream.reset();
44044          });
44045        }; // From the CEA-608 spec:
44046  
44047        /*
44048         * When XDS sub-packets are interleaved with other services, the end of each sub-packet shall be followed
44049         * by a control pair to change to a different service. When any of the control codes from 0x10 to 0x1F is
44050         * used to begin a control code pair, it indicates the return to captioning or Text data. The control code pair
44051         * and subsequent data should then be processed according to the FCC rules. It may be necessary for the
44052         * line 21 data encoder to automatically insert a control code pair (i.e. RCL, RU2, RU3, RU4, RDC, or RTD)
44053         * to switch to captioning or Text.
44054        */
44055        // With that in mind, we ignore any data between an XDS control code and a
44056        // subsequent closed-captioning control code.
44057  
44058  
44059        CaptionStream.prototype.dispatchCea608Packet = function (packet) {
44060          // NOTE: packet.type is the CEA608 field
44061          if (this.setsTextOrXDSActive(packet)) {
44062            this.activeCea608Channel_[packet.type] = null;
44063          } else if (this.setsChannel1Active(packet)) {
44064            this.activeCea608Channel_[packet.type] = 0;
44065          } else if (this.setsChannel2Active(packet)) {
44066            this.activeCea608Channel_[packet.type] = 1;
44067          }
44068  
44069          if (this.activeCea608Channel_[packet.type] === null) {
44070            // If we haven't received anything to set the active channel, or the
44071            // packets are Text/XDS data, discard the data; we don't want jumbled
44072            // captions
44073            return;
44074          }
44075  
44076          this.ccStreams_[(packet.type << 1) + this.activeCea608Channel_[packet.type]].push(packet);
44077        };
44078  
44079        CaptionStream.prototype.setsChannel1Active = function (packet) {
44080          return (packet.ccData & 0x7800) === 0x1000;
44081        };
44082  
44083        CaptionStream.prototype.setsChannel2Active = function (packet) {
44084          return (packet.ccData & 0x7800) === 0x1800;
44085        };
44086  
44087        CaptionStream.prototype.setsTextOrXDSActive = function (packet) {
44088          return (packet.ccData & 0x7100) === 0x0100 || (packet.ccData & 0x78fe) === 0x102a || (packet.ccData & 0x78fe) === 0x182a;
44089        }; // ----------------------
44090        // Session to Application
44091        // ----------------------
44092        // This hash maps non-ASCII, special, and extended character codes to their
44093        // proper Unicode equivalent. The first keys that are only a single byte
44094        // are the non-standard ASCII characters, which simply map the CEA608 byte
44095        // to the standard ASCII/Unicode. The two-byte keys that follow are the CEA608
44096        // character codes, but have their MSB bitmasked with 0x03 so that a lookup
44097        // can be performed regardless of the field and data channel on which the
44098        // character code was received.
44099  
44100  
44101        var CHARACTER_TRANSLATION = {
44102          0x2a: 0xe1,
44103          // á
44104          0x5c: 0xe9,
44105          // é
44106          0x5e: 0xed,
44107          // í
44108          0x5f: 0xf3,
44109          // ó
44110          0x60: 0xfa,
44111          // ú
44112          0x7b: 0xe7,
44113          // ç
44114          0x7c: 0xf7,
44115          // ÷
44116          0x7d: 0xd1,
44117          // Ñ
44118          0x7e: 0xf1,
44119          // ñ
44120          0x7f: 0x2588,
44121          // █
44122          0x0130: 0xae,
44123          // ®
44124          0x0131: 0xb0,
44125          // °
44126          0x0132: 0xbd,
44127          // ½
44128          0x0133: 0xbf,
44129          // ¿
44130          0x0134: 0x2122,
44131          // ™
44132          0x0135: 0xa2,
44133          // ¢
44134          0x0136: 0xa3,
44135          // £
44136          0x0137: 0x266a,
44137          // ♪
44138          0x0138: 0xe0,
44139          // à
44140          0x0139: 0xa0,
44141          //
44142          0x013a: 0xe8,
44143          // è
44144          0x013b: 0xe2,
44145          // â
44146          0x013c: 0xea,
44147          // ê
44148          0x013d: 0xee,
44149          // î
44150          0x013e: 0xf4,
44151          // ô
44152          0x013f: 0xfb,
44153          // û
44154          0x0220: 0xc1,
44155          // Á
44156          0x0221: 0xc9,
44157          // É
44158          0x0222: 0xd3,
44159          // Ó
vendor: 26,695 bytes, lines 44160-44866
44160          0x0223: 0xda,
44161          // Ú
44162          0x0224: 0xdc,
44163          // Ü
44164          0x0225: 0xfc,
44165          // ü
44166          0x0226: 0x2018,
44167          // ‘
44168          0x0227: 0xa1,
44169          // ¡
44170          0x0228: 0x2a,
44171          // *
44172          0x0229: 0x27,
44173          // '
44174          0x022a: 0x2014,
44175          // —
44176          0x022b: 0xa9,
44177          // ©
44178          0x022c: 0x2120,
44179          // ℠
44180          0x022d: 0x2022,
44181          // •
44182          0x022e: 0x201c,
44183          // “
44184          0x022f: 0x201d,
44185          // ”
44186          0x0230: 0xc0,
44187          // À
44188          0x0231: 0xc2,
44189          // Â
44190          0x0232: 0xc7,
44191          // Ç
44192          0x0233: 0xc8,
44193          // È
44194          0x0234: 0xca,
44195          // Ê
44196          0x0235: 0xcb,
44197          // Ë
44198          0x0236: 0xeb,
44199          // ë
44200          0x0237: 0xce,
44201          // Î
44202          0x0238: 0xcf,
44203          // Ï
44204          0x0239: 0xef,
44205          // ï
44206          0x023a: 0xd4,
44207          // Ô
44208          0x023b: 0xd9,
44209          // Ù
44210          0x023c: 0xf9,
44211          // ù
44212          0x023d: 0xdb,
44213          // Û
44214          0x023e: 0xab,
44215          // «
44216          0x023f: 0xbb,
44217          // »
44218          0x0320: 0xc3,
44219          // Ã
44220          0x0321: 0xe3,
44221          // ã
44222          0x0322: 0xcd,
44223          // Í
44224          0x0323: 0xcc,
44225          // Ì
44226          0x0324: 0xec,
44227          // ì
44228          0x0325: 0xd2,
44229          // Ò
44230          0x0326: 0xf2,
44231          // ò
44232          0x0327: 0xd5,
44233          // Õ
44234          0x0328: 0xf5,
44235          // õ
44236          0x0329: 0x7b,
44237          // {
44238          0x032a: 0x7d,
44239          // }
44240          0x032b: 0x5c,
44241          // \
44242          0x032c: 0x5e,
44243          // ^
44244          0x032d: 0x5f,
44245          // _
44246          0x032e: 0x7c,
44247          // |
44248          0x032f: 0x7e,
44249          // ~
44250          0x0330: 0xc4,
44251          // Ä
44252          0x0331: 0xe4,
44253          // ä
44254          0x0332: 0xd6,
44255          // Ö
44256          0x0333: 0xf6,
44257          // ö
44258          0x0334: 0xdf,
44259          // ß
44260          0x0335: 0xa5,
44261          // ¥
44262          0x0336: 0xa4,
44263          // ¤
44264          0x0337: 0x2502,
44265          // │
44266          0x0338: 0xc5,
44267          // Å
44268          0x0339: 0xe5,
44269          // å
44270          0x033a: 0xd8,
44271          // Ø
44272          0x033b: 0xf8,
44273          // ø
44274          0x033c: 0x250c,
44275          // ┌
44276          0x033d: 0x2510,
44277          // ┐
44278          0x033e: 0x2514,
44279          // └
44280          0x033f: 0x2518 // ┘
44281  
44282        };
44283  
44284        var getCharFromCode = function getCharFromCode(code) {
44285          if (code === null) {
44286            return '';
44287          }
44288  
44289          code = CHARACTER_TRANSLATION[code] || code;
44290          return String.fromCharCode(code);
44291        }; // the index of the last row in a CEA-608 display buffer
44292  
44293  
44294        var BOTTOM_ROW = 14; // This array is used for mapping PACs -> row #, since there's no way of
44295        // getting it through bit logic.
44296  
44297        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
44298        // cells. The "bottom" row is the last element in the outer array.
44299  
44300        var createDisplayBuffer = function createDisplayBuffer() {
44301          var result = [],
44302              i = BOTTOM_ROW + 1;
44303  
44304          while (i--) {
44305            result.push('');
44306          }
44307  
44308          return result;
44309        };
44310  
44311        var Cea608Stream = function Cea608Stream(field, dataChannel) {
44312          Cea608Stream.prototype.init.call(this);
44313          this.field_ = field || 0;
44314          this.dataChannel_ = dataChannel || 0;
44315          this.name_ = 'CC' + ((this.field_ << 1 | this.dataChannel_) + 1);
44316          this.setConstants();
44317          this.reset();
44318  
44319          this.push = function (packet) {
44320            var data, swap, char0, char1, text; // remove the parity bits
44321  
44322            data = packet.ccData & 0x7f7f; // ignore duplicate control codes; the spec demands they're sent twice
44323  
44324            if (data === this.lastControlCode_) {
44325              this.lastControlCode_ = null;
44326              return;
44327            } // Store control codes
44328  
44329  
44330            if ((data & 0xf000) === 0x1000) {
44331              this.lastControlCode_ = data;
44332            } else if (data !== this.PADDING_) {
44333              this.lastControlCode_ = null;
44334            }
44335  
44336            char0 = data >>> 8;
44337            char1 = data & 0xff;
44338  
44339            if (data === this.PADDING_) {
44340              return;
44341            } else if (data === this.RESUME_CAPTION_LOADING_) {
44342              this.mode_ = 'popOn';
44343            } else if (data === this.END_OF_CAPTION_) {
44344              // If an EOC is received while in paint-on mode, the displayed caption
44345              // text should be swapped to non-displayed memory as if it was a pop-on
44346              // caption. Because of that, we should explicitly switch back to pop-on
44347              // mode
44348              this.mode_ = 'popOn';
44349              this.clearFormatting(packet.pts); // if a caption was being displayed, it's gone now
44350  
44351              this.flushDisplayed(packet.pts); // flip memory
44352  
44353              swap = this.displayed_;
44354              this.displayed_ = this.nonDisplayed_;
44355              this.nonDisplayed_ = swap; // start measuring the time to display the caption
44356  
44357              this.startPts_ = packet.pts;
44358            } else if (data === this.ROLL_UP_2_ROWS_) {
44359              this.rollUpRows_ = 2;
44360              this.setRollUp(packet.pts);
44361            } else if (data === this.ROLL_UP_3_ROWS_) {
44362              this.rollUpRows_ = 3;
44363              this.setRollUp(packet.pts);
44364            } else if (data === this.ROLL_UP_4_ROWS_) {
44365              this.rollUpRows_ = 4;
44366              this.setRollUp(packet.pts);
44367            } else if (data === this.CARRIAGE_RETURN_) {
44368              this.clearFormatting(packet.pts);
44369              this.flushDisplayed(packet.pts);
44370              this.shiftRowsUp_();
44371              this.startPts_ = packet.pts;
44372            } else if (data === this.BACKSPACE_) {
44373              if (this.mode_ === 'popOn') {
44374                this.nonDisplayed_[this.row_] = this.nonDisplayed_[this.row_].slice(0, -1);
44375              } else {
44376                this.displayed_[this.row_] = this.displayed_[this.row_].slice(0, -1);
44377              }
44378            } else if (data === this.ERASE_DISPLAYED_MEMORY_) {
44379              this.flushDisplayed(packet.pts);
44380              this.displayed_ = createDisplayBuffer();
44381            } else if (data === this.ERASE_NON_DISPLAYED_MEMORY_) {
44382              this.nonDisplayed_ = createDisplayBuffer();
44383            } else if (data === this.RESUME_DIRECT_CAPTIONING_) {
44384              if (this.mode_ !== 'paintOn') {
44385                // NOTE: This should be removed when proper caption positioning is
44386                // implemented
44387                this.flushDisplayed(packet.pts);
44388                this.displayed_ = createDisplayBuffer();
44389              }
44390  
44391              this.mode_ = 'paintOn';
44392              this.startPts_ = packet.pts; // Append special characters to caption text
44393            } else if (this.isSpecialCharacter(char0, char1)) {
44394              // Bitmask char0 so that we can apply character transformations
44395              // regardless of field and data channel.
44396              // Then byte-shift to the left and OR with char1 so we can pass the
44397              // entire character code to `getCharFromCode`.
44398              char0 = (char0 & 0x03) << 8;
44399              text = getCharFromCode(char0 | char1);
44400              this[this.mode_](packet.pts, text);
44401              this.column_++; // Append extended characters to caption text
44402            } else if (this.isExtCharacter(char0, char1)) {
44403              // Extended characters always follow their "non-extended" equivalents.
44404              // IE if a "è" is desired, you'll always receive "eè"; non-compliant
44405              // decoders are supposed to drop the "è", while compliant decoders
44406              // backspace the "e" and insert "è".
44407              // Delete the previous character
44408              if (this.mode_ === 'popOn') {
44409                this.nonDisplayed_[this.row_] = this.nonDisplayed_[this.row_].slice(0, -1);
44410              } else {
44411                this.displayed_[this.row_] = this.displayed_[this.row_].slice(0, -1);
44412              } // Bitmask char0 so that we can apply character transformations
44413              // regardless of field and data channel.
44414              // Then byte-shift to the left and OR with char1 so we can pass the
44415              // entire character code to `getCharFromCode`.
44416  
44417  
44418              char0 = (char0 & 0x03) << 8;
44419              text = getCharFromCode(char0 | char1);
44420              this[this.mode_](packet.pts, text);
44421              this.column_++; // Process mid-row codes
44422            } else if (this.isMidRowCode(char0, char1)) {
44423              // Attributes are not additive, so clear all formatting
44424              this.clearFormatting(packet.pts); // According to the standard, mid-row codes
44425              // should be replaced with spaces, so add one now
44426  
44427              this[this.mode_](packet.pts, ' ');
44428              this.column_++;
44429  
44430              if ((char1 & 0xe) === 0xe) {
44431                this.addFormatting(packet.pts, ['i']);
44432              }
44433  
44434              if ((char1 & 0x1) === 0x1) {
44435                this.addFormatting(packet.pts, ['u']);
44436              } // Detect offset control codes and adjust cursor
44437  
44438            } else if (this.isOffsetControlCode(char0, char1)) {
44439              // Cursor position is set by indent PAC (see below) in 4-column
44440              // increments, with an additional offset code of 1-3 to reach any
44441              // of the 32 columns specified by CEA-608. So all we need to do
44442              // here is increment the column cursor by the given offset.
44443              this.column_ += char1 & 0x03; // Detect PACs (Preamble Address Codes)
44444            } else if (this.isPAC(char0, char1)) {
44445              // There's no logic for PAC -> row mapping, so we have to just
44446              // find the row code in an array and use its index :(
44447              var row = ROWS.indexOf(data & 0x1f20); // Configure the caption window if we're in roll-up mode
44448  
44449              if (this.mode_ === 'rollUp') {
44450                // This implies that the base row is incorrectly set.
44451                // As per the recommendation in CEA-608(Base Row Implementation), defer to the number
44452                // of roll-up rows set.
44453                if (row - this.rollUpRows_ + 1 < 0) {
44454                  row = this.rollUpRows_ - 1;
44455                }
44456  
44457                this.setRollUp(packet.pts, row);
44458              }
44459  
44460              if (row !== this.row_) {
44461                // formatting is only persistent for current row
44462                this.clearFormatting(packet.pts);
44463                this.row_ = row;
44464              } // All PACs can apply underline, so detect and apply
44465              // (All odd-numbered second bytes set underline)
44466  
44467  
44468              if (char1 & 0x1 && this.formatting_.indexOf('u') === -1) {
44469                this.addFormatting(packet.pts, ['u']);
44470              }
44471  
44472              if ((data & 0x10) === 0x10) {
44473                // We've got an indent level code. Each successive even number
44474                // increments the column cursor by 4, so we can get the desired
44475                // column position by bit-shifting to the right (to get n/2)
44476                // and multiplying by 4.
44477                this.column_ = ((data & 0xe) >> 1) * 4;
44478              }
44479  
44480              if (this.isColorPAC(char1)) {
44481                // it's a color code, though we only support white, which
44482                // can be either normal or italicized. white italics can be
44483                // either 0x4e or 0x6e depending on the row, so we just
44484                // bitwise-and with 0xe to see if italics should be turned on
44485                if ((char1 & 0xe) === 0xe) {
44486                  this.addFormatting(packet.pts, ['i']);
44487                }
44488              } // We have a normal character in char0, and possibly one in char1
44489  
44490            } else if (this.isNormalChar(char0)) {
44491              if (char1 === 0x00) {
44492                char1 = null;
44493              }
44494  
44495              text = getCharFromCode(char0);
44496              text += getCharFromCode(char1);
44497              this[this.mode_](packet.pts, text);
44498              this.column_ += text.length;
44499            } // finish data processing
44500  
44501          };
44502        };
44503  
44504        Cea608Stream.prototype = new stream(); // Trigger a cue point that captures the current state of the
44505        // display buffer
44506  
44507        Cea608Stream.prototype.flushDisplayed = function (pts) {
44508          var content = this.displayed_ // remove spaces from the start and end of the string
44509          .map(function (row) {
44510            try {
44511              return row.trim();
44512            } catch (e) {
44513              // Ordinarily, this shouldn't happen. However, caption
44514              // parsing errors should not throw exceptions and
44515              // break playback.
44516              // eslint-disable-next-line no-console
44517              console.error('Skipping malformed caption.');
44518              return '';
44519            }
44520          }) // combine all text rows to display in one cue
44521          .join('\n') // and remove blank rows from the start and end, but not the middle
44522          .replace(/^\n+|\n+$/g, '');
44523  
44524          if (content.length) {
44525            this.trigger('data', {
44526              startPts: this.startPts_,
44527              endPts: pts,
44528              text: content,
44529              stream: this.name_
44530            });
44531          }
44532        };
44533        /**
44534         * Zero out the data, used for startup and on seek
44535         */
44536  
44537  
44538        Cea608Stream.prototype.reset = function () {
44539          this.mode_ = 'popOn'; // When in roll-up mode, the index of the last row that will
44540          // actually display captions. If a caption is shifted to a row
44541          // with a lower index than this, it is cleared from the display
44542          // buffer
44543  
44544          this.topRow_ = 0;
44545          this.startPts_ = 0;
44546          this.displayed_ = createDisplayBuffer();
44547          this.nonDisplayed_ = createDisplayBuffer();
44548          this.lastControlCode_ = null; // Track row and column for proper line-breaking and spacing
44549  
44550          this.column_ = 0;
44551          this.row_ = BOTTOM_ROW;
44552          this.rollUpRows_ = 2; // This variable holds currently-applied formatting
44553  
44554          this.formatting_ = [];
44555        };
44556        /**
44557         * Sets up control code and related constants for this instance
44558         */
44559  
44560  
44561        Cea608Stream.prototype.setConstants = function () {
44562          // The following attributes have these uses:
44563          // ext_ :    char0 for mid-row codes, and the base for extended
44564          //           chars (ext_+0, ext_+1, and ext_+2 are char0s for
44565          //           extended codes)
44566          // control_: char0 for control codes, except byte-shifted to the
44567          //           left so that we can do this.control_ | CONTROL_CODE
44568          // offset_:  char0 for tab offset codes
44569          //
44570          // It's also worth noting that control codes, and _only_ control codes,
44571          // differ between field 1 and field2. Field 2 control codes are always
44572          // their field 1 value plus 1. That's why there's the "| field" on the
44573          // control value.
44574          if (this.dataChannel_ === 0) {
44575            this.BASE_ = 0x10;
44576            this.EXT_ = 0x11;
44577            this.CONTROL_ = (0x14 | this.field_) << 8;
44578            this.OFFSET_ = 0x17;
44579          } else if (this.dataChannel_ === 1) {
44580            this.BASE_ = 0x18;
44581            this.EXT_ = 0x19;
44582            this.CONTROL_ = (0x1c | this.field_) << 8;
44583            this.OFFSET_ = 0x1f;
44584          } // Constants for the LSByte command codes recognized by Cea608Stream. This
44585          // list is not exhaustive. For a more comprehensive listing and semantics see
44586          // http://www.gpo.gov/fdsys/pkg/CFR-2010-title47-vol1/pdf/CFR-2010-title47-vol1-sec15-119.pdf
44587          // Padding
44588  
44589  
44590          this.PADDING_ = 0x0000; // Pop-on Mode
44591  
44592          this.RESUME_CAPTION_LOADING_ = this.CONTROL_ | 0x20;
44593          this.END_OF_CAPTION_ = this.CONTROL_ | 0x2f; // Roll-up Mode
44594  
44595          this.ROLL_UP_2_ROWS_ = this.CONTROL_ | 0x25;
44596          this.ROLL_UP_3_ROWS_ = this.CONTROL_ | 0x26;
44597          this.ROLL_UP_4_ROWS_ = this.CONTROL_ | 0x27;
44598          this.CARRIAGE_RETURN_ = this.CONTROL_ | 0x2d; // paint-on mode
44599  
44600          this.RESUME_DIRECT_CAPTIONING_ = this.CONTROL_ | 0x29; // Erasure
44601  
44602          this.BACKSPACE_ = this.CONTROL_ | 0x21;
44603          this.ERASE_DISPLAYED_MEMORY_ = this.CONTROL_ | 0x2c;
44604          this.ERASE_NON_DISPLAYED_MEMORY_ = this.CONTROL_ | 0x2e;
44605        };
44606        /**
44607         * Detects if the 2-byte packet data is a special character
44608         *
44609         * Special characters have a second byte in the range 0x30 to 0x3f,
44610         * with the first byte being 0x11 (for data channel 1) or 0x19 (for
44611         * data channel 2).
44612         *
44613         * @param  {Integer} char0 The first byte
44614         * @param  {Integer} char1 The second byte
44615         * @return {Boolean}       Whether the 2 bytes are an special character
44616         */
44617  
44618  
44619        Cea608Stream.prototype.isSpecialCharacter = function (char0, char1) {
44620          return char0 === this.EXT_ && char1 >= 0x30 && char1 <= 0x3f;
44621        };
44622        /**
44623         * Detects if the 2-byte packet data is an extended character
44624         *
44625         * Extended characters have a second byte in the range 0x20 to 0x3f,
44626         * with the first byte being 0x12 or 0x13 (for data channel 1) or
44627         * 0x1a or 0x1b (for data channel 2).
44628         *
44629         * @param  {Integer} char0 The first byte
44630         * @param  {Integer} char1 The second byte
44631         * @return {Boolean}       Whether the 2 bytes are an extended character
44632         */
44633  
44634  
44635        Cea608Stream.prototype.isExtCharacter = function (char0, char1) {
44636          return (char0 === this.EXT_ + 1 || char0 === this.EXT_ + 2) && char1 >= 0x20 && char1 <= 0x3f;
44637        };
44638        /**
44639         * Detects if the 2-byte packet is a mid-row code
44640         *
44641         * Mid-row codes have a second byte in the range 0x20 to 0x2f, with
44642         * the first byte being 0x11 (for data channel 1) or 0x19 (for data
44643         * channel 2).
44644         *
44645         * @param  {Integer} char0 The first byte
44646         * @param  {Integer} char1 The second byte
44647         * @return {Boolean}       Whether the 2 bytes are a mid-row code
44648         */
44649  
44650  
44651        Cea608Stream.prototype.isMidRowCode = function (char0, char1) {
44652          return char0 === this.EXT_ && char1 >= 0x20 && char1 <= 0x2f;
44653        };
44654        /**
44655         * Detects if the 2-byte packet is an offset control code
44656         *
44657         * Offset control codes have a second byte in the range 0x21 to 0x23,
44658         * with the first byte being 0x17 (for data channel 1) or 0x1f (for
44659         * data channel 2).
44660         *
44661         * @param  {Integer} char0 The first byte
44662         * @param  {Integer} char1 The second byte
44663         * @return {Boolean}       Whether the 2 bytes are an offset control code
44664         */
44665  
44666  
44667        Cea608Stream.prototype.isOffsetControlCode = function (char0, char1) {
44668          return char0 === this.OFFSET_ && char1 >= 0x21 && char1 <= 0x23;
44669        };
44670        /**
44671         * Detects if the 2-byte packet is a Preamble Address Code
44672         *
44673         * PACs have a first byte in the range 0x10 to 0x17 (for data channel 1)
44674         * or 0x18 to 0x1f (for data channel 2), with the second byte in the
44675         * range 0x40 to 0x7f.
44676         *
44677         * @param  {Integer} char0 The first byte
44678         * @param  {Integer} char1 The second byte
44679         * @return {Boolean}       Whether the 2 bytes are a PAC
44680         */
44681  
44682  
44683        Cea608Stream.prototype.isPAC = function (char0, char1) {
44684          return char0 >= this.BASE_ && char0 < this.BASE_ + 8 && char1 >= 0x40 && char1 <= 0x7f;
44685        };
44686        /**
44687         * Detects if a packet's second byte is in the range of a PAC color code
44688         *
44689         * PAC color codes have the second byte be in the range 0x40 to 0x4f, or
44690         * 0x60 to 0x6f.
44691         *
44692         * @param  {Integer} char1 The second byte
44693         * @return {Boolean}       Whether the byte is a color PAC
44694         */
44695  
44696  
44697        Cea608Stream.prototype.isColorPAC = function (char1) {
44698          return char1 >= 0x40 && char1 <= 0x4f || char1 >= 0x60 && char1 <= 0x7f;
44699        };
44700        /**
44701         * Detects if a single byte is in the range of a normal character
44702         *
44703         * Normal text bytes are in the range 0x20 to 0x7f.
44704         *
44705         * @param  {Integer} char  The byte
44706         * @return {Boolean}       Whether the byte is a normal character
44707         */
44708  
44709  
44710        Cea608Stream.prototype.isNormalChar = function (_char) {
44711          return _char >= 0x20 && _char <= 0x7f;
44712        };
44713        /**
44714         * Configures roll-up
44715         *
44716         * @param  {Integer} pts         Current PTS
44717         * @param  {Integer} newBaseRow  Used by PACs to slide the current window to
44718         *                               a new position
44719         */
44720  
44721  
44722        Cea608Stream.prototype.setRollUp = function (pts, newBaseRow) {
44723          // Reset the base row to the bottom row when switching modes
44724          if (this.mode_ !== 'rollUp') {
44725            this.row_ = BOTTOM_ROW;
44726            this.mode_ = 'rollUp'; // Spec says to wipe memories when switching to roll-up
44727  
44728            this.flushDisplayed(pts);
44729            this.nonDisplayed_ = createDisplayBuffer();
44730            this.displayed_ = createDisplayBuffer();
44731          }
44732  
44733          if (newBaseRow !== undefined && newBaseRow !== this.row_) {
44734            // move currently displayed captions (up or down) to the new base row
44735            for (var i = 0; i < this.rollUpRows_; i++) {
44736              this.displayed_[newBaseRow - i] = this.displayed_[this.row_ - i];
44737              this.displayed_[this.row_ - i] = '';
44738            }
44739          }
44740  
44741          if (newBaseRow === undefined) {
44742            newBaseRow = this.row_;
44743          }
44744  
44745          this.topRow_ = newBaseRow - this.rollUpRows_ + 1;
44746        }; // Adds the opening HTML tag for the passed character to the caption text,
44747        // and keeps track of it for later closing
44748  
44749  
44750        Cea608Stream.prototype.addFormatting = function (pts, format) {
44751          this.formatting_ = this.formatting_.concat(format);
44752          var text = format.reduce(function (text, format) {
44753            return text + '<' + format + '>';
44754          }, '');
44755          this[this.mode_](pts, text);
44756        }; // Adds HTML closing tags for current formatting to caption text and
44757        // clears remembered formatting
44758  
44759  
44760        Cea608Stream.prototype.clearFormatting = function (pts) {
44761          if (!this.formatting_.length) {
44762            return;
44763          }
44764  
44765          var text = this.formatting_.reverse().reduce(function (text, format) {
44766            return text + '</' + format + '>';
44767          }, '');
44768          this.formatting_ = [];
44769          this[this.mode_](pts, text);
44770        }; // Mode Implementations
44771  
44772  
44773        Cea608Stream.prototype.popOn = function (pts, text) {
44774          var baseRow = this.nonDisplayed_[this.row_]; // buffer characters
44775  
44776          baseRow += text;
44777          this.nonDisplayed_[this.row_] = baseRow;
44778        };
44779  
44780        Cea608Stream.prototype.rollUp = function (pts, text) {
44781          var baseRow = this.displayed_[this.row_];
44782          baseRow += text;
44783          this.displayed_[this.row_] = baseRow;
44784        };
44785  
44786        Cea608Stream.prototype.shiftRowsUp_ = function () {
44787          var i; // clear out inactive rows
44788  
44789          for (i = 0; i < this.topRow_; i++) {
44790            this.displayed_[i] = '';
44791          }
44792  
44793          for (i = this.row_ + 1; i < BOTTOM_ROW + 1; i++) {
44794            this.displayed_[i] = '';
44795          } // shift displayed rows up
44796  
44797  
44798          for (i = this.topRow_; i < this.row_; i++) {
44799            this.displayed_[i] = this.displayed_[i + 1];
44800          } // clear out the bottom row
44801  
44802  
44803          this.displayed_[this.row_] = '';
44804        };
44805  
44806        Cea608Stream.prototype.paintOn = function (pts, text) {
44807          var baseRow = this.displayed_[this.row_];
44808          baseRow += text;
44809          this.displayed_[this.row_] = baseRow;
44810        }; // exports
44811  
44812  
44813        var captionStream = {
44814          CaptionStream: CaptionStream,
44815          Cea608Stream: Cea608Stream
44816        };
44817        /**
44818         * mux.js
44819         *
44820         * Copyright (c) Brightcove
44821         * Licensed Apache-2.0 https://github.com/videojs/mux.js/blob/master/LICENSE
44822         */
44823  
44824        var streamTypes = {
44825          H264_STREAM_TYPE: 0x1B,
44826          ADTS_STREAM_TYPE: 0x0F,
44827          METADATA_STREAM_TYPE: 0x15
44828        };
44829        var MAX_TS = 8589934592;
44830        var RO_THRESH = 4294967296;
44831        var TYPE_SHARED = 'shared';
44832  
44833        var handleRollover = function handleRollover(value, reference) {
44834          var direction = 1;
44835  
44836          if (value > reference) {
44837            // If the current timestamp value is greater than our reference timestamp and we detect a
44838            // timestamp rollover, this means the roll over is happening in the opposite direction.
44839            // Example scenario: Enter a long stream/video just after a rollover occurred. The reference
44840            // point will be set to a small number, e.g. 1. The user then seeks backwards over the
44841            // rollover point. In loading this segment, the timestamp values will be very large,
44842            // e.g. 2^33 - 1. Since this comes before the data we loaded previously, we want to adjust
44843            // the time stamp to be `value - 2^33`.
44844            direction = -1;
44845          } // Note: A seek forwards or back that is greater than the RO_THRESH (2^32, ~13 hours) will
44846          // cause an incorrect adjustment.
44847  
44848  
44849          while (Math.abs(reference - value) > RO_THRESH) {
44850            value += direction * MAX_TS;
44851          }
44852  
44853          return value;
44854        };
44855  
44856        var TimestampRolloverStream = function TimestampRolloverStream(type) {
44857          var lastDTS, referenceDTS;
44858          TimestampRolloverStream.prototype.init.call(this); // The "shared" type is used in cases where a stream will contain muxed
44859          // video and audio. We could use `undefined` here, but having a string
44860          // makes debugging a little clearer.
44861  
44862          this.type_ = type || TYPE_SHARED;
44863  
44864          this.push = function (data) {
44865            // Any "shared" rollover streams will accept _all_ data. Otherwise,
44866            // streams will only accept data that matches their type.
vendor: 12,471 bytes, lines 44867-45195
44867            if (this.type_ !== TYPE_SHARED && data.type !== this.type_) {
44868              return;
44869            }
44870  
44871            if (referenceDTS === undefined) {
44872              referenceDTS = data.dts;
44873            }
44874  
44875            data.dts = handleRollover(data.dts, referenceDTS);
44876            data.pts = handleRollover(data.pts, referenceDTS);
44877            lastDTS = data.dts;
44878            this.trigger('data', data);
44879          };
44880  
44881          this.flush = function () {
44882            referenceDTS = lastDTS;
44883            this.trigger('done');
44884          };
44885  
44886          this.endTimeline = function () {
44887            this.flush();
44888            this.trigger('endedtimeline');
44889          };
44890  
44891          this.discontinuity = function () {
44892            referenceDTS = void 0;
44893            lastDTS = void 0;
44894          };
44895  
44896          this.reset = function () {
44897            this.discontinuity();
44898            this.trigger('reset');
44899          };
44900        };
44901  
44902        TimestampRolloverStream.prototype = new stream();
44903        var timestampRolloverStream = {
44904          TimestampRolloverStream: TimestampRolloverStream,
44905          handleRollover: handleRollover
44906        };
44907  
44908        var percentEncode = function percentEncode(bytes, start, end) {
44909          var i,
44910              result = '';
44911  
44912          for (i = start; i < end; i++) {
44913            result += '%' + ('00' + bytes[i].toString(16)).slice(-2);
44914          }
44915  
44916          return result;
44917        },
44918            // return the string representation of the specified byte range,
44919        // interpreted as UTf-8.
44920        parseUtf8 = function parseUtf8(bytes, start, end) {
44921          return decodeURIComponent(percentEncode(bytes, start, end));
44922        },
44923            // return the string representation of the specified byte range,
44924        // interpreted as ISO-8859-1.
44925        parseIso88591 = function parseIso88591(bytes, start, end) {
44926          return unescape(percentEncode(bytes, start, end)); // jshint ignore:line
44927        },
44928            parseSyncSafeInteger = function parseSyncSafeInteger(data) {
44929          return data[0] << 21 | data[1] << 14 | data[2] << 7 | data[3];
44930        },
44931            tagParsers = {
44932          TXXX: function TXXX(tag) {
44933            var i;
44934  
44935            if (tag.data[0] !== 3) {
44936              // ignore frames with unrecognized character encodings
44937              return;
44938            }
44939  
44940            for (i = 1; i < tag.data.length; i++) {
44941              if (tag.data[i] === 0) {
44942                // parse the text fields
44943                tag.description = parseUtf8(tag.data, 1, i); // do not include the null terminator in the tag value
44944  
44945                tag.value = parseUtf8(tag.data, i + 1, tag.data.length).replace(/\0*$/, '');
44946                break;
44947              }
44948            }
44949  
44950            tag.data = tag.value;
44951          },
44952          WXXX: function WXXX(tag) {
44953            var i;
44954  
44955            if (tag.data[0] !== 3) {
44956              // ignore frames with unrecognized character encodings
44957              return;
44958            }
44959  
44960            for (i = 1; i < tag.data.length; i++) {
44961              if (tag.data[i] === 0) {
44962                // parse the description and URL fields
44963                tag.description = parseUtf8(tag.data, 1, i);
44964                tag.url = parseUtf8(tag.data, i + 1, tag.data.length);
44965                break;
44966              }
44967            }
44968          },
44969          PRIV: function PRIV(tag) {
44970            var i;
44971  
44972            for (i = 0; i < tag.data.length; i++) {
44973              if (tag.data[i] === 0) {
44974                // parse the description and URL fields
44975                tag.owner = parseIso88591(tag.data, 0, i);
44976                break;
44977              }
44978            }
44979  
44980            tag.privateData = tag.data.subarray(i + 1);
44981            tag.data = tag.privateData;
44982          }
44983        },
44984            _MetadataStream;
44985  
44986        _MetadataStream = function MetadataStream(options) {
44987          var settings = {
44988            debug: !!(options && options.debug),
44989            // the bytes of the program-level descriptor field in MP2T
44990            // see ISO/IEC 13818-1:2013 (E), section 2.6 "Program and
44991            // program element descriptors"
44992            descriptor: options && options.descriptor
44993          },
44994              // the total size in bytes of the ID3 tag being parsed
44995          tagSize = 0,
44996              // tag data that is not complete enough to be parsed
44997          buffer = [],
44998              // the total number of bytes currently in the buffer
44999          bufferSize = 0,
45000              i;
45001  
45002          _MetadataStream.prototype.init.call(this); // calculate the text track in-band metadata track dispatch type
45003          // https://html.spec.whatwg.org/multipage/embedded-content.html#steps-to-expose-a-media-resource-specific-text-track
45004  
45005  
45006          this.dispatchType = streamTypes.METADATA_STREAM_TYPE.toString(16);
45007  
45008          if (settings.descriptor) {
45009            for (i = 0; i < settings.descriptor.length; i++) {
45010              this.dispatchType += ('00' + settings.descriptor[i].toString(16)).slice(-2);
45011            }
45012          }
45013  
45014          this.push = function (chunk) {
45015            var tag, frameStart, frameSize, frame, i, frameHeader;
45016  
45017            if (chunk.type !== 'timed-metadata') {
45018              return;
45019            } // if data_alignment_indicator is set in the PES header,
45020            // we must have the start of a new ID3 tag. Assume anything
45021            // remaining in the buffer was malformed and throw it out
45022  
45023  
45024            if (chunk.dataAlignmentIndicator) {
45025              bufferSize = 0;
45026              buffer.length = 0;
45027            } // ignore events that don't look like ID3 data
45028  
45029  
45030            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))) {
45031              if (settings.debug) {
45032                // eslint-disable-next-line no-console
45033                console.log('Skipping unrecognized metadata packet');
45034              }
45035  
45036              return;
45037            } // add this chunk to the data we've collected so far
45038  
45039  
45040            buffer.push(chunk);
45041            bufferSize += chunk.data.byteLength; // grab the size of the entire frame from the ID3 header
45042  
45043            if (buffer.length === 1) {
45044              // the frame size is transmitted as a 28-bit integer in the
45045              // last four bytes of the ID3 header.
45046              // The most significant bit of each byte is dropped and the
45047              // results concatenated to recover the actual value.
45048              tagSize = parseSyncSafeInteger(chunk.data.subarray(6, 10)); // ID3 reports the tag size excluding the header but it's more
45049              // convenient for our comparisons to include it
45050  
45051              tagSize += 10;
45052            } // if the entire frame has not arrived, wait for more data
45053  
45054  
45055            if (bufferSize < tagSize) {
45056              return;
45057            } // collect the entire frame so it can be parsed
45058  
45059  
45060            tag = {
45061              data: new Uint8Array(tagSize),
45062              frames: [],
45063              pts: buffer[0].pts,
45064              dts: buffer[0].dts
45065            };
45066  
45067            for (i = 0; i < tagSize;) {
45068              tag.data.set(buffer[0].data.subarray(0, tagSize - i), i);
45069              i += buffer[0].data.byteLength;
45070              bufferSize -= buffer[0].data.byteLength;
45071              buffer.shift();
45072            } // find the start of the first frame and the end of the tag
45073  
45074  
45075            frameStart = 10;
45076  
45077            if (tag.data[5] & 0x40) {
45078              // advance the frame start past the extended header
45079              frameStart += 4; // header size field
45080  
45081              frameStart += parseSyncSafeInteger(tag.data.subarray(10, 14)); // clip any padding off the end
45082  
45083              tagSize -= parseSyncSafeInteger(tag.data.subarray(16, 20));
45084            } // parse one or more ID3 frames
45085            // http://id3.org/id3v2.3.0#ID3v2_frame_overview
45086  
45087  
45088            do {
45089              // determine the number of bytes in this frame
45090              frameSize = parseSyncSafeInteger(tag.data.subarray(frameStart + 4, frameStart + 8));
45091  
45092              if (frameSize < 1) {
45093                // eslint-disable-next-line no-console
45094                return console.log('Malformed ID3 frame encountered. Skipping metadata parsing.');
45095              }
45096  
45097              frameHeader = String.fromCharCode(tag.data[frameStart], tag.data[frameStart + 1], tag.data[frameStart + 2], tag.data[frameStart + 3]);
45098              frame = {
45099                id: frameHeader,
45100                data: tag.data.subarray(frameStart + 10, frameStart + frameSize + 10)
45101              };
45102              frame.key = frame.id;
45103  
45104              if (tagParsers[frame.id]) {
45105                tagParsers[frame.id](frame); // handle the special PRIV frame used to indicate the start
45106                // time for raw AAC data
45107  
45108                if (frame.owner === 'com.apple.streaming.transportStreamTimestamp') {
45109                  var d = frame.data,
45110                      size = (d[3] & 0x01) << 30 | d[4] << 22 | d[5] << 14 | d[6] << 6 | d[7] >>> 2;
45111                  size *= 4;
45112                  size += d[7] & 0x03;
45113                  frame.timeStamp = size; // in raw AAC, all subsequent data will be timestamped based
45114                  // on the value of this frame
45115                  // we couldn't have known the appropriate pts and dts before
45116                  // parsing this ID3 tag so set those values now
45117  
45118                  if (tag.pts === undefined && tag.dts === undefined) {
45119                    tag.pts = frame.timeStamp;
45120                    tag.dts = frame.timeStamp;
45121                  }
45122  
45123                  this.trigger('timestamp', frame);
45124                }
45125              }
45126  
45127              tag.frames.push(frame);
45128              frameStart += 10; // advance past the frame header
45129  
45130              frameStart += frameSize; // advance past the frame body
45131            } while (frameStart < tagSize);
45132  
45133            this.trigger('data', tag);
45134          };
45135        };
45136  
45137        _MetadataStream.prototype = new stream();
45138        var metadataStream = _MetadataStream;
45139        var TimestampRolloverStream$1 = timestampRolloverStream.TimestampRolloverStream; // object types
45140  
45141        var _TransportPacketStream, _TransportParseStream, _ElementaryStream; // constants
45142  
45143  
45144        var MP2T_PACKET_LENGTH = 188,
45145            // bytes
45146        SYNC_BYTE = 0x47;
45147        /**
45148         * Splits an incoming stream of binary data into MPEG-2 Transport
45149         * Stream packets.
45150         */
45151  
45152        _TransportPacketStream = function TransportPacketStream() {
45153          var buffer = new Uint8Array(MP2T_PACKET_LENGTH),
45154              bytesInBuffer = 0;
45155  
45156          _TransportPacketStream.prototype.init.call(this); // Deliver new bytes to the stream.
45157  
45158          /**
45159           * Split a stream of data into M2TS packets
45160          **/
45161  
45162  
45163          this.push = function (bytes) {
45164            var startIndex = 0,
45165                endIndex = MP2T_PACKET_LENGTH,
45166                everything; // If there are bytes remaining from the last segment, prepend them to the
45167            // bytes that were pushed in
45168  
45169            if (bytesInBuffer) {
45170              everything = new Uint8Array(bytes.byteLength + bytesInBuffer);
45171              everything.set(buffer.subarray(0, bytesInBuffer));
45172              everything.set(bytes, bytesInBuffer);
45173              bytesInBuffer = 0;
45174            } else {
45175              everything = bytes;
45176            } // While we have enough data for a packet
45177  
45178  
45179            while (endIndex < everything.byteLength) {
45180              // Look for a pair of start and end sync bytes in the data..
45181              if (everything[startIndex] === SYNC_BYTE && everything[endIndex] === SYNC_BYTE) {
45182                // We found a packet so emit it and jump one whole packet forward in
45183                // the stream
45184                this.trigger('data', everything.subarray(startIndex, endIndex));
45185                startIndex += MP2T_PACKET_LENGTH;
45186                endIndex += MP2T_PACKET_LENGTH;
45187                continue;
45188              } // If we get here, we have somehow become de-synchronized and we need to step
45189              // forward one byte at a time until we find a pair of sync bytes that denote
45190              // a packet
45191  
45192  
45193              startIndex++;
45194              endIndex++;
45195            } // If there was some data left over at the end of the segment that couldn't
vendor: 7,518 bytes, lines 45196-45372
45196            // possibly be a whole packet, keep it because it might be the start of a packet
45197            // that continues in the next segment
45198  
45199  
45200            if (startIndex < everything.byteLength) {
45201              buffer.set(everything.subarray(startIndex), 0);
45202              bytesInBuffer = everything.byteLength - startIndex;
45203            }
45204          };
45205          /**
45206           * Passes identified M2TS packets to the TransportParseStream to be parsed
45207          **/
45208  
45209  
45210          this.flush = function () {
45211            // If the buffer contains a whole packet when we are being flushed, emit it
45212            // and empty the buffer. Otherwise hold onto the data because it may be
45213            // important for decoding the next segment
45214            if (bytesInBuffer === MP2T_PACKET_LENGTH && buffer[0] === SYNC_BYTE) {
45215              this.trigger('data', buffer);
45216              bytesInBuffer = 0;
45217            }
45218  
45219            this.trigger('done');
45220          };
45221  
45222          this.endTimeline = function () {
45223            this.flush();
45224            this.trigger('endedtimeline');
45225          };
45226  
45227          this.reset = function () {
45228            bytesInBuffer = 0;
45229            this.trigger('reset');
45230          };
45231        };
45232  
45233        _TransportPacketStream.prototype = new stream();
45234        /**
45235         * Accepts an MP2T TransportPacketStream and emits data events with parsed
45236         * forms of the individual transport stream packets.
45237         */
45238  
45239        _TransportParseStream = function TransportParseStream() {
45240          var parsePsi, parsePat, parsePmt, self;
45241  
45242          _TransportParseStream.prototype.init.call(this);
45243  
45244          self = this;
45245          this.packetsWaitingForPmt = [];
45246          this.programMapTable = undefined;
45247  
45248          parsePsi = function parsePsi(payload, psi) {
45249            var offset = 0; // PSI packets may be split into multiple sections and those
45250            // sections may be split into multiple packets. If a PSI
45251            // section starts in this packet, the payload_unit_start_indicator
45252            // will be true and the first byte of the payload will indicate
45253            // the offset from the current position to the start of the
45254            // section.
45255  
45256            if (psi.payloadUnitStartIndicator) {
45257              offset += payload[offset] + 1;
45258            }
45259  
45260            if (psi.type === 'pat') {
45261              parsePat(payload.subarray(offset), psi);
45262            } else {
45263              parsePmt(payload.subarray(offset), psi);
45264            }
45265          };
45266  
45267          parsePat = function parsePat(payload, pat) {
45268            pat.section_number = payload[7]; // eslint-disable-line camelcase
45269  
45270            pat.last_section_number = payload[8]; // eslint-disable-line camelcase
45271            // skip the PSI header and parse the first PMT entry
45272  
45273            self.pmtPid = (payload[10] & 0x1F) << 8 | payload[11];
45274            pat.pmtPid = self.pmtPid;
45275          };
45276          /**
45277           * Parse out the relevant fields of a Program Map Table (PMT).
45278           * @param payload {Uint8Array} the PMT-specific portion of an MP2T
45279           * packet. The first byte in this array should be the table_id
45280           * field.
45281           * @param pmt {object} the object that should be decorated with
45282           * fields parsed from the PMT.
45283           */
45284  
45285  
45286          parsePmt = function parsePmt(payload, pmt) {
45287            var sectionLength, tableEnd, programInfoLength, offset; // PMTs can be sent ahead of the time when they should actually
45288            // take effect. We don't believe this should ever be the case
45289            // for HLS but we'll ignore "forward" PMT declarations if we see
45290            // them. Future PMT declarations have the current_next_indicator
45291            // set to zero.
45292  
45293            if (!(payload[5] & 0x01)) {
45294              return;
45295            } // overwrite any existing program map table
45296  
45297  
45298            self.programMapTable = {
45299              video: null,
45300              audio: null,
45301              'timed-metadata': {}
45302            }; // the mapping table ends at the end of the current section
45303  
45304            sectionLength = (payload[1] & 0x0f) << 8 | payload[2];
45305            tableEnd = 3 + sectionLength - 4; // to determine where the table is, we have to figure out how
45306            // long the program info descriptors are
45307  
45308            programInfoLength = (payload[10] & 0x0f) << 8 | payload[11]; // advance the offset to the first entry in the mapping table
45309  
45310            offset = 12 + programInfoLength;
45311  
45312            while (offset < tableEnd) {
45313              var streamType = payload[offset];
45314              var pid = (payload[offset + 1] & 0x1F) << 8 | payload[offset + 2]; // only map a single elementary_pid for audio and video stream types
45315              // TODO: should this be done for metadata too? for now maintain behavior of
45316              //       multiple metadata streams
45317  
45318              if (streamType === streamTypes.H264_STREAM_TYPE && self.programMapTable.video === null) {
45319                self.programMapTable.video = pid;
45320              } else if (streamType === streamTypes.ADTS_STREAM_TYPE && self.programMapTable.audio === null) {
45321                self.programMapTable.audio = pid;
45322              } else if (streamType === streamTypes.METADATA_STREAM_TYPE) {
45323                // map pid to stream type for metadata streams
45324                self.programMapTable['timed-metadata'][pid] = streamType;
45325              } // move to the next table entry
45326              // skip past the elementary stream descriptors, if present
45327  
45328  
45329              offset += ((payload[offset + 3] & 0x0F) << 8 | payload[offset + 4]) + 5;
45330            } // record the map on the packet as well
45331  
45332  
45333            pmt.programMapTable = self.programMapTable;
45334          };
45335          /**
45336           * Deliver a new MP2T packet to the next stream in the pipeline.
45337           */
45338  
45339  
45340          this.push = function (packet) {
45341            var result = {},
45342                offset = 4;
45343            result.payloadUnitStartIndicator = !!(packet[1] & 0x40); // pid is a 13-bit field starting at the last bit of packet[1]
45344  
45345            result.pid = packet[1] & 0x1f;
45346            result.pid <<= 8;
45347            result.pid |= packet[2]; // if an adaption field is present, its length is specified by the
45348            // fifth byte of the TS packet header. The adaptation field is
45349            // used to add stuffing to PES packets that don't fill a complete
45350            // TS packet, and to specify some forms of timing and control data
45351            // that we do not currently use.
45352  
45353            if ((packet[3] & 0x30) >>> 4 > 0x01) {
45354              offset += packet[offset] + 1;
45355            } // parse the rest of the packet based on the type
45356  
45357  
45358            if (result.pid === 0) {
45359              result.type = 'pat';
45360              parsePsi(packet.subarray(offset), result);
45361              this.trigger('data', result);
45362            } else if (result.pid === this.pmtPid) {
45363              result.type = 'pmt';
45364              parsePsi(packet.subarray(offset), result);
45365              this.trigger('data', result); // if there are any packets waiting for a PMT to be found, process them now
45366  
45367              while (this.packetsWaitingForPmt.length) {
45368                this.processPes_.apply(this, this.packetsWaitingForPmt.shift());
45369              }
45370            } else if (this.programMapTable === undefined) {
45371              // When we have not seen a PMT yet, defer further processing of
45372              // PES packets until one has been parsed
vendor: 4,086 bytes, lines 45373-45463
45373              this.packetsWaitingForPmt.push([packet, offset, result]);
45374            } else {
45375              this.processPes_(packet, offset, result);
45376            }
45377          };
45378  
45379          this.processPes_ = function (packet, offset, result) {
45380            // set the appropriate stream type
45381            if (result.pid === this.programMapTable.video) {
45382              result.streamType = streamTypes.H264_STREAM_TYPE;
45383            } else if (result.pid === this.programMapTable.audio) {
45384              result.streamType = streamTypes.ADTS_STREAM_TYPE;
45385            } else {
45386              // if not video or audio, it is timed-metadata or unknown
45387              // if unknown, streamType will be undefined
45388              result.streamType = this.programMapTable['timed-metadata'][result.pid];
45389            }
45390  
45391            result.type = 'pes';
45392            result.data = packet.subarray(offset);
45393            this.trigger('data', result);
45394          };
45395        };
45396  
45397        _TransportParseStream.prototype = new stream();
45398        _TransportParseStream.STREAM_TYPES = {
45399          h264: 0x1b,
45400          adts: 0x0f
45401        };
45402        /**
45403         * Reconsistutes program elementary stream (PES) packets from parsed
45404         * transport stream packets. That is, if you pipe an
45405         * mp2t.TransportParseStream into a mp2t.ElementaryStream, the output
45406         * events will be events which capture the bytes for individual PES
45407         * packets plus relevant metadata that has been extracted from the
45408         * container.
45409         */
45410  
45411        _ElementaryStream = function ElementaryStream() {
45412          var self = this,
45413              // PES packet fragments
45414          video = {
45415            data: [],
45416            size: 0
45417          },
45418              audio = {
45419            data: [],
45420            size: 0
45421          },
45422              timedMetadata = {
45423            data: [],
45424            size: 0
45425          },
45426              programMapTable,
45427              parsePes = function parsePes(payload, pes) {
45428            var ptsDtsFlags; // get the packet length, this will be 0 for video
45429  
45430            pes.packetLength = 6 + (payload[4] << 8 | payload[5]); // find out if this packets starts a new keyframe
45431  
45432            pes.dataAlignmentIndicator = (payload[6] & 0x04) !== 0; // PES packets may be annotated with a PTS value, or a PTS value
45433            // and a DTS value. Determine what combination of values is
45434            // available to work with.
45435  
45436            ptsDtsFlags = payload[7]; // PTS and DTS are normally stored as a 33-bit number.  Javascript
45437            // performs all bitwise operations on 32-bit integers but javascript
45438            // supports a much greater range (52-bits) of integer using standard
45439            // mathematical operations.
45440            // We construct a 31-bit value using bitwise operators over the 31
45441            // most significant bits and then multiply by 4 (equal to a left-shift
45442            // of 2) before we add the final 2 least significant bits of the
45443            // timestamp (equal to an OR.)
45444  
45445            if (ptsDtsFlags & 0xC0) {
45446              // the PTS and DTS are not written out directly. For information
45447              // on how they are encoded, see
45448              // http://dvd.sourceforge.net/dvdinfo/pes-hdr.html
45449              pes.pts = (payload[9] & 0x0E) << 27 | (payload[10] & 0xFF) << 20 | (payload[11] & 0xFE) << 12 | (payload[12] & 0xFF) << 5 | (payload[13] & 0xFE) >>> 3;
45450              pes.pts *= 4; // Left shift by 2
45451  
45452              pes.pts += (payload[13] & 0x06) >>> 1; // OR by the two LSBs
45453  
45454              pes.dts = pes.pts;
45455  
45456              if (ptsDtsFlags & 0x40) {
45457                pes.dts = (payload[14] & 0x0E) << 27 | (payload[15] & 0xFF) << 20 | (payload[16] & 0xFE) << 12 | (payload[17] & 0xFF) << 5 | (payload[18] & 0xFE) >>> 3;
45458                pes.dts *= 4; // Left shift by 2
45459  
45460                pes.dts += (payload[18] & 0x06) >>> 1; // OR by the two LSBs
45461              }
45462            } // the data section starts immediately after the PES header.
45463            // pes_header_data_length specifies the number of header bytes
vendor: 5,896 bytes, lines 45464-45631
45464            // that follow the last byte of the field.
45465  
45466  
45467            pes.data = payload.subarray(9 + payload[8]);
45468          },
45469  
45470          /**
45471            * Pass completely parsed PES packets to the next stream in the pipeline
45472           **/
45473          flushStream = function flushStream(stream$$1, type, forceFlush) {
45474            var packetData = new Uint8Array(stream$$1.size),
45475                event = {
45476              type: type
45477            },
45478                i = 0,
45479                offset = 0,
45480                packetFlushable = false,
45481                fragment; // do nothing if there is not enough buffered data for a complete
45482            // PES header
45483  
45484            if (!stream$$1.data.length || stream$$1.size < 9) {
45485              return;
45486            }
45487  
45488            event.trackId = stream$$1.data[0].pid; // reassemble the packet
45489  
45490            for (i = 0; i < stream$$1.data.length; i++) {
45491              fragment = stream$$1.data[i];
45492              packetData.set(fragment.data, offset);
45493              offset += fragment.data.byteLength;
45494            } // parse assembled packet's PES header
45495  
45496  
45497            parsePes(packetData, event); // non-video PES packets MUST have a non-zero PES_packet_length
45498            // check that there is enough stream data to fill the packet
45499  
45500            packetFlushable = type === 'video' || event.packetLength <= stream$$1.size; // flush pending packets if the conditions are right
45501  
45502            if (forceFlush || packetFlushable) {
45503              stream$$1.size = 0;
45504              stream$$1.data.length = 0;
45505            } // only emit packets that are complete. this is to avoid assembling
45506            // incomplete PES packets due to poor segmentation
45507  
45508  
45509            if (packetFlushable) {
45510              self.trigger('data', event);
45511            }
45512          };
45513  
45514          _ElementaryStream.prototype.init.call(this);
45515          /**
45516           * Identifies M2TS packet types and parses PES packets using metadata
45517           * parsed from the PMT
45518           **/
45519  
45520  
45521          this.push = function (data) {
45522            ({
45523              pat: function pat() {// we have to wait for the PMT to arrive as well before we
45524                // have any meaningful metadata
45525              },
45526              pes: function pes() {
45527                var stream$$1, streamType;
45528  
45529                switch (data.streamType) {
45530                  case streamTypes.H264_STREAM_TYPE:
45531                  case streamTypes.H264_STREAM_TYPE:
45532                    stream$$1 = video;
45533                    streamType = 'video';
45534                    break;
45535  
45536                  case streamTypes.ADTS_STREAM_TYPE:
45537                    stream$$1 = audio;
45538                    streamType = 'audio';
45539                    break;
45540  
45541                  case streamTypes.METADATA_STREAM_TYPE:
45542                    stream$$1 = timedMetadata;
45543                    streamType = 'timed-metadata';
45544                    break;
45545  
45546                  default:
45547                    // ignore unknown stream types
45548                    return;
45549                } // if a new packet is starting, we can flush the completed
45550                // packet
45551  
45552  
45553                if (data.payloadUnitStartIndicator) {
45554                  flushStream(stream$$1, streamType, true);
45555                } // buffer this fragment until we are sure we've received the
45556                // complete payload
45557  
45558  
45559                stream$$1.data.push(data);
45560                stream$$1.size += data.data.byteLength;
45561              },
45562              pmt: function pmt() {
45563                var event = {
45564                  type: 'metadata',
45565                  tracks: []
45566                };
45567                programMapTable = data.programMapTable; // translate audio and video streams to tracks
45568  
45569                if (programMapTable.video !== null) {
45570                  event.tracks.push({
45571                    timelineStartInfo: {
45572                      baseMediaDecodeTime: 0
45573                    },
45574                    id: +programMapTable.video,
45575                    codec: 'avc',
45576                    type: 'video'
45577                  });
45578                }
45579  
45580                if (programMapTable.audio !== null) {
45581                  event.tracks.push({
45582                    timelineStartInfo: {
45583                      baseMediaDecodeTime: 0
45584                    },
45585                    id: +programMapTable.audio,
45586                    codec: 'adts',
45587                    type: 'audio'
45588                  });
45589                }
45590  
45591                self.trigger('data', event);
45592              }
45593            })[data.type]();
45594          };
45595  
45596          this.reset = function () {
45597            video.size = 0;
45598            video.data.length = 0;
45599            audio.size = 0;
45600            audio.data.length = 0;
45601            this.trigger('reset');
45602          };
45603          /**
45604           * Flush any remaining input. Video PES packets may be of variable
45605           * length. Normally, the start of a new video packet can trigger the
45606           * finalization of the previous packet. That is not possible if no
45607           * more video is forthcoming, however. In that case, some other
45608           * mechanism (like the end of the file) has to be employed. When it is
45609           * clear that no additional data is forthcoming, calling this method
45610           * will flush the buffered packets.
45611           */
45612  
45613  
45614          this.flushStreams_ = function () {
45615            // !!THIS ORDER IS IMPORTANT!!
45616            // video first then audio
45617            flushStream(video, 'video');
45618            flushStream(audio, 'audio');
45619            flushStream(timedMetadata, 'timed-metadata');
45620          };
45621  
45622          this.flush = function () {
45623            this.flushStreams_();
45624            this.trigger('done');
45625          };
45626        };
45627  
45628        _ElementaryStream.prototype = new stream();
45629        var m2ts = {
45630          PAT_PID: 0x0000,
45631          MP2T_PACKET_LENGTH: MP2T_PACKET_LENGTH,
vendor: 4,688 bytes, lines 45632-45743
45632          TransportPacketStream: _TransportPacketStream,
45633          TransportParseStream: _TransportParseStream,
45634          ElementaryStream: _ElementaryStream,
45635          TimestampRolloverStream: TimestampRolloverStream$1,
45636          CaptionStream: captionStream.CaptionStream,
45637          Cea608Stream: captionStream.Cea608Stream,
45638          MetadataStream: metadataStream
45639        };
45640  
45641        for (var type in streamTypes) {
45642          if (streamTypes.hasOwnProperty(type)) {
45643            m2ts[type] = streamTypes[type];
45644          }
45645        }
45646  
45647        var m2ts_1 = m2ts;
45648        var ONE_SECOND_IN_TS$2 = clock.ONE_SECOND_IN_TS;
45649  
45650        var _AdtsStream;
45651  
45652        var ADTS_SAMPLING_FREQUENCIES = [96000, 88200, 64000, 48000, 44100, 32000, 24000, 22050, 16000, 12000, 11025, 8000, 7350];
45653        /*
45654         * Accepts a ElementaryStream and emits data events with parsed
45655         * AAC Audio Frames of the individual packets. Input audio in ADTS
45656         * format is unpacked and re-emitted as AAC frames.
45657         *
45658         * @see http://wiki.multimedia.cx/index.php?title=ADTS
45659         * @see http://wiki.multimedia.cx/?title=Understanding_AAC
45660         */
45661  
45662        _AdtsStream = function AdtsStream(handlePartialSegments) {
45663          var buffer,
45664              frameNum = 0;
45665  
45666          _AdtsStream.prototype.init.call(this);
45667  
45668          this.push = function (packet) {
45669            var i = 0,
45670                frameLength,
45671                protectionSkipBytes,
45672                frameEnd,
45673                oldBuffer,
45674                sampleCount,
45675                adtsFrameDuration;
45676  
45677            if (!handlePartialSegments) {
45678              frameNum = 0;
45679            }
45680  
45681            if (packet.type !== 'audio') {
45682              // ignore non-audio data
45683              return;
45684            } // Prepend any data in the buffer to the input data so that we can parse
45685            // aac frames the cross a PES packet boundary
45686  
45687  
45688            if (buffer) {
45689              oldBuffer = buffer;
45690              buffer = new Uint8Array(oldBuffer.byteLength + packet.data.byteLength);
45691              buffer.set(oldBuffer);
45692              buffer.set(packet.data, oldBuffer.byteLength);
45693            } else {
45694              buffer = packet.data;
45695            } // unpack any ADTS frames which have been fully received
45696            // for details on the ADTS header, see http://wiki.multimedia.cx/index.php?title=ADTS
45697  
45698  
45699            while (i + 5 < buffer.length) {
45700              // Look for the start of an ADTS header..
45701              if (buffer[i] !== 0xFF || (buffer[i + 1] & 0xF6) !== 0xF0) {
45702                // If a valid header was not found,  jump one forward and attempt to
45703                // find a valid ADTS header starting at the next byte
45704                i++;
45705                continue;
45706              } // The protection skip bit tells us if we have 2 bytes of CRC data at the
45707              // end of the ADTS header
45708  
45709  
45710              protectionSkipBytes = (~buffer[i + 1] & 0x01) * 2; // Frame length is a 13 bit integer starting 16 bits from the
45711              // end of the sync sequence
45712  
45713              frameLength = (buffer[i + 3] & 0x03) << 11 | buffer[i + 4] << 3 | (buffer[i + 5] & 0xe0) >> 5;
45714              sampleCount = ((buffer[i + 6] & 0x03) + 1) * 1024;
45715              adtsFrameDuration = sampleCount * ONE_SECOND_IN_TS$2 / ADTS_SAMPLING_FREQUENCIES[(buffer[i + 2] & 0x3c) >>> 2];
45716              frameEnd = i + frameLength; // If we don't have enough data to actually finish this ADTS frame, return
45717              // and wait for more data
45718  
45719              if (buffer.byteLength < frameEnd) {
45720                return;
45721              } // Otherwise, deliver the complete AAC frame
45722  
45723  
45724              this.trigger('data', {
45725                pts: packet.pts + frameNum * adtsFrameDuration,
45726                dts: packet.dts + frameNum * adtsFrameDuration,
45727                sampleCount: sampleCount,
45728                audioobjecttype: (buffer[i + 2] >>> 6 & 0x03) + 1,
45729                channelcount: (buffer[i + 2] & 1) << 2 | (buffer[i + 3] & 0xc0) >>> 6,
45730                samplerate: ADTS_SAMPLING_FREQUENCIES[(buffer[i + 2] & 0x3c) >>> 2],
45731                samplingfrequencyindex: (buffer[i + 2] & 0x3c) >>> 2,
45732                // assume ISO/IEC 14496-12 AudioSampleEntry default of 16
45733                samplesize: 16,
45734                data: buffer.subarray(i + 7 + protectionSkipBytes, frameEnd)
45735              });
45736              frameNum++; // If the buffer is empty, clear it and return
45737  
45738              if (buffer.byteLength === frameEnd) {
45739                buffer = undefined;
45740                return;
45741              } // Remove the finished frame from the buffer and start the process again
45742  
45743  
vendor: 4,881 bytes, lines 45744-45898
45744              buffer = buffer.subarray(frameEnd);
45745            }
45746          };
45747  
45748          this.flush = function () {
45749            frameNum = 0;
45750            this.trigger('done');
45751          };
45752  
45753          this.reset = function () {
45754            buffer = void 0;
45755            this.trigger('reset');
45756          };
45757  
45758          this.endTimeline = function () {
45759            buffer = void 0;
45760            this.trigger('endedtimeline');
45761          };
45762        };
45763  
45764        _AdtsStream.prototype = new stream();
45765        var adts = _AdtsStream;
45766        /**
45767         * mux.js
45768         *
45769         * Copyright (c) Brightcove
45770         * Licensed Apache-2.0 https://github.com/videojs/mux.js/blob/master/LICENSE
45771         */
45772  
45773        var ExpGolomb;
45774        /**
45775         * Parser for exponential Golomb codes, a variable-bitwidth number encoding
45776         * scheme used by h264.
45777         */
45778  
45779        ExpGolomb = function ExpGolomb(workingData) {
45780          var // the number of bytes left to examine in workingData
45781          workingBytesAvailable = workingData.byteLength,
45782              // the current word being examined
45783          workingWord = 0,
45784              // :uint
45785          // the number of bits left to examine in the current word
45786          workingBitsAvailable = 0; // :uint;
45787          // ():uint
45788  
45789          this.length = function () {
45790            return 8 * workingBytesAvailable;
45791          }; // ():uint
45792  
45793  
45794          this.bitsAvailable = function () {
45795            return 8 * workingBytesAvailable + workingBitsAvailable;
45796          }; // ():void
45797  
45798  
45799          this.loadWord = function () {
45800            var position = workingData.byteLength - workingBytesAvailable,
45801                workingBytes = new Uint8Array(4),
45802                availableBytes = Math.min(4, workingBytesAvailable);
45803  
45804            if (availableBytes === 0) {
45805              throw new Error('no bytes available');
45806            }
45807  
45808            workingBytes.set(workingData.subarray(position, position + availableBytes));
45809            workingWord = new DataView(workingBytes.buffer).getUint32(0); // track the amount of workingData that has been processed
45810  
45811            workingBitsAvailable = availableBytes * 8;
45812            workingBytesAvailable -= availableBytes;
45813          }; // (count:int):void
45814  
45815  
45816          this.skipBits = function (count) {
45817            var skipBytes; // :int
45818  
45819            if (workingBitsAvailable > count) {
45820              workingWord <<= count;
45821              workingBitsAvailable -= count;
45822            } else {
45823              count -= workingBitsAvailable;
45824              skipBytes = Math.floor(count / 8);
45825              count -= skipBytes * 8;
45826              workingBytesAvailable -= skipBytes;
45827              this.loadWord();
45828              workingWord <<= count;
45829              workingBitsAvailable -= count;
45830            }
45831          }; // (size:int):uint
45832  
45833  
45834          this.readBits = function (size) {
45835            var bits = Math.min(workingBitsAvailable, size),
45836                // :uint
45837            valu = workingWord >>> 32 - bits; // :uint
45838            // if size > 31, handle error
45839  
45840            workingBitsAvailable -= bits;
45841  
45842            if (workingBitsAvailable > 0) {
45843              workingWord <<= bits;
45844            } else if (workingBytesAvailable > 0) {
45845              this.loadWord();
45846            }
45847  
45848            bits = size - bits;
45849  
45850            if (bits > 0) {
45851              return valu << bits | this.readBits(bits);
45852            }
45853  
45854            return valu;
45855          }; // ():uint
45856  
45857  
45858          this.skipLeadingZeros = function () {
45859            var leadingZeroCount; // :uint
45860  
45861            for (leadingZeroCount = 0; leadingZeroCount < workingBitsAvailable; ++leadingZeroCount) {
45862              if ((workingWord & 0x80000000 >>> leadingZeroCount) !== 0) {
45863                // the first bit of working word is 1
45864                workingWord <<= leadingZeroCount;
45865                workingBitsAvailable -= leadingZeroCount;
45866                return leadingZeroCount;
45867              }
45868            } // we exhausted workingWord and still have not found a 1
45869  
45870  
45871            this.loadWord();
45872            return leadingZeroCount + this.skipLeadingZeros();
45873          }; // ():void
45874  
45875  
45876          this.skipUnsignedExpGolomb = function () {
45877            this.skipBits(1 + this.skipLeadingZeros());
45878          }; // ():void
45879  
45880  
45881          this.skipExpGolomb = function () {
45882            this.skipBits(1 + this.skipLeadingZeros());
45883          }; // ():uint
45884  
45885  
45886          this.readUnsignedExpGolomb = function () {
45887            var clz = this.skipLeadingZeros(); // :uint
45888  
45889            return this.readBits(clz + 1) - 1;
45890          }; // ():int
45891  
45892  
45893          this.readExpGolomb = function () {
45894            var valu = this.readUnsignedExpGolomb(); // :int
45895  
45896            if (0x01 & valu) {
45897              // the number is odd if the low order bit is set
45898              return 1 + valu >>> 1;
vendor: 5,389 bytes, lines 45898-46077
45898 // add 1 to make it even, and divide by 2
45899            }
45900  
45901            return -1 * (valu >>> 1); // divide by two then make it negative
45902          }; // Some convenience functions
45903          // :Boolean
45904  
45905  
45906          this.readBoolean = function () {
45907            return this.readBits(1) === 1;
45908          }; // ():int
45909  
45910  
45911          this.readUnsignedByte = function () {
45912            return this.readBits(8);
45913          };
45914  
45915          this.loadWord();
45916        };
45917  
45918        var expGolomb = ExpGolomb;
45919  
45920        var _H264Stream, _NalByteStream;
45921  
45922        var PROFILES_WITH_OPTIONAL_SPS_DATA;
45923        /**
45924         * Accepts a NAL unit byte stream and unpacks the embedded NAL units.
45925         */
45926  
45927        _NalByteStream = function NalByteStream() {
45928          var syncPoint = 0,
45929              i,
45930              buffer;
45931  
45932          _NalByteStream.prototype.init.call(this);
45933          /*
45934           * Scans a byte stream and triggers a data event with the NAL units found.
45935           * @param {Object} data Event received from H264Stream
45936           * @param {Uint8Array} data.data The h264 byte stream to be scanned
45937           *
45938           * @see H264Stream.push
45939           */
45940  
45941  
45942          this.push = function (data) {
45943            var swapBuffer;
45944  
45945            if (!buffer) {
45946              buffer = data.data;
45947            } else {
45948              swapBuffer = new Uint8Array(buffer.byteLength + data.data.byteLength);
45949              swapBuffer.set(buffer);
45950              swapBuffer.set(data.data, buffer.byteLength);
45951              buffer = swapBuffer;
45952            }
45953  
45954            var len = buffer.byteLength; // Rec. ITU-T H.264, Annex B
45955            // scan for NAL unit boundaries
45956            // a match looks like this:
45957            // 0 0 1 .. NAL .. 0 0 1
45958            // ^ sync point        ^ i
45959            // or this:
45960            // 0 0 1 .. NAL .. 0 0 0
45961            // ^ sync point        ^ i
45962            // advance the sync point to a NAL start, if necessary
45963  
45964            for (; syncPoint < len - 3; syncPoint++) {
45965              if (buffer[syncPoint + 2] === 1) {
45966                // the sync point is properly aligned
45967                i = syncPoint + 5;
45968                break;
45969              }
45970            }
45971  
45972            while (i < len) {
45973              // look at the current byte to determine if we've hit the end of
45974              // a NAL unit boundary
45975              switch (buffer[i]) {
45976                case 0:
45977                  // skip past non-sync sequences
45978                  if (buffer[i - 1] !== 0) {
45979                    i += 2;
45980                    break;
45981                  } else if (buffer[i - 2] !== 0) {
45982                    i++;
45983                    break;
45984                  } // deliver the NAL unit if it isn't empty
45985  
45986  
45987                  if (syncPoint + 3 !== i - 2) {
45988                    this.trigger('data', buffer.subarray(syncPoint + 3, i - 2));
45989                  } // drop trailing zeroes
45990  
45991  
45992                  do {
45993                    i++;
45994                  } while (buffer[i] !== 1 && i < len);
45995  
45996                  syncPoint = i - 2;
45997                  i += 3;
45998                  break;
45999  
46000                case 1:
46001                  // skip past non-sync sequences
46002                  if (buffer[i - 1] !== 0 || buffer[i - 2] !== 0) {
46003                    i += 3;
46004                    break;
46005                  } // deliver the NAL unit
46006  
46007  
46008                  this.trigger('data', buffer.subarray(syncPoint + 3, i - 2));
46009                  syncPoint = i - 2;
46010                  i += 3;
46011                  break;
46012  
46013                default:
46014                  // the current byte isn't a one or zero, so it cannot be part
46015                  // of a sync sequence
46016                  i += 3;
46017                  break;
46018              }
46019            } // filter out the NAL units that were delivered
46020  
46021  
46022            buffer = buffer.subarray(syncPoint);
46023            i -= syncPoint;
46024            syncPoint = 0;
46025          };
46026  
46027          this.reset = function () {
46028            buffer = null;
46029            syncPoint = 0;
46030            this.trigger('reset');
46031          };
46032  
46033          this.flush = function () {
46034            // deliver the last buffered NAL unit
46035            if (buffer && buffer.byteLength > 3) {
46036              this.trigger('data', buffer.subarray(syncPoint + 3));
46037            } // reset the stream state
46038  
46039  
46040            buffer = null;
46041            syncPoint = 0;
46042            this.trigger('done');
46043          };
46044  
46045          this.endTimeline = function () {
46046            this.flush();
46047            this.trigger('endedtimeline');
46048          };
46049        };
46050  
46051        _NalByteStream.prototype = new stream(); // values of profile_idc that indicate additional fields are included in the SPS
46052        // see Recommendation ITU-T H.264 (4/2013),
46053        // 7.3.2.1.1 Sequence parameter set data syntax
46054  
46055        PROFILES_WITH_OPTIONAL_SPS_DATA = {
46056          100: true,
46057          110: true,
46058          122: true,
46059          244: true,
46060          44: true,
46061          83: true,
46062          86: true,
46063          118: true,
46064          128: true,
46065          138: true,
46066          139: true,
46067          134: true
46068        };
46069        /**
46070         * Accepts input from a ElementaryStream and produces H.264 NAL unit data
46071         * events.
46072         */
46073  
46074        _H264Stream = function H264Stream() {
46075          var nalByteStream = new _NalByteStream(),
46076              self,
46077              trackId,
46078              currentPts,
46079              currentDts,
46080              discardEmulationPreventionBytes,
46081              readSequenceParameterSet,
46082              skipScalingList;
46083  
46084          _H264Stream.prototype.init.call(this);
46085  
46086          self = this;
46087          /*
46088           * Pushes a packet from a stream onto the NalByteStream
46089           *
46090           * @param {Object} packet - A packet received from a stream
46091           * @param {Uint8Array} packet.data - The raw bytes of the packet
46092           * @param {Number} packet.dts - Decode timestamp of the packet
46093           * @param {Number} packet.pts - Presentation timestamp of the packet
46094           * @param {Number} packet.trackId - The id of the h264 track this packet came from
46095           * @param {('video'|'audio')} packet.type - The type of packet
46096           *
46097           */
46098  
46099          this.push = function (packet) {
46100            if (packet.type !== 'video') {
46101              return;
46102            }
46103  
46104            trackId = packet.trackId;
46105            currentPts = packet.pts;
46106            currentDts = packet.dts;
46107            nalByteStream.push(packet);
46108          };
46109          /*
46110           * Identify NAL unit types and pass on the NALU, trackId, presentation and decode timestamps
46111           * for the NALUs to the next stream component.
46112           * Also, preprocess caption and sequence parameter NALUs.
46113           *
46114           * @param {Uint8Array} data - A NAL unit identified by `NalByteStream.push`
46115           * @see NalByteStream.push
46116           */
46117  
46118  
46119          nalByteStream.on('data', function (data) {
46120            var event = {
46121              trackId: trackId,
vendor: 12,978 bytes, lines 46122-46488
46122              pts: currentPts,
46123              dts: currentDts,
46124              data: data
46125            };
46126  
46127            switch (data[0] & 0x1f) {
46128              case 0x05:
46129                event.nalUnitType = 'slice_layer_without_partitioning_rbsp_idr';
46130                break;
46131  
46132              case 0x06:
46133                event.nalUnitType = 'sei_rbsp';
46134                event.escapedRBSP = discardEmulationPreventionBytes(data.subarray(1));
46135                break;
46136  
46137              case 0x07:
46138                event.nalUnitType = 'seq_parameter_set_rbsp';
46139                event.escapedRBSP = discardEmulationPreventionBytes(data.subarray(1));
46140                event.config = readSequenceParameterSet(event.escapedRBSP);
46141                break;
46142  
46143              case 0x08:
46144                event.nalUnitType = 'pic_parameter_set_rbsp';
46145                break;
46146  
46147              case 0x09:
46148                event.nalUnitType = 'access_unit_delimiter_rbsp';
46149                break;
46150            } // This triggers data on the H264Stream
46151  
46152  
46153            self.trigger('data', event);
46154          });
46155          nalByteStream.on('done', function () {
46156            self.trigger('done');
46157          });
46158          nalByteStream.on('partialdone', function () {
46159            self.trigger('partialdone');
46160          });
46161          nalByteStream.on('reset', function () {
46162            self.trigger('reset');
46163          });
46164          nalByteStream.on('endedtimeline', function () {
46165            self.trigger('endedtimeline');
46166          });
46167  
46168          this.flush = function () {
46169            nalByteStream.flush();
46170          };
46171  
46172          this.partialFlush = function () {
46173            nalByteStream.partialFlush();
46174          };
46175  
46176          this.reset = function () {
46177            nalByteStream.reset();
46178          };
46179  
46180          this.endTimeline = function () {
46181            nalByteStream.endTimeline();
46182          };
46183          /**
46184           * Advance the ExpGolomb decoder past a scaling list. The scaling
46185           * list is optionally transmitted as part of a sequence parameter
46186           * set and is not relevant to transmuxing.
46187           * @param count {number} the number of entries in this scaling list
46188           * @param expGolombDecoder {object} an ExpGolomb pointed to the
46189           * start of a scaling list
46190           * @see Recommendation ITU-T H.264, Section 7.3.2.1.1.1
46191           */
46192  
46193  
46194          skipScalingList = function skipScalingList(count, expGolombDecoder) {
46195            var lastScale = 8,
46196                nextScale = 8,
46197                j,
46198                deltaScale;
46199  
46200            for (j = 0; j < count; j++) {
46201              if (nextScale !== 0) {
46202                deltaScale = expGolombDecoder.readExpGolomb();
46203                nextScale = (lastScale + deltaScale + 256) % 256;
46204              }
46205  
46206              lastScale = nextScale === 0 ? lastScale : nextScale;
46207            }
46208          };
46209          /**
46210           * Expunge any "Emulation Prevention" bytes from a "Raw Byte
46211           * Sequence Payload"
46212           * @param data {Uint8Array} the bytes of a RBSP from a NAL
46213           * unit
46214           * @return {Uint8Array} the RBSP without any Emulation
46215           * Prevention Bytes
46216           */
46217  
46218  
46219          discardEmulationPreventionBytes = function discardEmulationPreventionBytes(data) {
46220            var length = data.byteLength,
46221                emulationPreventionBytesPositions = [],
46222                i = 1,
46223                newLength,
46224                newData; // Find all `Emulation Prevention Bytes`
46225  
46226            while (i < length - 2) {
46227              if (data[i] === 0 && data[i + 1] === 0 && data[i + 2] === 0x03) {
46228                emulationPreventionBytesPositions.push(i + 2);
46229                i += 2;
46230              } else {
46231                i++;
46232              }
46233            } // If no Emulation Prevention Bytes were found just return the original
46234            // array
46235  
46236  
46237            if (emulationPreventionBytesPositions.length === 0) {
46238              return data;
46239            } // Create a new array to hold the NAL unit data
46240  
46241  
46242            newLength = length - emulationPreventionBytesPositions.length;
46243            newData = new Uint8Array(newLength);
46244            var sourceIndex = 0;
46245  
46246            for (i = 0; i < newLength; sourceIndex++, i++) {
46247              if (sourceIndex === emulationPreventionBytesPositions[0]) {
46248                // Skip this byte
46249                sourceIndex++; // Remove this position index
46250  
46251                emulationPreventionBytesPositions.shift();
46252              }
46253  
46254              newData[i] = data[sourceIndex];
46255            }
46256  
46257            return newData;
46258          };
46259          /**
46260           * Read a sequence parameter set and return some interesting video
46261           * properties. A sequence parameter set is the H264 metadata that
46262           * describes the properties of upcoming video frames.
46263           * @param data {Uint8Array} the bytes of a sequence parameter set
46264           * @return {object} an object with configuration parsed from the
46265           * sequence parameter set, including the dimensions of the
46266           * associated video frames.
46267           */
46268  
46269  
46270          readSequenceParameterSet = function readSequenceParameterSet(data) {
46271            var frameCropLeftOffset = 0,
46272                frameCropRightOffset = 0,
46273                frameCropTopOffset = 0,
46274                frameCropBottomOffset = 0,
46275                sarScale = 1,
46276                expGolombDecoder,
46277                profileIdc,
46278                levelIdc,
46279                profileCompatibility,
46280                chromaFormatIdc,
46281                picOrderCntType,
46282                numRefFramesInPicOrderCntCycle,
46283                picWidthInMbsMinus1,
46284                picHeightInMapUnitsMinus1,
46285                frameMbsOnlyFlag,
46286                scalingListCount,
46287                sarRatio,
46288                aspectRatioIdc,
46289                i;
46290            expGolombDecoder = new expGolomb(data);
46291            profileIdc = expGolombDecoder.readUnsignedByte(); // profile_idc
46292  
46293            profileCompatibility = expGolombDecoder.readUnsignedByte(); // constraint_set[0-5]_flag
46294  
46295            levelIdc = expGolombDecoder.readUnsignedByte(); // level_idc u(8)
46296  
46297            expGolombDecoder.skipUnsignedExpGolomb(); // seq_parameter_set_id
46298            // some profiles have more optional data we don't need
46299  
46300            if (PROFILES_WITH_OPTIONAL_SPS_DATA[profileIdc]) {
46301              chromaFormatIdc = expGolombDecoder.readUnsignedExpGolomb();
46302  
46303              if (chromaFormatIdc === 3) {
46304                expGolombDecoder.skipBits(1); // separate_colour_plane_flag
46305              }
46306  
46307              expGolombDecoder.skipUnsignedExpGolomb(); // bit_depth_luma_minus8
46308  
46309              expGolombDecoder.skipUnsignedExpGolomb(); // bit_depth_chroma_minus8
46310  
46311              expGolombDecoder.skipBits(1); // qpprime_y_zero_transform_bypass_flag
46312  
46313              if (expGolombDecoder.readBoolean()) {
46314                // seq_scaling_matrix_present_flag
46315                scalingListCount = chromaFormatIdc !== 3 ? 8 : 12;
46316  
46317                for (i = 0; i < scalingListCount; i++) {
46318                  if (expGolombDecoder.readBoolean()) {
46319                    // seq_scaling_list_present_flag[ i ]
46320                    if (i < 6) {
46321                      skipScalingList(16, expGolombDecoder);
46322                    } else {
46323                      skipScalingList(64, expGolombDecoder);
46324                    }
46325                  }
46326                }
46327              }
46328            }
46329  
46330            expGolombDecoder.skipUnsignedExpGolomb(); // log2_max_frame_num_minus4
46331  
46332            picOrderCntType = expGolombDecoder.readUnsignedExpGolomb();
46333  
46334            if (picOrderCntType === 0) {
46335              expGolombDecoder.readUnsignedExpGolomb(); // log2_max_pic_order_cnt_lsb_minus4
46336            } else if (picOrderCntType === 1) {
46337              expGolombDecoder.skipBits(1); // delta_pic_order_always_zero_flag
46338  
46339              expGolombDecoder.skipExpGolomb(); // offset_for_non_ref_pic
46340  
46341              expGolombDecoder.skipExpGolomb(); // offset_for_top_to_bottom_field
46342  
46343              numRefFramesInPicOrderCntCycle = expGolombDecoder.readUnsignedExpGolomb();
46344  
46345              for (i = 0; i < numRefFramesInPicOrderCntCycle; i++) {
46346                expGolombDecoder.skipExpGolomb(); // offset_for_ref_frame[ i ]
46347              }
46348            }
46349  
46350            expGolombDecoder.skipUnsignedExpGolomb(); // max_num_ref_frames
46351  
46352            expGolombDecoder.skipBits(1); // gaps_in_frame_num_value_allowed_flag
46353  
46354            picWidthInMbsMinus1 = expGolombDecoder.readUnsignedExpGolomb();
46355            picHeightInMapUnitsMinus1 = expGolombDecoder.readUnsignedExpGolomb();
46356            frameMbsOnlyFlag = expGolombDecoder.readBits(1);
46357  
46358            if (frameMbsOnlyFlag === 0) {
46359              expGolombDecoder.skipBits(1); // mb_adaptive_frame_field_flag
46360            }
46361  
46362            expGolombDecoder.skipBits(1); // direct_8x8_inference_flag
46363  
46364            if (expGolombDecoder.readBoolean()) {
46365              // frame_cropping_flag
46366              frameCropLeftOffset = expGolombDecoder.readUnsignedExpGolomb();
46367              frameCropRightOffset = expGolombDecoder.readUnsignedExpGolomb();
46368              frameCropTopOffset = expGolombDecoder.readUnsignedExpGolomb();
46369              frameCropBottomOffset = expGolombDecoder.readUnsignedExpGolomb();
46370            }
46371  
46372            if (expGolombDecoder.readBoolean()) {
46373              // vui_parameters_present_flag
46374              if (expGolombDecoder.readBoolean()) {
46375                // aspect_ratio_info_present_flag
46376                aspectRatioIdc = expGolombDecoder.readUnsignedByte();
46377  
46378                switch (aspectRatioIdc) {
46379                  case 1:
46380                    sarRatio = [1, 1];
46381                    break;
46382  
46383                  case 2:
46384                    sarRatio = [12, 11];
46385                    break;
46386  
46387                  case 3:
46388                    sarRatio = [10, 11];
46389                    break;
46390  
46391                  case 4:
46392                    sarRatio = [16, 11];
46393                    break;
46394  
46395                  case 5:
46396                    sarRatio = [40, 33];
46397                    break;
46398  
46399                  case 6:
46400                    sarRatio = [24, 11];
46401                    break;
46402  
46403                  case 7:
46404                    sarRatio = [20, 11];
46405                    break;
46406  
46407                  case 8:
46408                    sarRatio = [32, 11];
46409                    break;
46410  
46411                  case 9:
46412                    sarRatio = [80, 33];
46413                    break;
46414  
46415                  case 10:
46416                    sarRatio = [18, 11];
46417                    break;
46418  
46419                  case 11:
46420                    sarRatio = [15, 11];
46421                    break;
46422  
46423                  case 12:
46424                    sarRatio = [64, 33];
46425                    break;
46426  
46427                  case 13:
46428                    sarRatio = [160, 99];
46429                    break;
46430  
46431                  case 14:
46432                    sarRatio = [4, 3];
46433                    break;
46434  
46435                  case 15:
46436                    sarRatio = [3, 2];
46437                    break;
46438  
46439                  case 16:
46440                    sarRatio = [2, 1];
46441                    break;
46442  
46443                  case 255:
46444                    {
46445                      sarRatio = [expGolombDecoder.readUnsignedByte() << 8 | expGolombDecoder.readUnsignedByte(), expGolombDecoder.readUnsignedByte() << 8 | expGolombDecoder.readUnsignedByte()];
46446                      break;
46447                    }
46448                }
46449  
46450                if (sarRatio) {
46451                  sarScale = sarRatio[0] / sarRatio[1];
46452                }
46453              }
46454            }
46455  
46456            return {
46457              profileIdc: profileIdc,
46458              levelIdc: levelIdc,
46459              profileCompatibility: profileCompatibility,
46460              width: Math.ceil(((picWidthInMbsMinus1 + 1) * 16 - frameCropLeftOffset * 2 - frameCropRightOffset * 2) * sarScale),
46461              height: (2 - frameMbsOnlyFlag) * (picHeightInMapUnitsMinus1 + 1) * 16 - frameCropTopOffset * 2 - frameCropBottomOffset * 2,
46462              sarRatio: sarRatio
46463            };
46464          };
46465        };
46466  
46467        _H264Stream.prototype = new stream();
46468        var h264 = {
46469          H264Stream: _H264Stream,
46470          NalByteStream: _NalByteStream
46471        };
46472        /**
46473         * mux.js
46474         *
46475         * Copyright (c) Brightcove
46476         * Licensed Apache-2.0 https://github.com/videojs/mux.js/blob/master/LICENSE
46477         *
46478         * Utilities to detect basic properties and metadata about Aac data.
46479         */
46480  
46481        var ADTS_SAMPLING_FREQUENCIES$1 = [96000, 88200, 64000, 48000, 44100, 32000, 24000, 22050, 16000, 12000, 11025, 8000, 7350];
46482  
46483        var isLikelyAacData = function isLikelyAacData(data) {
46484          if (data[0] === 'I'.charCodeAt(0) && data[1] === 'D'.charCodeAt(0) && data[2] === '3'.charCodeAt(0)) {
46485            return true;
46486          }
46487  
46488          return false;
vendor: 7,716 bytes, lines 46489-46695
46489        };
46490  
46491        var parseSyncSafeInteger$1 = function parseSyncSafeInteger(data) {
46492          return data[0] << 21 | data[1] << 14 | data[2] << 7 | data[3];
46493        }; // return a percent-encoded representation of the specified byte range
46494        // @see http://en.wikipedia.org/wiki/Percent-encoding
46495  
46496  
46497        var percentEncode$1 = function percentEncode(bytes, start, end) {
46498          var i,
46499              result = '';
46500  
46501          for (i = start; i < end; i++) {
46502            result += '%' + ('00' + bytes[i].toString(16)).slice(-2);
46503          }
46504  
46505          return result;
46506        }; // return the string representation of the specified byte range,
46507        // interpreted as ISO-8859-1.
46508  
46509  
46510        var parseIso88591$1 = function parseIso88591(bytes, start, end) {
46511          return unescape(percentEncode$1(bytes, start, end)); // jshint ignore:line
46512        };
46513  
46514        var parseId3TagSize = function parseId3TagSize(header, byteIndex) {
46515          var returnSize = header[byteIndex + 6] << 21 | header[byteIndex + 7] << 14 | header[byteIndex + 8] << 7 | header[byteIndex + 9],
46516              flags = header[byteIndex + 5],
46517              footerPresent = (flags & 16) >> 4;
46518  
46519          if (footerPresent) {
46520            return returnSize + 20;
46521          }
46522  
46523          return returnSize + 10;
46524        };
46525  
46526        var parseAdtsSize = function parseAdtsSize(header, byteIndex) {
46527          var lowThree = (header[byteIndex + 5] & 0xE0) >> 5,
46528              middle = header[byteIndex + 4] << 3,
46529              highTwo = header[byteIndex + 3] & 0x3 << 11;
46530          return highTwo | middle | lowThree;
46531        };
46532  
46533        var parseType = function parseType(header, byteIndex) {
46534          if (header[byteIndex] === 'I'.charCodeAt(0) && header[byteIndex + 1] === 'D'.charCodeAt(0) && header[byteIndex + 2] === '3'.charCodeAt(0)) {
46535            return 'timed-metadata';
46536          } else if (header[byteIndex] & 0xff === 0xff && (header[byteIndex + 1] & 0xf0) === 0xf0) {
46537            return 'audio';
46538          }
46539  
46540          return null;
46541        };
46542  
46543        var parseSampleRate = function parseSampleRate(packet) {
46544          var i = 0;
46545  
46546          while (i + 5 < packet.length) {
46547            if (packet[i] !== 0xFF || (packet[i + 1] & 0xF6) !== 0xF0) {
46548              // If a valid header was not found,  jump one forward and attempt to
46549              // find a valid ADTS header starting at the next byte
46550              i++;
46551              continue;
46552            }
46553  
46554            return ADTS_SAMPLING_FREQUENCIES$1[(packet[i + 2] & 0x3c) >>> 2];
46555          }
46556  
46557          return null;
46558        };
46559  
46560        var parseAacTimestamp = function parseAacTimestamp(packet) {
46561          var frameStart, frameSize, frame, frameHeader; // find the start of the first frame and the end of the tag
46562  
46563          frameStart = 10;
46564  
46565          if (packet[5] & 0x40) {
46566            // advance the frame start past the extended header
46567            frameStart += 4; // header size field
46568  
46569            frameStart += parseSyncSafeInteger$1(packet.subarray(10, 14));
46570          } // parse one or more ID3 frames
46571          // http://id3.org/id3v2.3.0#ID3v2_frame_overview
46572  
46573  
46574          do {
46575            // determine the number of bytes in this frame
46576            frameSize = parseSyncSafeInteger$1(packet.subarray(frameStart + 4, frameStart + 8));
46577  
46578            if (frameSize < 1) {
46579              return null;
46580            }
46581  
46582            frameHeader = String.fromCharCode(packet[frameStart], packet[frameStart + 1], packet[frameStart + 2], packet[frameStart + 3]);
46583  
46584            if (frameHeader === 'PRIV') {
46585              frame = packet.subarray(frameStart + 10, frameStart + frameSize + 10);
46586  
46587              for (var i = 0; i < frame.byteLength; i++) {
46588                if (frame[i] === 0) {
46589                  var owner = parseIso88591$1(frame, 0, i);
46590  
46591                  if (owner === 'com.apple.streaming.transportStreamTimestamp') {
46592                    var d = frame.subarray(i + 1);
46593                    var size = (d[3] & 0x01) << 30 | d[4] << 22 | d[5] << 14 | d[6] << 6 | d[7] >>> 2;
46594                    size *= 4;
46595                    size += d[7] & 0x03;
46596                    return size;
46597                  }
46598  
46599                  break;
46600                }
46601              }
46602            }
46603  
46604            frameStart += 10; // advance past the frame header
46605  
46606            frameStart += frameSize; // advance past the frame body
46607          } while (frameStart < packet.byteLength);
46608  
46609          return null;
46610        };
46611  
46612        var utils = {
46613          isLikelyAacData: isLikelyAacData,
46614          parseId3TagSize: parseId3TagSize,
46615          parseAdtsSize: parseAdtsSize,
46616          parseType: parseType,
46617          parseSampleRate: parseSampleRate,
46618          parseAacTimestamp: parseAacTimestamp
46619        }; // Constants
46620  
46621        var _AacStream;
46622        /**
46623         * Splits an incoming stream of binary data into ADTS and ID3 Frames.
46624         */
46625  
46626  
46627        _AacStream = function AacStream() {
46628          var everything = new Uint8Array(),
46629              timeStamp = 0;
46630  
46631          _AacStream.prototype.init.call(this);
46632  
46633          this.setTimestamp = function (timestamp) {
46634            timeStamp = timestamp;
46635          };
46636  
46637          this.push = function (bytes) {
46638            var frameSize = 0,
46639                byteIndex = 0,
46640                bytesLeft,
46641                chunk,
46642                packet,
46643                tempLength; // If there are bytes remaining from the last segment, prepend them to the
46644            // bytes that were pushed in
46645  
46646            if (everything.length) {
46647              tempLength = everything.length;
46648              everything = new Uint8Array(bytes.byteLength + tempLength);
46649              everything.set(everything.subarray(0, tempLength));
46650              everything.set(bytes, tempLength);
46651            } else {
46652              everything = bytes;
46653            }
46654  
46655            while (everything.length - byteIndex >= 3) {
46656              if (everything[byteIndex] === 'I'.charCodeAt(0) && everything[byteIndex + 1] === 'D'.charCodeAt(0) && everything[byteIndex + 2] === '3'.charCodeAt(0)) {
46657                // Exit early because we don't have enough to parse
46658                // the ID3 tag header
46659                if (everything.length - byteIndex < 10) {
46660                  break;
46661                } // check framesize
46662  
46663  
46664                frameSize = utils.parseId3TagSize(everything, byteIndex); // Exit early if we don't have enough in the buffer
46665                // to emit a full packet
46666                // Add to byteIndex to support multiple ID3 tags in sequence
46667  
46668                if (byteIndex + frameSize > everything.length) {
46669                  break;
46670                }
46671  
46672                chunk = {
46673                  type: 'timed-metadata',
46674                  data: everything.subarray(byteIndex, byteIndex + frameSize)
46675                };
46676                this.trigger('data', chunk);
46677                byteIndex += frameSize;
46678                continue;
46679              } else if ((everything[byteIndex] & 0xff) === 0xff && (everything[byteIndex + 1] & 0xf0) === 0xf0) {
46680                // Exit early because we don't have enough to parse
46681                // the ADTS frame header
46682                if (everything.length - byteIndex < 7) {
46683                  break;
46684                }
46685  
46686                frameSize = utils.parseAdtsSize(everything, byteIndex); // Exit early if we don't have enough in the buffer
46687                // to emit a full packet
46688  
46689                if (byteIndex + frameSize > everything.length) {
46690                  break;
46691                }
46692  
46693                packet = {
46694                  type: 'audio',
46695                  data: everything.subarray(byteIndex, byteIndex + frameSize),
vendor: 10,030 bytes, lines 46696-46956
46696                  pts: timeStamp,
46697                  dts: timeStamp
46698                };
46699                this.trigger('data', packet);
46700                byteIndex += frameSize;
46701                continue;
46702              }
46703  
46704              byteIndex++;
46705            }
46706  
46707            bytesLeft = everything.length - byteIndex;
46708  
46709            if (bytesLeft > 0) {
46710              everything = everything.subarray(byteIndex);
46711            } else {
46712              everything = new Uint8Array();
46713            }
46714          };
46715  
46716          this.reset = function () {
46717            everything = new Uint8Array();
46718            this.trigger('reset');
46719          };
46720  
46721          this.endTimeline = function () {
46722            everything = new Uint8Array();
46723            this.trigger('endedtimeline');
46724          };
46725        };
46726  
46727        _AacStream.prototype = new stream();
46728        var aac = _AacStream;
46729        var H264Stream = h264.H264Stream;
46730        var isLikelyAacData$1 = utils.isLikelyAacData;
46731        var ONE_SECOND_IN_TS$3 = clock.ONE_SECOND_IN_TS; // constants
46732  
46733        var AUDIO_PROPERTIES = ['audioobjecttype', 'channelcount', 'samplerate', 'samplingfrequencyindex', 'samplesize'];
46734        var VIDEO_PROPERTIES = ['width', 'height', 'profileIdc', 'levelIdc', 'profileCompatibility', 'sarRatio']; // object types
46735  
46736        var _VideoSegmentStream, _AudioSegmentStream, _Transmuxer, _CoalesceStream;
46737        /**
46738         * Compare two arrays (even typed) for same-ness
46739         */
46740  
46741  
46742        var arrayEquals = function arrayEquals(a, b) {
46743          var i;
46744  
46745          if (a.length !== b.length) {
46746            return false;
46747          } // compare the value of each element in the array
46748  
46749  
46750          for (i = 0; i < a.length; i++) {
46751            if (a[i] !== b[i]) {
46752              return false;
46753            }
46754          }
46755  
46756          return true;
46757        };
46758  
46759        var generateVideoSegmentTimingInfo = function generateVideoSegmentTimingInfo(baseMediaDecodeTime, startDts, startPts, endDts, endPts, prependedContentDuration) {
46760          var ptsOffsetFromDts = startPts - startDts,
46761              decodeDuration = endDts - startDts,
46762              presentationDuration = endPts - startPts; // The PTS and DTS values are based on the actual stream times from the segment,
46763          // however, the player time values will reflect a start from the baseMediaDecodeTime.
46764          // In order to provide relevant values for the player times, base timing info on the
46765          // baseMediaDecodeTime and the DTS and PTS durations of the segment.
46766  
46767          return {
46768            start: {
46769              dts: baseMediaDecodeTime,
46770              pts: baseMediaDecodeTime + ptsOffsetFromDts
46771            },
46772            end: {
46773              dts: baseMediaDecodeTime + decodeDuration,
46774              pts: baseMediaDecodeTime + presentationDuration
46775            },
46776            prependedContentDuration: prependedContentDuration,
46777            baseMediaDecodeTime: baseMediaDecodeTime
46778          };
46779        };
46780        /**
46781         * Constructs a single-track, ISO BMFF media segment from AAC data
46782         * events. The output of this stream can be fed to a SourceBuffer
46783         * configured with a suitable initialization segment.
46784         * @param track {object} track metadata configuration
46785         * @param options {object} transmuxer options object
46786         * @param options.keepOriginalTimestamps {boolean} If true, keep the timestamps
46787         *        in the source; false to adjust the first segment to start at 0.
46788         */
46789  
46790  
46791        _AudioSegmentStream = function AudioSegmentStream(track, options) {
46792          var adtsFrames = [],
46793              sequenceNumber = 0,
46794              earliestAllowedDts = 0,
46795              audioAppendStartTs = 0,
46796              videoBaseMediaDecodeTime = Infinity;
46797          options = options || {};
46798  
46799          _AudioSegmentStream.prototype.init.call(this);
46800  
46801          this.push = function (data) {
46802            trackDecodeInfo.collectDtsInfo(track, data);
46803  
46804            if (track) {
46805              AUDIO_PROPERTIES.forEach(function (prop) {
46806                track[prop] = data[prop];
46807              });
46808            } // buffer audio data until end() is called
46809  
46810  
46811            adtsFrames.push(data);
46812          };
46813  
46814          this.setEarliestDts = function (earliestDts) {
46815            earliestAllowedDts = earliestDts - track.timelineStartInfo.baseMediaDecodeTime;
46816          };
46817  
46818          this.setVideoBaseMediaDecodeTime = function (baseMediaDecodeTime) {
46819            videoBaseMediaDecodeTime = baseMediaDecodeTime;
46820          };
46821  
46822          this.setAudioAppendStart = function (timestamp) {
46823            audioAppendStartTs = timestamp;
46824          };
46825  
46826          this.flush = function () {
46827            var frames, moof, mdat, boxes, frameDuration; // return early if no audio data has been observed
46828  
46829            if (adtsFrames.length === 0) {
46830              this.trigger('done', 'AudioSegmentStream');
46831              return;
46832            }
46833  
46834            frames = audioFrameUtils.trimAdtsFramesByEarliestDts(adtsFrames, track, earliestAllowedDts);
46835            track.baseMediaDecodeTime = trackDecodeInfo.calculateTrackBaseMediaDecodeTime(track, options.keepOriginalTimestamps);
46836            audioFrameUtils.prefixWithSilence(track, frames, audioAppendStartTs, videoBaseMediaDecodeTime); // we have to build the index from byte locations to
46837            // samples (that is, adts frames) in the audio data
46838  
46839            track.samples = audioFrameUtils.generateSampleTable(frames); // concatenate the audio data to constuct the mdat
46840  
46841            mdat = mp4Generator.mdat(audioFrameUtils.concatenateFrameData(frames));
46842            adtsFrames = [];
46843            moof = mp4Generator.moof(sequenceNumber, [track]);
46844            boxes = new Uint8Array(moof.byteLength + mdat.byteLength); // bump the sequence number for next time
46845  
46846            sequenceNumber++;
46847            boxes.set(moof);
46848            boxes.set(mdat, moof.byteLength);
46849            trackDecodeInfo.clearDtsInfo(track);
46850            frameDuration = Math.ceil(ONE_SECOND_IN_TS$3 * 1024 / track.samplerate); // TODO this check was added to maintain backwards compatibility (particularly with
46851            // tests) on adding the timingInfo event. However, it seems unlikely that there's a
46852            // valid use-case where an init segment/data should be triggered without associated
46853            // frames. Leaving for now, but should be looked into.
46854  
46855            if (frames.length) {
46856              this.trigger('timingInfo', {
46857                start: frames[0].pts,
46858                end: frames[0].pts + frames.length * frameDuration
46859              });
46860            }
46861  
46862            this.trigger('data', {
46863              track: track,
46864              boxes: boxes
46865            });
46866            this.trigger('done', 'AudioSegmentStream');
46867          };
46868  
46869          this.reset = function () {
46870            trackDecodeInfo.clearDtsInfo(track);
46871            adtsFrames = [];
46872            this.trigger('reset');
46873          };
46874        };
46875  
46876        _AudioSegmentStream.prototype = new stream();
46877        /**
46878         * Constructs a single-track, ISO BMFF media segment from H264 data
46879         * events. The output of this stream can be fed to a SourceBuffer
46880         * configured with a suitable initialization segment.
46881         * @param track {object} track metadata configuration
46882         * @param options {object} transmuxer options object
46883         * @param options.alignGopsAtEnd {boolean} If true, start from the end of the
46884         *        gopsToAlignWith list when attempting to align gop pts
46885         * @param options.keepOriginalTimestamps {boolean} If true, keep the timestamps
46886         *        in the source; false to adjust the first segment to start at 0.
46887         */
46888  
46889        _VideoSegmentStream = function VideoSegmentStream(track, options) {
46890          var sequenceNumber = 0,
46891              nalUnits = [],
46892              gopsToAlignWith = [],
46893              config,
46894              pps;
46895          options = options || {};
46896  
46897          _VideoSegmentStream.prototype.init.call(this);
46898  
46899          delete track.minPTS;
46900          this.gopCache_ = [];
46901          /**
46902            * Constructs a ISO BMFF segment given H264 nalUnits
46903            * @param {Object} nalUnit A data event representing a nalUnit
46904            * @param {String} nalUnit.nalUnitType
46905            * @param {Object} nalUnit.config Properties for a mp4 track
46906            * @param {Uint8Array} nalUnit.data The nalUnit bytes
46907            * @see lib/codecs/h264.js
46908           **/
46909  
46910          this.push = function (nalUnit) {
46911            trackDecodeInfo.collectDtsInfo(track, nalUnit); // record the track config
46912  
46913            if (nalUnit.nalUnitType === 'seq_parameter_set_rbsp' && !config) {
46914              config = nalUnit.config;
46915              track.sps = [nalUnit.data];
46916              VIDEO_PROPERTIES.forEach(function (prop) {
46917                track[prop] = config[prop];
46918              }, this);
46919            }
46920  
46921            if (nalUnit.nalUnitType === 'pic_parameter_set_rbsp' && !pps) {
46922              pps = nalUnit.data;
46923              track.pps = [nalUnit.data];
46924            } // buffer video until flush() is called
46925  
46926  
46927            nalUnits.push(nalUnit);
46928          };
46929          /**
46930            * Pass constructed ISO BMFF track and boxes on to the
46931            * next stream in the pipeline
46932           **/
46933  
46934  
46935          this.flush = function () {
46936            var frames,
46937                gopForFusion,
46938                gops,
46939                moof,
46940                mdat,
46941                boxes,
46942                prependedContentDuration = 0,
46943                firstGop,
46944                lastGop; // Throw away nalUnits at the start of the byte stream until
46945            // we find the first AUD
46946  
46947            while (nalUnits.length) {
46948              if (nalUnits[0].nalUnitType === 'access_unit_delimiter_rbsp') {
46949                break;
46950              }
46951  
46952              nalUnits.shift();
46953            } // Return early if no video data has been observed
46954  
46955  
46956            if (nalUnits.length === 0) {
vendor: 10,988 bytes, lines 46957-47212
46957              this.resetStream_();
46958              this.trigger('done', 'VideoSegmentStream');
46959              return;
46960            } // Organize the raw nal-units into arrays that represent
46961            // higher-level constructs such as frames and gops
46962            // (group-of-pictures)
46963  
46964  
46965            frames = frameUtils.groupNalsIntoFrames(nalUnits);
46966            gops = frameUtils.groupFramesIntoGops(frames); // If the first frame of this fragment is not a keyframe we have
46967            // a problem since MSE (on Chrome) requires a leading keyframe.
46968            //
46969            // We have two approaches to repairing this situation:
46970            // 1) GOP-FUSION:
46971            //    This is where we keep track of the GOPS (group-of-pictures)
46972            //    from previous fragments and attempt to find one that we can
46973            //    prepend to the current fragment in order to create a valid
46974            //    fragment.
46975            // 2) KEYFRAME-PULLING:
46976            //    Here we search for the first keyframe in the fragment and
46977            //    throw away all the frames between the start of the fragment
46978            //    and that keyframe. We then extend the duration and pull the
46979            //    PTS of the keyframe forward so that it covers the time range
46980            //    of the frames that were disposed of.
46981            //
46982            // #1 is far prefereable over #2 which can cause "stuttering" but
46983            // requires more things to be just right.
46984  
46985            if (!gops[0][0].keyFrame) {
46986              // Search for a gop for fusion from our gopCache
46987              gopForFusion = this.getGopForFusion_(nalUnits[0], track);
46988  
46989              if (gopForFusion) {
46990                // in order to provide more accurate timing information about the segment, save
46991                // the number of seconds prepended to the original segment due to GOP fusion
46992                prependedContentDuration = gopForFusion.duration;
46993                gops.unshift(gopForFusion); // Adjust Gops' metadata to account for the inclusion of the
46994                // new gop at the beginning
46995  
46996                gops.byteLength += gopForFusion.byteLength;
46997                gops.nalCount += gopForFusion.nalCount;
46998                gops.pts = gopForFusion.pts;
46999                gops.dts = gopForFusion.dts;
47000                gops.duration += gopForFusion.duration;
47001              } else {
47002                // If we didn't find a candidate gop fall back to keyframe-pulling
47003                gops = frameUtils.extendFirstKeyFrame(gops);
47004              }
47005            } // Trim gops to align with gopsToAlignWith
47006  
47007  
47008            if (gopsToAlignWith.length) {
47009              var alignedGops;
47010  
47011              if (options.alignGopsAtEnd) {
47012                alignedGops = this.alignGopsAtEnd_(gops);
47013              } else {
47014                alignedGops = this.alignGopsAtStart_(gops);
47015              }
47016  
47017              if (!alignedGops) {
47018                // save all the nals in the last GOP into the gop cache
47019                this.gopCache_.unshift({
47020                  gop: gops.pop(),
47021                  pps: track.pps,
47022                  sps: track.sps
47023                }); // Keep a maximum of 6 GOPs in the cache
47024  
47025                this.gopCache_.length = Math.min(6, this.gopCache_.length); // Clear nalUnits
47026  
47027                nalUnits = []; // return early no gops can be aligned with desired gopsToAlignWith
47028  
47029                this.resetStream_();
47030                this.trigger('done', 'VideoSegmentStream');
47031                return;
47032              } // Some gops were trimmed. clear dts info so minSegmentDts and pts are correct
47033              // when recalculated before sending off to CoalesceStream
47034  
47035  
47036              trackDecodeInfo.clearDtsInfo(track);
47037              gops = alignedGops;
47038            }
47039  
47040            trackDecodeInfo.collectDtsInfo(track, gops); // First, we have to build the index from byte locations to
47041            // samples (that is, frames) in the video data
47042  
47043            track.samples = frameUtils.generateSampleTable(gops); // Concatenate the video data and construct the mdat
47044  
47045            mdat = mp4Generator.mdat(frameUtils.concatenateNalData(gops));
47046            track.baseMediaDecodeTime = trackDecodeInfo.calculateTrackBaseMediaDecodeTime(track, options.keepOriginalTimestamps);
47047            this.trigger('processedGopsInfo', gops.map(function (gop) {
47048              return {
47049                pts: gop.pts,
47050                dts: gop.dts,
47051                byteLength: gop.byteLength
47052              };
47053            }));
47054            firstGop = gops[0];
47055            lastGop = gops[gops.length - 1];
47056            this.trigger('segmentTimingInfo', generateVideoSegmentTimingInfo(track.baseMediaDecodeTime, firstGop.dts, firstGop.pts, lastGop.dts + lastGop.duration, lastGop.pts + lastGop.duration, prependedContentDuration));
47057            this.trigger('timingInfo', {
47058              start: gops[0].pts,
47059              end: gops[gops.length - 1].pts + gops[gops.length - 1].duration
47060            }); // save all the nals in the last GOP into the gop cache
47061  
47062            this.gopCache_.unshift({
47063              gop: gops.pop(),
47064              pps: track.pps,
47065              sps: track.sps
47066            }); // Keep a maximum of 6 GOPs in the cache
47067  
47068            this.gopCache_.length = Math.min(6, this.gopCache_.length); // Clear nalUnits
47069  
47070            nalUnits = [];
47071            this.trigger('baseMediaDecodeTime', track.baseMediaDecodeTime);
47072            this.trigger('timelineStartInfo', track.timelineStartInfo);
47073            moof = mp4Generator.moof(sequenceNumber, [track]); // it would be great to allocate this array up front instead of
47074            // throwing away hundreds of media segment fragments
47075  
47076            boxes = new Uint8Array(moof.byteLength + mdat.byteLength); // Bump the sequence number for next time
47077  
47078            sequenceNumber++;
47079            boxes.set(moof);
47080            boxes.set(mdat, moof.byteLength);
47081            this.trigger('data', {
47082              track: track,
47083              boxes: boxes
47084            });
47085            this.resetStream_(); // Continue with the flush process now
47086  
47087            this.trigger('done', 'VideoSegmentStream');
47088          };
47089  
47090          this.reset = function () {
47091            this.resetStream_();
47092            nalUnits = [];
47093            this.gopCache_.length = 0;
47094            gopsToAlignWith.length = 0;
47095            this.trigger('reset');
47096          };
47097  
47098          this.resetStream_ = function () {
47099            trackDecodeInfo.clearDtsInfo(track); // reset config and pps because they may differ across segments
47100            // for instance, when we are rendition switching
47101  
47102            config = undefined;
47103            pps = undefined;
47104          }; // Search for a candidate Gop for gop-fusion from the gop cache and
47105          // return it or return null if no good candidate was found
47106  
47107  
47108          this.getGopForFusion_ = function (nalUnit) {
47109            var halfSecond = 45000,
47110                // Half-a-second in a 90khz clock
47111            allowableOverlap = 10000,
47112                // About 3 frames @ 30fps
47113            nearestDistance = Infinity,
47114                dtsDistance,
47115                nearestGopObj,
47116                currentGop,
47117                currentGopObj,
47118                i; // Search for the GOP nearest to the beginning of this nal unit
47119  
47120            for (i = 0; i < this.gopCache_.length; i++) {
47121              currentGopObj = this.gopCache_[i];
47122              currentGop = currentGopObj.gop; // Reject Gops with different SPS or PPS
47123  
47124              if (!(track.pps && arrayEquals(track.pps[0], currentGopObj.pps[0])) || !(track.sps && arrayEquals(track.sps[0], currentGopObj.sps[0]))) {
47125                continue;
47126              } // Reject Gops that would require a negative baseMediaDecodeTime
47127  
47128  
47129              if (currentGop.dts < track.timelineStartInfo.dts) {
47130                continue;
47131              } // The distance between the end of the gop and the start of the nalUnit
47132  
47133  
47134              dtsDistance = nalUnit.dts - currentGop.dts - currentGop.duration; // Only consider GOPS that start before the nal unit and end within
47135              // a half-second of the nal unit
47136  
47137              if (dtsDistance >= -allowableOverlap && dtsDistance <= halfSecond) {
47138                // Always use the closest GOP we found if there is more than
47139                // one candidate
47140                if (!nearestGopObj || nearestDistance > dtsDistance) {
47141                  nearestGopObj = currentGopObj;
47142                  nearestDistance = dtsDistance;
47143                }
47144              }
47145            }
47146  
47147            if (nearestGopObj) {
47148              return nearestGopObj.gop;
47149            }
47150  
47151            return null;
47152          }; // trim gop list to the first gop found that has a matching pts with a gop in the list
47153          // of gopsToAlignWith starting from the START of the list
47154  
47155  
47156          this.alignGopsAtStart_ = function (gops) {
47157            var alignIndex, gopIndex, align, gop, byteLength, nalCount, duration, alignedGops;
47158            byteLength = gops.byteLength;
47159            nalCount = gops.nalCount;
47160            duration = gops.duration;
47161            alignIndex = gopIndex = 0;
47162  
47163            while (alignIndex < gopsToAlignWith.length && gopIndex < gops.length) {
47164              align = gopsToAlignWith[alignIndex];
47165              gop = gops[gopIndex];
47166  
47167              if (align.pts === gop.pts) {
47168                break;
47169              }
47170  
47171              if (gop.pts > align.pts) {
47172                // this current gop starts after the current gop we want to align on, so increment
47173                // align index
47174                alignIndex++;
47175                continue;
47176              } // current gop starts before the current gop we want to align on. so increment gop
47177              // index
47178  
47179  
47180              gopIndex++;
47181              byteLength -= gop.byteLength;
47182              nalCount -= gop.nalCount;
47183              duration -= gop.duration;
47184            }
47185  
47186            if (gopIndex === 0) {
47187              // no gops to trim
47188              return gops;
47189            }
47190  
47191            if (gopIndex === gops.length) {
47192              // all gops trimmed, skip appending all gops
47193              return null;
47194            }
47195  
47196            alignedGops = gops.slice(gopIndex);
47197            alignedGops.byteLength = byteLength;
47198            alignedGops.duration = duration;
47199            alignedGops.nalCount = nalCount;
47200            alignedGops.pts = alignedGops[0].pts;
47201            alignedGops.dts = alignedGops[0].dts;
47202            return alignedGops;
47203          }; // trim gop list to the first gop found that has a matching pts with a gop in the list
47204          // of gopsToAlignWith starting from the END of the list
47205  
47206  
47207          this.alignGopsAtEnd_ = function (gops) {
47208            var alignIndex, gopIndex, align, gop, alignEndIndex, matchFound;
47209            alignIndex = gopsToAlignWith.length - 1;
47210            gopIndex = gops.length - 1;
47211            alignEndIndex = null;
47212            matchFound = false;
vendor: 4,591 bytes, lines 47213-47341
47213  
47214            while (alignIndex >= 0 && gopIndex >= 0) {
47215              align = gopsToAlignWith[alignIndex];
47216              gop = gops[gopIndex];
47217  
47218              if (align.pts === gop.pts) {
47219                matchFound = true;
47220                break;
47221              }
47222  
47223              if (align.pts > gop.pts) {
47224                alignIndex--;
47225                continue;
47226              }
47227  
47228              if (alignIndex === gopsToAlignWith.length - 1) {
47229                // gop.pts is greater than the last alignment candidate. If no match is found
47230                // by the end of this loop, we still want to append gops that come after this
47231                // point
47232                alignEndIndex = gopIndex;
47233              }
47234  
47235              gopIndex--;
47236            }
47237  
47238            if (!matchFound && alignEndIndex === null) {
47239              return null;
47240            }
47241  
47242            var trimIndex;
47243  
47244            if (matchFound) {
47245              trimIndex = gopIndex;
47246            } else {
47247              trimIndex = alignEndIndex;
47248            }
47249  
47250            if (trimIndex === 0) {
47251              return gops;
47252            }
47253  
47254            var alignedGops = gops.slice(trimIndex);
47255            var metadata = alignedGops.reduce(function (total, gop) {
47256              total.byteLength += gop.byteLength;
47257              total.duration += gop.duration;
47258              total.nalCount += gop.nalCount;
47259              return total;
47260            }, {
47261              byteLength: 0,
47262              duration: 0,
47263              nalCount: 0
47264            });
47265            alignedGops.byteLength = metadata.byteLength;
47266            alignedGops.duration = metadata.duration;
47267            alignedGops.nalCount = metadata.nalCount;
47268            alignedGops.pts = alignedGops[0].pts;
47269            alignedGops.dts = alignedGops[0].dts;
47270            return alignedGops;
47271          };
47272  
47273          this.alignGopsWith = function (newGopsToAlignWith) {
47274            gopsToAlignWith = newGopsToAlignWith;
47275          };
47276        };
47277  
47278        _VideoSegmentStream.prototype = new stream();
47279        /**
47280         * A Stream that can combine multiple streams (ie. audio & video)
47281         * into a single output segment for MSE. Also supports audio-only
47282         * and video-only streams.
47283         * @param options {object} transmuxer options object
47284         * @param options.keepOriginalTimestamps {boolean} If true, keep the timestamps
47285         *        in the source; false to adjust the first segment to start at media timeline start.
47286         */
47287  
47288        _CoalesceStream = function CoalesceStream(options, metadataStream) {
47289          // Number of Tracks per output segment
47290          // If greater than 1, we combine multiple
47291          // tracks into a single segment
47292          this.numberOfTracks = 0;
47293          this.metadataStream = metadataStream;
47294          options = options || {};
47295  
47296          if (typeof options.remux !== 'undefined') {
47297            this.remuxTracks = !!options.remux;
47298          } else {
47299            this.remuxTracks = true;
47300          }
47301  
47302          if (typeof options.keepOriginalTimestamps === 'boolean') {
47303            this.keepOriginalTimestamps = options.keepOriginalTimestamps;
47304          } else {
47305            this.keepOriginalTimestamps = false;
47306          }
47307  
47308          this.pendingTracks = [];
47309          this.videoTrack = null;
47310          this.pendingBoxes = [];
47311          this.pendingCaptions = [];
47312          this.pendingMetadata = [];
47313          this.pendingBytes = 0;
47314          this.emittedTracks = 0;
47315  
47316          _CoalesceStream.prototype.init.call(this); // Take output from multiple
47317  
47318  
47319          this.push = function (output) {
47320            // buffer incoming captions until the associated video segment
47321            // finishes
47322            if (output.text) {
47323              return this.pendingCaptions.push(output);
47324            } // buffer incoming id3 tags until the final flush
47325  
47326  
47327            if (output.frames) {
47328              return this.pendingMetadata.push(output);
47329            } // Add this track to the list of pending tracks and store
47330            // important information required for the construction of
47331            // the final segment
47332  
47333  
47334            this.pendingTracks.push(output.track);
47335            this.pendingBytes += output.boxes.byteLength; // TODO: is there an issue for this against chrome?
47336            // We unshift audio and push video because
47337            // as of Chrome 75 when switching from
47338            // one init segment to another if the video
47339            // mdat does not appear after the audio mdat
47340            // only audio will play for the duration of our transmux.
47341  
vendor: 18,043 bytes, lines 47342-47729
47342            if (output.track.type === 'video') {
47343              this.videoTrack = output.track;
47344              this.pendingBoxes.push(output.boxes);
47345            }
47346  
47347            if (output.track.type === 'audio') {
47348              this.audioTrack = output.track;
47349              this.pendingBoxes.unshift(output.boxes);
47350            }
47351          };
47352        };
47353  
47354        _CoalesceStream.prototype = new stream();
47355  
47356        _CoalesceStream.prototype.flush = function (flushSource) {
47357          var offset = 0,
47358              event = {
47359            captions: [],
47360            captionStreams: {},
47361            metadata: [],
47362            info: {}
47363          },
47364              caption,
47365              id3,
47366              initSegment,
47367              timelineStartPts = 0,
47368              i;
47369  
47370          if (this.pendingTracks.length < this.numberOfTracks) {
47371            if (flushSource !== 'VideoSegmentStream' && flushSource !== 'AudioSegmentStream') {
47372              // Return because we haven't received a flush from a data-generating
47373              // portion of the segment (meaning that we have only recieved meta-data
47374              // or captions.)
47375              return;
47376            } else if (this.remuxTracks) {
47377              // Return until we have enough tracks from the pipeline to remux (if we
47378              // are remuxing audio and video into a single MP4)
47379              return;
47380            } else if (this.pendingTracks.length === 0) {
47381              // In the case where we receive a flush without any data having been
47382              // received we consider it an emitted track for the purposes of coalescing
47383              // `done` events.
47384              // We do this for the case where there is an audio and video track in the
47385              // segment but no audio data. (seen in several playlists with alternate
47386              // audio tracks and no audio present in the main TS segments.)
47387              this.emittedTracks++;
47388  
47389              if (this.emittedTracks >= this.numberOfTracks) {
47390                this.trigger('done');
47391                this.emittedTracks = 0;
47392              }
47393  
47394              return;
47395            }
47396          }
47397  
47398          if (this.videoTrack) {
47399            timelineStartPts = this.videoTrack.timelineStartInfo.pts;
47400            VIDEO_PROPERTIES.forEach(function (prop) {
47401              event.info[prop] = this.videoTrack[prop];
47402            }, this);
47403          } else if (this.audioTrack) {
47404            timelineStartPts = this.audioTrack.timelineStartInfo.pts;
47405            AUDIO_PROPERTIES.forEach(function (prop) {
47406              event.info[prop] = this.audioTrack[prop];
47407            }, this);
47408          }
47409  
47410          if (this.videoTrack || this.audioTrack) {
47411            if (this.pendingTracks.length === 1) {
47412              event.type = this.pendingTracks[0].type;
47413            } else {
47414              event.type = 'combined';
47415            }
47416  
47417            this.emittedTracks += this.pendingTracks.length;
47418            initSegment = mp4Generator.initSegment(this.pendingTracks); // Create a new typed array to hold the init segment
47419  
47420            event.initSegment = new Uint8Array(initSegment.byteLength); // Create an init segment containing a moov
47421            // and track definitions
47422  
47423            event.initSegment.set(initSegment); // Create a new typed array to hold the moof+mdats
47424  
47425            event.data = new Uint8Array(this.pendingBytes); // Append each moof+mdat (one per track) together
47426  
47427            for (i = 0; i < this.pendingBoxes.length; i++) {
47428              event.data.set(this.pendingBoxes[i], offset);
47429              offset += this.pendingBoxes[i].byteLength;
47430            } // Translate caption PTS times into second offsets to match the
47431            // video timeline for the segment, and add track info
47432  
47433  
47434            for (i = 0; i < this.pendingCaptions.length; i++) {
47435              caption = this.pendingCaptions[i];
47436              caption.startTime = clock.metadataTsToSeconds(caption.startPts, timelineStartPts, this.keepOriginalTimestamps);
47437              caption.endTime = clock.metadataTsToSeconds(caption.endPts, timelineStartPts, this.keepOriginalTimestamps);
47438              event.captionStreams[caption.stream] = true;
47439              event.captions.push(caption);
47440            } // Translate ID3 frame PTS times into second offsets to match the
47441            // video timeline for the segment
47442  
47443  
47444            for (i = 0; i < this.pendingMetadata.length; i++) {
47445              id3 = this.pendingMetadata[i];
47446              id3.cueTime = clock.metadataTsToSeconds(id3.pts, timelineStartPts, this.keepOriginalTimestamps);
47447              event.metadata.push(id3);
47448            } // We add this to every single emitted segment even though we only need
47449            // it for the first
47450  
47451  
47452            event.metadata.dispatchType = this.metadataStream.dispatchType; // Reset stream state
47453  
47454            this.pendingTracks.length = 0;
47455            this.videoTrack = null;
47456            this.pendingBoxes.length = 0;
47457            this.pendingCaptions.length = 0;
47458            this.pendingBytes = 0;
47459            this.pendingMetadata.length = 0; // Emit the built segment
47460            // We include captions and ID3 tags for backwards compatibility,
47461            // ideally we should send only video and audio in the data event
47462  
47463            this.trigger('data', event); // Emit each caption to the outside world
47464            // Ideally, this would happen immediately on parsing captions,
47465            // but we need to ensure that video data is sent back first
47466            // so that caption timing can be adjusted to match video timing
47467  
47468            for (i = 0; i < event.captions.length; i++) {
47469              caption = event.captions[i];
47470              this.trigger('caption', caption);
47471            } // Emit each id3 tag to the outside world
47472            // Ideally, this would happen immediately on parsing the tag,
47473            // but we need to ensure that video data is sent back first
47474            // so that ID3 frame timing can be adjusted to match video timing
47475  
47476  
47477            for (i = 0; i < event.metadata.length; i++) {
47478              id3 = event.metadata[i];
47479              this.trigger('id3Frame', id3);
47480            }
47481          } // Only emit `done` if all tracks have been flushed and emitted
47482  
47483  
47484          if (this.emittedTracks >= this.numberOfTracks) {
47485            this.trigger('done');
47486            this.emittedTracks = 0;
47487          }
47488        };
47489  
47490        _CoalesceStream.prototype.setRemux = function (val) {
47491          this.remuxTracks = val;
47492        };
47493        /**
47494         * A Stream that expects MP2T binary data as input and produces
47495         * corresponding media segments, suitable for use with Media Source
47496         * Extension (MSE) implementations that support the ISO BMFF byte
47497         * stream format, like Chrome.
47498         */
47499  
47500  
47501        _Transmuxer = function Transmuxer(options) {
47502          var self = this,
47503              hasFlushed = true,
47504              videoTrack,
47505              audioTrack;
47506  
47507          _Transmuxer.prototype.init.call(this);
47508  
47509          options = options || {};
47510          this.baseMediaDecodeTime = options.baseMediaDecodeTime || 0;
47511          this.transmuxPipeline_ = {};
47512  
47513          this.setupAacPipeline = function () {
47514            var pipeline = {};
47515            this.transmuxPipeline_ = pipeline;
47516            pipeline.type = 'aac';
47517            pipeline.metadataStream = new m2ts_1.MetadataStream(); // set up the parsing pipeline
47518  
47519            pipeline.aacStream = new aac();
47520            pipeline.audioTimestampRolloverStream = new m2ts_1.TimestampRolloverStream('audio');
47521            pipeline.timedMetadataTimestampRolloverStream = new m2ts_1.TimestampRolloverStream('timed-metadata');
47522            pipeline.adtsStream = new adts();
47523            pipeline.coalesceStream = new _CoalesceStream(options, pipeline.metadataStream);
47524            pipeline.headOfPipeline = pipeline.aacStream;
47525            pipeline.aacStream.pipe(pipeline.audioTimestampRolloverStream).pipe(pipeline.adtsStream);
47526            pipeline.aacStream.pipe(pipeline.timedMetadataTimestampRolloverStream).pipe(pipeline.metadataStream).pipe(pipeline.coalesceStream);
47527            pipeline.metadataStream.on('timestamp', function (frame) {
47528              pipeline.aacStream.setTimestamp(frame.timeStamp);
47529            });
47530            pipeline.aacStream.on('data', function (data) {
47531              if (data.type === 'timed-metadata' && !pipeline.audioSegmentStream) {
47532                audioTrack = audioTrack || {
47533                  timelineStartInfo: {
47534                    baseMediaDecodeTime: self.baseMediaDecodeTime
47535                  },
47536                  codec: 'adts',
47537                  type: 'audio'
47538                }; // hook up the audio segment stream to the first track with aac data
47539  
47540                pipeline.coalesceStream.numberOfTracks++;
47541                pipeline.audioSegmentStream = new _AudioSegmentStream(audioTrack, options);
47542                pipeline.audioSegmentStream.on('timingInfo', self.trigger.bind(self, 'audioTimingInfo')); // Set up the final part of the audio pipeline
47543  
47544                pipeline.adtsStream.pipe(pipeline.audioSegmentStream).pipe(pipeline.coalesceStream);
47545              } // emit pmt info
47546  
47547  
47548              self.trigger('trackinfo', {
47549                hasAudio: !!audioTrack,
47550                hasVideo: !!videoTrack
47551              });
47552            }); // Re-emit any data coming from the coalesce stream to the outside world
47553  
47554            pipeline.coalesceStream.on('data', this.trigger.bind(this, 'data')); // Let the consumer know we have finished flushing the entire pipeline
47555  
47556            pipeline.coalesceStream.on('done', this.trigger.bind(this, 'done'));
47557          };
47558  
47559          this.setupTsPipeline = function () {
47560            var pipeline = {};
47561            this.transmuxPipeline_ = pipeline;
47562            pipeline.type = 'ts';
47563            pipeline.metadataStream = new m2ts_1.MetadataStream(); // set up the parsing pipeline
47564  
47565            pipeline.packetStream = new m2ts_1.TransportPacketStream();
47566            pipeline.parseStream = new m2ts_1.TransportParseStream();
47567            pipeline.elementaryStream = new m2ts_1.ElementaryStream();
47568            pipeline.timestampRolloverStream = new m2ts_1.TimestampRolloverStream();
47569            pipeline.adtsStream = new adts();
47570            pipeline.h264Stream = new H264Stream();
47571            pipeline.captionStream = new m2ts_1.CaptionStream();
47572            pipeline.coalesceStream = new _CoalesceStream(options, pipeline.metadataStream);
47573            pipeline.headOfPipeline = pipeline.packetStream; // disassemble MPEG2-TS packets into elementary streams
47574  
47575            pipeline.packetStream.pipe(pipeline.parseStream).pipe(pipeline.elementaryStream).pipe(pipeline.timestampRolloverStream); // !!THIS ORDER IS IMPORTANT!!
47576            // demux the streams
47577  
47578            pipeline.timestampRolloverStream.pipe(pipeline.h264Stream);
47579            pipeline.timestampRolloverStream.pipe(pipeline.adtsStream);
47580            pipeline.timestampRolloverStream.pipe(pipeline.metadataStream).pipe(pipeline.coalesceStream); // Hook up CEA-608/708 caption stream
47581  
47582            pipeline.h264Stream.pipe(pipeline.captionStream).pipe(pipeline.coalesceStream);
47583            pipeline.elementaryStream.on('data', function (data) {
47584              var i;
47585  
47586              if (data.type === 'metadata') {
47587                i = data.tracks.length; // scan the tracks listed in the metadata
47588  
47589                while (i--) {
47590                  if (!videoTrack && data.tracks[i].type === 'video') {
47591                    videoTrack = data.tracks[i];
47592                    videoTrack.timelineStartInfo.baseMediaDecodeTime = self.baseMediaDecodeTime;
47593                  } else if (!audioTrack && data.tracks[i].type === 'audio') {
47594                    audioTrack = data.tracks[i];
47595                    audioTrack.timelineStartInfo.baseMediaDecodeTime = self.baseMediaDecodeTime;
47596                  }
47597                } // hook up the video segment stream to the first track with h264 data
47598  
47599  
47600                if (videoTrack && !pipeline.videoSegmentStream) {
47601                  pipeline.coalesceStream.numberOfTracks++;
47602                  pipeline.videoSegmentStream = new _VideoSegmentStream(videoTrack, options);
47603                  pipeline.videoSegmentStream.on('timelineStartInfo', function (timelineStartInfo) {
47604                    // When video emits timelineStartInfo data after a flush, we forward that
47605                    // info to the AudioSegmentStream, if it exists, because video timeline
47606                    // data takes precedence.
47607                    if (audioTrack) {
47608                      audioTrack.timelineStartInfo = timelineStartInfo; // On the first segment we trim AAC frames that exist before the
47609                      // very earliest DTS we have seen in video because Chrome will
47610                      // interpret any video track with a baseMediaDecodeTime that is
47611                      // non-zero as a gap.
47612  
47613                      pipeline.audioSegmentStream.setEarliestDts(timelineStartInfo.dts);
47614                    }
47615                  });
47616                  pipeline.videoSegmentStream.on('processedGopsInfo', self.trigger.bind(self, 'gopInfo'));
47617                  pipeline.videoSegmentStream.on('segmentTimingInfo', self.trigger.bind(self, 'videoSegmentTimingInfo'));
47618                  pipeline.videoSegmentStream.on('baseMediaDecodeTime', function (baseMediaDecodeTime) {
47619                    if (audioTrack) {
47620                      pipeline.audioSegmentStream.setVideoBaseMediaDecodeTime(baseMediaDecodeTime);
47621                    }
47622                  });
47623                  pipeline.videoSegmentStream.on('timingInfo', self.trigger.bind(self, 'videoTimingInfo')); // Set up the final part of the video pipeline
47624  
47625                  pipeline.h264Stream.pipe(pipeline.videoSegmentStream).pipe(pipeline.coalesceStream);
47626                }
47627  
47628                if (audioTrack && !pipeline.audioSegmentStream) {
47629                  // hook up the audio segment stream to the first track with aac data
47630                  pipeline.coalesceStream.numberOfTracks++;
47631                  pipeline.audioSegmentStream = new _AudioSegmentStream(audioTrack, options);
47632                  pipeline.audioSegmentStream.on('timingInfo', self.trigger.bind(self, 'audioTimingInfo')); // Set up the final part of the audio pipeline
47633  
47634                  pipeline.adtsStream.pipe(pipeline.audioSegmentStream).pipe(pipeline.coalesceStream);
47635                } // emit pmt info
47636  
47637  
47638                self.trigger('trackinfo', {
47639                  hasAudio: !!audioTrack,
47640                  hasVideo: !!videoTrack
47641                });
47642              }
47643            }); // Re-emit any data coming from the coalesce stream to the outside world
47644  
47645            pipeline.coalesceStream.on('data', this.trigger.bind(this, 'data'));
47646            pipeline.coalesceStream.on('id3Frame', function (id3Frame) {
47647              id3Frame.dispatchType = pipeline.metadataStream.dispatchType;
47648              self.trigger('id3Frame', id3Frame);
47649            });
47650            pipeline.coalesceStream.on('caption', this.trigger.bind(this, 'caption')); // Let the consumer know we have finished flushing the entire pipeline
47651  
47652            pipeline.coalesceStream.on('done', this.trigger.bind(this, 'done'));
47653          }; // hook up the segment streams once track metadata is delivered
47654  
47655  
47656          this.setBaseMediaDecodeTime = function (baseMediaDecodeTime) {
47657            var pipeline = this.transmuxPipeline_;
47658  
47659            if (!options.keepOriginalTimestamps) {
47660              this.baseMediaDecodeTime = baseMediaDecodeTime;
47661            }
47662  
47663            if (audioTrack) {
47664              audioTrack.timelineStartInfo.dts = undefined;
47665              audioTrack.timelineStartInfo.pts = undefined;
47666              trackDecodeInfo.clearDtsInfo(audioTrack);
47667  
47668              if (!options.keepOriginalTimestamps) {
47669                audioTrack.timelineStartInfo.baseMediaDecodeTime = baseMediaDecodeTime;
47670              }
47671  
47672              if (pipeline.audioTimestampRolloverStream) {
47673                pipeline.audioTimestampRolloverStream.discontinuity();
47674              }
47675            }
47676  
47677            if (videoTrack) {
47678              if (pipeline.videoSegmentStream) {
47679                pipeline.videoSegmentStream.gopCache_ = [];
47680              }
47681  
47682              videoTrack.timelineStartInfo.dts = undefined;
47683              videoTrack.timelineStartInfo.pts = undefined;
47684              trackDecodeInfo.clearDtsInfo(videoTrack);
47685              pipeline.captionStream.reset();
47686  
47687              if (!options.keepOriginalTimestamps) {
47688                videoTrack.timelineStartInfo.baseMediaDecodeTime = baseMediaDecodeTime;
47689              }
47690            }
47691  
47692            if (pipeline.timestampRolloverStream) {
47693              pipeline.timestampRolloverStream.discontinuity();
47694            }
47695          };
47696  
47697          this.setAudioAppendStart = function (timestamp) {
47698            if (audioTrack) {
47699              this.transmuxPipeline_.audioSegmentStream.setAudioAppendStart(timestamp);
47700            }
47701          };
47702  
47703          this.setRemux = function (val) {
47704            var pipeline = this.transmuxPipeline_;
47705            options.remux = val;
47706  
47707            if (pipeline && pipeline.coalesceStream) {
47708              pipeline.coalesceStream.setRemux(val);
47709            }
47710          };
47711  
47712          this.alignGopsWith = function (gopsToAlignWith) {
47713            if (videoTrack && this.transmuxPipeline_.videoSegmentStream) {
47714              this.transmuxPipeline_.videoSegmentStream.alignGopsWith(gopsToAlignWith);
47715            }
47716          }; // feed incoming data to the front of the parsing pipeline
47717  
47718  
47719          this.push = function (data) {
47720            if (hasFlushed) {
47721              var isAac = isLikelyAacData$1(data);
47722  
47723              if (isAac && this.transmuxPipeline_.type !== 'aac') {
47724                this.setupAacPipeline();
47725              } else if (!isAac && this.transmuxPipeline_.type !== 'ts') {
47726                this.setupTsPipeline();
47727              }
47728  
47729              hasFlushed = false;
vendor: 35,594 bytes, lines 47730-48759
47730            }
47731  
47732            this.transmuxPipeline_.headOfPipeline.push(data);
47733          }; // flush any buffered data
47734  
47735  
47736          this.flush = function () {
47737            hasFlushed = true; // Start at the top of the pipeline and flush all pending work
47738  
47739            this.transmuxPipeline_.headOfPipeline.flush();
47740          };
47741  
47742          this.endTimeline = function () {
47743            this.transmuxPipeline_.headOfPipeline.endTimeline();
47744          };
47745  
47746          this.reset = function () {
47747            if (this.transmuxPipeline_.headOfPipeline) {
47748              this.transmuxPipeline_.headOfPipeline.reset();
47749            }
47750          }; // Caption data has to be reset when seeking outside buffered range
47751  
47752  
47753          this.resetCaptions = function () {
47754            if (this.transmuxPipeline_.captionStream) {
47755              this.transmuxPipeline_.captionStream.reset();
47756            }
47757          };
47758        };
47759  
47760        _Transmuxer.prototype = new stream();
47761        var transmuxer = {
47762          Transmuxer: _Transmuxer,
47763          VideoSegmentStream: _VideoSegmentStream,
47764          AudioSegmentStream: _AudioSegmentStream,
47765          AUDIO_PROPERTIES: AUDIO_PROPERTIES,
47766          VIDEO_PROPERTIES: VIDEO_PROPERTIES,
47767          // exported for testing
47768          generateVideoSegmentTimingInfo: generateVideoSegmentTimingInfo
47769        };
47770  
47771        var classCallCheck = function classCallCheck(instance, Constructor) {
47772          if (!(instance instanceof Constructor)) {
47773            throw new TypeError("Cannot call a class as a function");
47774          }
47775        };
47776  
47777        var createClass = function () {
47778          function defineProperties(target, props) {
47779            for (var i = 0; i < props.length; i++) {
47780              var descriptor = props[i];
47781              descriptor.enumerable = descriptor.enumerable || false;
47782              descriptor.configurable = true;
47783              if ("value" in descriptor) descriptor.writable = true;
47784              Object.defineProperty(target, descriptor.key, descriptor);
47785            }
47786          }
47787  
47788          return function (Constructor, protoProps, staticProps) {
47789            if (protoProps) defineProperties(Constructor.prototype, protoProps);
47790            if (staticProps) defineProperties(Constructor, staticProps);
47791            return Constructor;
47792          };
47793        }();
47794        /**
47795         * @file transmuxer-worker.js
47796         */
47797  
47798        /**
47799         * Re-emits transmuxer events by converting them into messages to the
47800         * world outside the worker.
47801         *
47802         * @param {Object} transmuxer the transmuxer to wire events on
47803         * @private
47804         */
47805  
47806  
47807        var wireTransmuxerEvents = function wireTransmuxerEvents(self, transmuxer$$1) {
47808          transmuxer$$1.on('data', function (segment) {
47809            // transfer ownership of the underlying ArrayBuffer
47810            // instead of doing a copy to save memory
47811            // ArrayBuffers are transferable but generic TypedArrays are not
47812            // @link https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API/Using_web_workers#Passing_data_by_transferring_ownership_(transferable_objects)
47813            var initArray = segment.initSegment;
47814            segment.initSegment = {
47815              data: initArray.buffer,
47816              byteOffset: initArray.byteOffset,
47817              byteLength: initArray.byteLength
47818            };
47819            var typedArray = segment.data;
47820            segment.data = typedArray.buffer;
47821            self.postMessage({
47822              action: 'data',
47823              segment: segment,
47824              byteOffset: typedArray.byteOffset,
47825              byteLength: typedArray.byteLength
47826            }, [segment.data]);
47827          });
47828  
47829          if (transmuxer$$1.captionStream) {
47830            transmuxer$$1.captionStream.on('data', function (caption) {
47831              self.postMessage({
47832                action: 'caption',
47833                data: caption
47834              });
47835            });
47836          }
47837  
47838          transmuxer$$1.on('done', function (data) {
47839            self.postMessage({
47840              action: 'done'
47841            });
47842          });
47843          transmuxer$$1.on('gopInfo', function (gopInfo) {
47844            self.postMessage({
47845              action: 'gopInfo',
47846              gopInfo: gopInfo
47847            });
47848          });
47849          transmuxer$$1.on('videoSegmentTimingInfo', function (videoSegmentTimingInfo) {
47850            self.postMessage({
47851              action: 'videoSegmentTimingInfo',
47852              videoSegmentTimingInfo: videoSegmentTimingInfo
47853            });
47854          });
47855        };
47856        /**
47857         * All incoming messages route through this hash. If no function exists
47858         * to handle an incoming message, then we ignore the message.
47859         *
47860         * @class MessageHandlers
47861         * @param {Object} options the options to initialize with
47862         */
47863  
47864  
47865        var MessageHandlers = function () {
47866          function MessageHandlers(self, options) {
47867            classCallCheck(this, MessageHandlers);
47868            this.options = options || {};
47869            this.self = self;
47870            this.init();
47871          }
47872          /**
47873           * initialize our web worker and wire all the events.
47874           */
47875  
47876  
47877          createClass(MessageHandlers, [{
47878            key: 'init',
47879            value: function init() {
47880              if (this.transmuxer) {
47881                this.transmuxer.dispose();
47882              }
47883  
47884              this.transmuxer = new transmuxer.Transmuxer(this.options);
47885              wireTransmuxerEvents(this.self, this.transmuxer);
47886            }
47887            /**
47888             * Adds data (a ts segment) to the start of the transmuxer pipeline for
47889             * processing.
47890             *
47891             * @param {ArrayBuffer} data data to push into the muxer
47892             */
47893  
47894          }, {
47895            key: 'push',
47896            value: function push(data) {
47897              // Cast array buffer to correct type for transmuxer
47898              var segment = new Uint8Array(data.data, data.byteOffset, data.byteLength);
47899              this.transmuxer.push(segment);
47900            }
47901            /**
47902             * Recreate the transmuxer so that the next segment added via `push`
47903             * start with a fresh transmuxer.
47904             */
47905  
47906          }, {
47907            key: 'reset',
47908            value: function reset() {
47909              this.init();
47910            }
47911            /**
47912             * Set the value that will be used as the `baseMediaDecodeTime` time for the
47913             * next segment pushed in. Subsequent segments will have their `baseMediaDecodeTime`
47914             * set relative to the first based on the PTS values.
47915             *
47916             * @param {Object} data used to set the timestamp offset in the muxer
47917             */
47918  
47919          }, {
47920            key: 'setTimestampOffset',
47921            value: function setTimestampOffset(data) {
47922              var timestampOffset = data.timestampOffset || 0;
47923              this.transmuxer.setBaseMediaDecodeTime(Math.round(timestampOffset * 90000));
47924            }
47925          }, {
47926            key: 'setAudioAppendStart',
47927            value: function setAudioAppendStart(data) {
47928              this.transmuxer.setAudioAppendStart(Math.ceil(data.appendStart * 90000));
47929            }
47930            /**
47931             * Forces the pipeline to finish processing the last segment and emit it's
47932             * results.
47933             *
47934             * @param {Object} data event data, not really used
47935             */
47936  
47937          }, {
47938            key: 'flush',
47939            value: function flush(data) {
47940              this.transmuxer.flush();
47941            }
47942          }, {
47943            key: 'resetCaptions',
47944            value: function resetCaptions() {
47945              this.transmuxer.resetCaptions();
47946            }
47947          }, {
47948            key: 'alignGopsWith',
47949            value: function alignGopsWith(data) {
47950              this.transmuxer.alignGopsWith(data.gopsToAlignWith.slice());
47951            }
47952          }]);
47953          return MessageHandlers;
47954        }();
47955        /**
47956         * Our web wroker interface so that things can talk to mux.js
47957         * that will be running in a web worker. the scope is passed to this by
47958         * webworkify.
47959         *
47960         * @param {Object} self the scope for the web worker
47961         */
47962  
47963  
47964        var TransmuxerWorker = function TransmuxerWorker(self) {
47965          self.onmessage = function (event) {
47966            if (event.data.action === 'init' && event.data.options) {
47967              this.messageHandlers = new MessageHandlers(self, event.data.options);
47968              return;
47969            }
47970  
47971            if (!this.messageHandlers) {
47972              this.messageHandlers = new MessageHandlers(self);
47973            }
47974  
47975            if (event.data && event.data.action && event.data.action !== 'init') {
47976              if (this.messageHandlers[event.data.action]) {
47977                this.messageHandlers[event.data.action](event.data);
47978              }
47979            }
47980          };
47981        };
47982  
47983        var transmuxerWorker = new TransmuxerWorker(self);
47984        return transmuxerWorker;
47985      }();
47986    });
47987    /**
47988     * @file - codecs.js - Handles tasks regarding codec strings such as translating them to
47989     * codec strings, or translating codec strings into objects that can be examined.
47990     */
47991    // Default codec parameters if none were provided for video and/or audio
47992  
47993    var defaultCodecs = {
47994      videoCodec: 'avc1',
47995      videoObjectTypeIndicator: '.4d400d',
47996      // AAC-LC
47997      audioProfile: '2'
47998    };
47999    /**
48000     * Replace the old apple-style `avc1.<dd>.<dd>` codec string with the standard
48001     * `avc1.<hhhhhh>`
48002     *
48003     * @param {Array} codecs an array of codec strings to fix
48004     * @return {Array} the translated codec array
48005     * @private
48006     */
48007  
48008    var translateLegacyCodecs = function translateLegacyCodecs(codecs) {
48009      return codecs.map(function (codec) {
48010        return codec.replace(/avc1\.(\d+)\.(\d+)/i, function (orig, profile, avcLevel) {
48011          var profileHex = ('00' + Number(profile).toString(16)).slice(-2);
48012          var avcLevelHex = ('00' + Number(avcLevel).toString(16)).slice(-2);
48013          return 'avc1.' + profileHex + '00' + avcLevelHex;
48014        });
48015      });
48016    };
48017    /**
48018     * Parses a codec string to retrieve the number of codecs specified,
48019     * the video codec and object type indicator, and the audio profile.
48020     */
48021  
48022  
48023    var parseCodecs = function parseCodecs() {
48024      var codecs = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : '';
48025      var result = {
48026        codecCount: 0
48027      };
48028      var parsed = void 0;
48029      result.codecCount = codecs.split(',').length;
48030      result.codecCount = result.codecCount || 2; // parse the video codec
48031  
48032      parsed = /(^|\s|,)+(avc[13])([^ ,]*)/i.exec(codecs);
48033  
48034      if (parsed) {
48035        result.videoCodec = parsed[2];
48036        result.videoObjectTypeIndicator = parsed[3];
48037      } // parse the last field of the audio codec
48038  
48039  
48040      result.audioProfile = /(^|\s|,)+mp4a.[0-9A-Fa-f]+\.([0-9A-Fa-f]+)/i.exec(codecs);
48041      result.audioProfile = result.audioProfile && result.audioProfile[2];
48042      return result;
48043    };
48044    /**
48045     * Replace codecs in the codec string with the old apple-style `avc1.<dd>.<dd>` to the
48046     * standard `avc1.<hhhhhh>`.
48047     *
48048     * @param codecString {String} the codec string
48049     * @return {String} the codec string with old apple-style codecs replaced
48050     *
48051     * @private
48052     */
48053  
48054  
48055    var mapLegacyAvcCodecs = function mapLegacyAvcCodecs(codecString) {
48056      return codecString.replace(/avc1\.(\d+)\.(\d+)/i, function (match) {
48057        return translateLegacyCodecs([match])[0];
48058      });
48059    };
48060    /**
48061     * Build a media mime-type string from a set of parameters
48062     * @param {String} type either 'audio' or 'video'
48063     * @param {String} container either 'mp2t' or 'mp4'
48064     * @param {Array} codecs an array of codec strings to add
48065     * @return {String} a valid media mime-type
48066     */
48067  
48068  
48069    var makeMimeTypeString = function makeMimeTypeString(type, container, codecs) {
48070      // The codecs array is filtered so that falsey values are
48071      // dropped and don't cause Array#join to create spurious
48072      // commas
48073      return type + '/' + container + '; codecs="' + codecs.filter(function (c) {
48074        return !!c;
48075      }).join(', ') + '"';
48076    };
48077    /**
48078     * Returns the type container based on information in the playlist
48079     * @param {Playlist} media the current media playlist
48080     * @return {String} a valid media container type
48081     */
48082  
48083  
48084    var getContainerType = function getContainerType(media) {
48085      // An initialization segment means the media playlist is an iframe
48086      // playlist or is using the mp4 container. We don't currently
48087      // support iframe playlists, so assume this is signalling mp4
48088      // fragments.
48089      if (media.segments && media.segments.length && media.segments[0].map) {
48090        return 'mp4';
48091      }
48092  
48093      return 'mp2t';
48094    };
48095    /**
48096     * Returns a set of codec strings parsed from the playlist or the default
48097     * codec strings if no codecs were specified in the playlist
48098     * @param {Playlist} media the current media playlist
48099     * @return {Object} an object with the video and audio codecs
48100     */
48101  
48102  
48103    var getCodecs = function getCodecs(media) {
48104      // if the codecs were explicitly specified, use them instead of the
48105      // defaults
48106      var mediaAttributes = media.attributes || {};
48107  
48108      if (mediaAttributes.CODECS) {
48109        return parseCodecs(mediaAttributes.CODECS);
48110      }
48111  
48112      return defaultCodecs;
48113    };
48114  
48115    var audioProfileFromDefault = function audioProfileFromDefault(master, audioGroupId) {
48116      if (!master.mediaGroups.AUDIO || !audioGroupId) {
48117        return null;
48118      }
48119  
48120      var audioGroup = master.mediaGroups.AUDIO[audioGroupId];
48121  
48122      if (!audioGroup) {
48123        return null;
48124      }
48125  
48126      for (var name in audioGroup) {
48127        var audioType = audioGroup[name];
48128  
48129        if (audioType["default"] && audioType.playlists) {
48130          // codec should be the same for all playlists within the audio type
48131          return parseCodecs(audioType.playlists[0].attributes.CODECS).audioProfile;
48132        }
48133      }
48134  
48135      return null;
48136    };
48137    /**
48138     * Calculates the MIME type strings for a working configuration of
48139     * SourceBuffers to play variant streams in a master playlist. If
48140     * there is no possible working configuration, an empty array will be
48141     * returned.
48142     *
48143     * @param master {Object} the m3u8 object for the master playlist
48144     * @param media {Object} the m3u8 object for the variant playlist
48145     * @return {Array} the MIME type strings. If the array has more than
48146     * one entry, the first element should be applied to the video
48147     * SourceBuffer and the second to the audio SourceBuffer.
48148     *
48149     * @private
48150     */
48151  
48152  
48153    var mimeTypesForPlaylist = function mimeTypesForPlaylist(master, media) {
48154      var containerType = getContainerType(media);
48155      var codecInfo = getCodecs(media);
48156      var mediaAttributes = media.attributes || {}; // Default condition for a traditional HLS (no demuxed audio/video)
48157  
48158      var isMuxed = true;
48159      var isMaat = false;
48160  
48161      if (!media) {
48162        // Not enough information
48163        return [];
48164      }
48165  
48166      if (master.mediaGroups.AUDIO && mediaAttributes.AUDIO) {
48167        var audioGroup = master.mediaGroups.AUDIO[mediaAttributes.AUDIO]; // Handle the case where we are in a multiple-audio track scenario
48168  
48169        if (audioGroup) {
48170          isMaat = true; // Start with the everything demuxed then...
48171  
48172          isMuxed = false; // ...check to see if any audio group tracks are muxed (ie. lacking a uri)
48173  
48174          for (var groupId in audioGroup) {
48175            // either a uri is present (if the case of HLS and an external playlist), or
48176            // playlists is present (in the case of DASH where we don't have external audio
48177            // playlists)
48178            if (!audioGroup[groupId].uri && !audioGroup[groupId].playlists) {
48179              isMuxed = true;
48180              break;
48181            }
48182          }
48183        }
48184      } // HLS with multiple-audio tracks must always get an audio codec.
48185      // Put another way, there is no way to have a video-only multiple-audio HLS!
48186  
48187  
48188      if (isMaat && !codecInfo.audioProfile) {
48189        if (!isMuxed) {
48190          // It is possible for codecs to be specified on the audio media group playlist but
48191          // not on the rendition playlist. This is mostly the case for DASH, where audio and
48192          // video are always separate (and separately specified).
48193          codecInfo.audioProfile = audioProfileFromDefault(master, mediaAttributes.AUDIO);
48194        }
48195  
48196        if (!codecInfo.audioProfile) {
48197          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)');
48198          codecInfo.audioProfile = defaultCodecs.audioProfile;
48199        }
48200      } // Generate the final codec strings from the codec object generated above
48201  
48202  
48203      var codecStrings = {};
48204  
48205      if (codecInfo.videoCodec) {
48206        codecStrings.video = '' + codecInfo.videoCodec + codecInfo.videoObjectTypeIndicator;
48207      }
48208  
48209      if (codecInfo.audioProfile) {
48210        codecStrings.audio = 'mp4a.40.' + codecInfo.audioProfile;
48211      } // Finally, make and return an array with proper mime-types depending on
48212      // the configuration
48213  
48214  
48215      var justAudio = makeMimeTypeString('audio', containerType, [codecStrings.audio]);
48216      var justVideo = makeMimeTypeString('video', containerType, [codecStrings.video]);
48217      var bothVideoAudio = makeMimeTypeString('video', containerType, [codecStrings.video, codecStrings.audio]);
48218  
48219      if (isMaat) {
48220        if (!isMuxed && codecStrings.video) {
48221          return [justVideo, justAudio];
48222        }
48223  
48224        if (!isMuxed && !codecStrings.video) {
48225          // There is no muxed content and no video codec string, so this is an audio only
48226          // stream with alternate audio.
48227          return [justAudio, justAudio];
48228        } // There exists the possiblity that this will return a `video/container`
48229        // mime-type for the first entry in the array even when there is only audio.
48230        // This doesn't appear to be a problem and simplifies the code.
48231  
48232  
48233        return [bothVideoAudio, justAudio];
48234      } // If there is no video codec at all, always just return a single
48235      // audio/<container> mime-type
48236  
48237  
48238      if (!codecStrings.video) {
48239        return [justAudio];
48240      } // When not using separate audio media groups, audio and video is
48241      // *always* muxed
48242  
48243  
48244      return [bothVideoAudio];
48245    };
48246    /**
48247     * Parse a content type header into a type and parameters
48248     * object
48249     *
48250     * @param {String} type the content type header
48251     * @return {Object} the parsed content-type
48252     * @private
48253     */
48254  
48255  
48256    var parseContentType = function parseContentType(type) {
48257      var object = {
48258        type: '',
48259        parameters: {}
48260      };
48261      var parameters = type.trim().split(';'); // first parameter should always be content-type
48262  
48263      object.type = parameters.shift().trim();
48264      parameters.forEach(function (parameter) {
48265        var pair = parameter.trim().split('=');
48266  
48267        if (pair.length > 1) {
48268          var name = pair[0].replace(/"/g, '').trim();
48269          var value = pair[1].replace(/"/g, '').trim();
48270          object.parameters[name] = value;
48271        }
48272      });
48273      return object;
48274    };
48275    /**
48276     * Check if a codec string refers to an audio codec.
48277     *
48278     * @param {String} codec codec string to check
48279     * @return {Boolean} if this is an audio codec
48280     * @private
48281     */
48282  
48283  
48284    var isAudioCodec = function isAudioCodec(codec) {
48285      return /mp4a\.\d+.\d+/i.test(codec);
48286    };
48287    /**
48288     * Check if a codec string refers to a video codec.
48289     *
48290     * @param {String} codec codec string to check
48291     * @return {Boolean} if this is a video codec
48292     * @private
48293     */
48294  
48295  
48296    var isVideoCodec = function isVideoCodec(codec) {
48297      return /avc1\.[\da-f]+/i.test(codec);
48298    };
48299    /**
48300     * Returns a list of gops in the buffer that have a pts value of 3 seconds or more in
48301     * front of current time.
48302     *
48303     * @param {Array} buffer
48304     *        The current buffer of gop information
48305     * @param {Number} currentTime
48306     *        The current time
48307     * @param {Double} mapping
48308     *        Offset to map display time to stream presentation time
48309     * @return {Array}
48310     *         List of gops considered safe to append over
48311     */
48312  
48313  
48314    var gopsSafeToAlignWith = function gopsSafeToAlignWith(buffer, currentTime, mapping) {
48315      if (typeof currentTime === 'undefined' || currentTime === null || !buffer.length) {
48316        return [];
48317      } // pts value for current time + 3 seconds to give a bit more wiggle room
48318  
48319  
48320      var currentTimePts = Math.ceil((currentTime - mapping + 3) * 90000);
48321      var i = void 0;
48322  
48323      for (i = 0; i < buffer.length; i++) {
48324        if (buffer[i].pts > currentTimePts) {
48325          break;
48326        }
48327      }
48328  
48329      return buffer.slice(i);
48330    };
48331    /**
48332     * Appends gop information (timing and byteLength) received by the transmuxer for the
48333     * gops appended in the last call to appendBuffer
48334     *
48335     * @param {Array} buffer
48336     *        The current buffer of gop information
48337     * @param {Array} gops
48338     *        List of new gop information
48339     * @param {boolean} replace
48340     *        If true, replace the buffer with the new gop information. If false, append the
48341     *        new gop information to the buffer in the right location of time.
48342     * @return {Array}
48343     *         Updated list of gop information
48344     */
48345  
48346  
48347    var updateGopBuffer = function updateGopBuffer(buffer, gops, replace) {
48348      if (!gops.length) {
48349        return buffer;
48350      }
48351  
48352      if (replace) {
48353        // If we are in safe append mode, then completely overwrite the gop buffer
48354        // with the most recent appeneded data. This will make sure that when appending
48355        // future segments, we only try to align with gops that are both ahead of current
48356        // time and in the last segment appended.
48357        return gops.slice();
48358      }
48359  
48360      var start = gops[0].pts;
48361      var i = 0;
48362  
48363      for (i; i < buffer.length; i++) {
48364        if (buffer[i].pts >= start) {
48365          break;
48366        }
48367      }
48368  
48369      return buffer.slice(0, i).concat(gops);
48370    };
48371    /**
48372     * Removes gop information in buffer that overlaps with provided start and end
48373     *
48374     * @param {Array} buffer
48375     *        The current buffer of gop information
48376     * @param {Double} start
48377     *        position to start the remove at
48378     * @param {Double} end
48379     *        position to end the remove at
48380     * @param {Double} mapping
48381     *        Offset to map display time to stream presentation time
48382     */
48383  
48384  
48385    var removeGopBuffer = function removeGopBuffer(buffer, start, end, mapping) {
48386      var startPts = Math.ceil((start - mapping) * 90000);
48387      var endPts = Math.ceil((end - mapping) * 90000);
48388      var updatedBuffer = buffer.slice();
48389      var i = buffer.length;
48390  
48391      while (i--) {
48392        if (buffer[i].pts <= endPts) {
48393          break;
48394        }
48395      }
48396  
48397      if (i === -1) {
48398        // no removal because end of remove range is before start of buffer
48399        return updatedBuffer;
48400      }
48401  
48402      var j = i + 1;
48403  
48404      while (j--) {
48405        if (buffer[j].pts <= startPts) {
48406          break;
48407        }
48408      } // clamp remove range start to 0 index
48409  
48410  
48411      j = Math.max(j, 0);
48412      updatedBuffer.splice(j, i - j + 1);
48413      return updatedBuffer;
48414    };
48415  
48416    var buffered = function buffered(videoBuffer, audioBuffer, audioDisabled) {
48417      var start = null;
48418      var end = null;
48419      var arity = 0;
48420      var extents = [];
48421      var ranges = []; // neither buffer has been created yet
48422  
48423      if (!videoBuffer && !audioBuffer) {
48424        return videojs$1.createTimeRange();
48425      } // only one buffer is configured
48426  
48427  
48428      if (!videoBuffer) {
48429        return audioBuffer.buffered;
48430      }
48431  
48432      if (!audioBuffer) {
48433        return videoBuffer.buffered;
48434      } // both buffers are configured
48435  
48436  
48437      if (audioDisabled) {
48438        return videoBuffer.buffered;
48439      } // both buffers are empty
48440  
48441  
48442      if (videoBuffer.buffered.length === 0 && audioBuffer.buffered.length === 0) {
48443        return videojs$1.createTimeRange();
48444      } // Handle the case where we have both buffers and create an
48445      // intersection of the two
48446  
48447  
48448      var videoBuffered = videoBuffer.buffered;
48449      var audioBuffered = audioBuffer.buffered;
48450      var count = videoBuffered.length; // A) Gather up all start and end times
48451  
48452      while (count--) {
48453        extents.push({
48454          time: videoBuffered.start(count),
48455          type: 'start'
48456        });
48457        extents.push({
48458          time: videoBuffered.end(count),
48459          type: 'end'
48460        });
48461      }
48462  
48463      count = audioBuffered.length;
48464  
48465      while (count--) {
48466        extents.push({
48467          time: audioBuffered.start(count),
48468          type: 'start'
48469        });
48470        extents.push({
48471          time: audioBuffered.end(count),
48472          type: 'end'
48473        });
48474      } // B) Sort them by time
48475  
48476  
48477      extents.sort(function (a, b) {
48478        return a.time - b.time;
48479      }); // C) Go along one by one incrementing arity for start and decrementing
48480      //    arity for ends
48481  
48482      for (count = 0; count < extents.length; count++) {
48483        if (extents[count].type === 'start') {
48484          arity++; // D) If arity is ever incremented to 2 we are entering an
48485          //    overlapping range
48486  
48487          if (arity === 2) {
48488            start = extents[count].time;
48489          }
48490        } else if (extents[count].type === 'end') {
48491          arity--; // E) If arity is ever decremented to 1 we leaving an
48492          //    overlapping range
48493  
48494          if (arity === 1) {
48495            end = extents[count].time;
48496          }
48497        } // F) Record overlapping ranges
48498  
48499  
48500        if (start !== null && end !== null) {
48501          ranges.push([start, end]);
48502          start = null;
48503          end = null;
48504        }
48505      }
48506  
48507      return videojs$1.createTimeRanges(ranges);
48508    };
48509    /**
48510     * @file virtual-source-buffer.js
48511     */
48512  
48513  
48514    var ONE_SECOND_IN_TS$2 = 90000; // We create a wrapper around the SourceBuffer so that we can manage the
48515    // state of the `updating` property manually. We have to do this because
48516    // Firefox changes `updating` to false long before triggering `updateend`
48517    // events and that was causing strange problems in videojs-contrib-hls
48518  
48519    var makeWrappedSourceBuffer = function makeWrappedSourceBuffer(mediaSource, mimeType) {
48520      var sourceBuffer = mediaSource.addSourceBuffer(mimeType);
48521      var wrapper = Object.create(null);
48522      wrapper.updating = false;
48523      wrapper.realBuffer_ = sourceBuffer;
48524  
48525      var _loop = function _loop(key) {
48526        if (typeof sourceBuffer[key] === 'function') {
48527          wrapper[key] = function () {
48528            return sourceBuffer[key].apply(sourceBuffer, arguments);
48529          };
48530        } else if (typeof wrapper[key] === 'undefined') {
48531          Object.defineProperty(wrapper, key, {
48532            get: function get$$1() {
48533              return sourceBuffer[key];
48534            },
48535            set: function set$$1(v) {
48536              return sourceBuffer[key] = v;
48537            }
48538          });
48539        }
48540      };
48541  
48542      for (var key in sourceBuffer) {
48543        _loop(key);
48544      }
48545  
48546      return wrapper;
48547    };
48548    /**
48549     * VirtualSourceBuffers exist so that we can transmux non native formats
48550     * into a native format, but keep the same api as a native source buffer.
48551     * It creates a transmuxer, that works in its own thread (a web worker) and
48552     * that transmuxer muxes the data into a native format. VirtualSourceBuffer will
48553     * then send all of that data to the naive sourcebuffer so that it is
48554     * indestinguishable from a natively supported format.
48555     *
48556     * @param {HtmlMediaSource} mediaSource the parent mediaSource
48557     * @param {Array} codecs array of codecs that we will be dealing with
48558     * @class VirtualSourceBuffer
48559     * @extends video.js.EventTarget
48560     */
48561  
48562  
48563    var VirtualSourceBuffer = function (_videojs$EventTarget) {
48564      inherits$2(VirtualSourceBuffer, _videojs$EventTarget);
48565  
48566      function VirtualSourceBuffer(mediaSource, codecs) {
48567        classCallCheck$1(this, VirtualSourceBuffer);
48568  
48569        var _this = possibleConstructorReturn$1(this, (VirtualSourceBuffer.__proto__ || Object.getPrototypeOf(VirtualSourceBuffer)).call(this, videojs$1.EventTarget));
48570  
48571        _this.timestampOffset_ = 0;
48572        _this.pendingBuffers_ = [];
48573        _this.bufferUpdating_ = false;
48574        _this.mediaSource_ = mediaSource;
48575        _this.codecs_ = codecs;
48576        _this.audioCodec_ = null;
48577        _this.videoCodec_ = null;
48578        _this.audioDisabled_ = false;
48579        _this.appendAudioInitSegment_ = true;
48580        _this.gopBuffer_ = [];
48581        _this.timeMapping_ = 0;
48582        _this.safeAppend_ = videojs$1.browser.IE_VERSION >= 11;
48583        var options = {
48584          remux: false,
48585          alignGopsAtEnd: _this.safeAppend_
48586        };
48587  
48588        _this.codecs_.forEach(function (codec) {
48589          if (isAudioCodec(codec)) {
48590            _this.audioCodec_ = codec;
48591          } else if (isVideoCodec(codec)) {
48592            _this.videoCodec_ = codec;
48593          }
48594        }); // append muxed segments to their respective native buffers as
48595        // soon as they are available
48596  
48597  
48598        _this.transmuxer_ = new TransmuxWorker();
48599  
48600        _this.transmuxer_.postMessage({
48601          action: 'init',
48602          options: options
48603        });
48604  
48605        _this.transmuxer_.onmessage = function (event) {
48606          if (event.data.action === 'data') {
48607            return _this.data_(event);
48608          }
48609  
48610          if (event.data.action === 'done') {
48611            return _this.done_(event);
48612          }
48613  
48614          if (event.data.action === 'gopInfo') {
48615            return _this.appendGopInfo_(event);
48616          }
48617  
48618          if (event.data.action === 'videoSegmentTimingInfo') {
48619            return _this.videoSegmentTimingInfo_(event.data.videoSegmentTimingInfo);
48620          }
48621        }; // this timestampOffset is a property with the side-effect of resetting
48622        // baseMediaDecodeTime in the transmuxer on the setter
48623  
48624  
48625        Object.defineProperty(_this, 'timestampOffset', {
48626          get: function get$$1() {
48627            return this.timestampOffset_;
48628          },
48629          set: function set$$1(val) {
48630            if (typeof val === 'number' && val >= 0) {
48631              this.timestampOffset_ = val;
48632              this.appendAudioInitSegment_ = true; // reset gop buffer on timestampoffset as this signals a change in timeline
48633  
48634              this.gopBuffer_.length = 0;
48635              this.timeMapping_ = 0; // We have to tell the transmuxer to set the baseMediaDecodeTime to
48636              // the desired timestampOffset for the next segment
48637  
48638              this.transmuxer_.postMessage({
48639                action: 'setTimestampOffset',
48640                timestampOffset: val
48641              });
48642            }
48643          }
48644        }); // setting the append window affects both source buffers
48645  
48646        Object.defineProperty(_this, 'appendWindowStart', {
48647          get: function get$$1() {
48648            return (this.videoBuffer_ || this.audioBuffer_).appendWindowStart;
48649          },
48650          set: function set$$1(start) {
48651            if (this.videoBuffer_) {
48652              this.videoBuffer_.appendWindowStart = start;
48653            }
48654  
48655            if (this.audioBuffer_) {
48656              this.audioBuffer_.appendWindowStart = start;
48657            }
48658          }
48659        }); // this buffer is "updating" if either of its native buffers are
48660  
48661        Object.defineProperty(_this, 'updating', {
48662          get: function get$$1() {
48663            return !!(this.bufferUpdating_ || !this.audioDisabled_ && this.audioBuffer_ && this.audioBuffer_.updating || this.videoBuffer_ && this.videoBuffer_.updating);
48664          }
48665        }); // the buffered property is the intersection of the buffered
48666        // ranges of the native source buffers
48667  
48668        Object.defineProperty(_this, 'buffered', {
48669          get: function get$$1() {
48670            return buffered(this.videoBuffer_, this.audioBuffer_, this.audioDisabled_);
48671          }
48672        });
48673        return _this;
48674      }
48675      /**
48676       * When we get a data event from the transmuxer
48677       * we call this function and handle the data that
48678       * was sent to us
48679       *
48680       * @private
48681       * @param {Event} event the data event from the transmuxer
48682       */
48683  
48684  
48685      createClass$1(VirtualSourceBuffer, [{
48686        key: 'data_',
48687        value: function data_(event) {
48688          var segment = event.data.segment; // Cast ArrayBuffer to TypedArray
48689  
48690          segment.data = new Uint8Array(segment.data, event.data.byteOffset, event.data.byteLength);
48691          segment.initSegment = new Uint8Array(segment.initSegment.data, segment.initSegment.byteOffset, segment.initSegment.byteLength);
48692          createTextTracksIfNecessary(this, this.mediaSource_, segment); // Add the segments to the pendingBuffers array
48693  
48694          this.pendingBuffers_.push(segment);
48695          return;
48696        }
48697        /**
48698         * When we get a done event from the transmuxer
48699         * we call this function and we process all
48700         * of the pending data that we have been saving in the
48701         * data_ function
48702         *
48703         * @private
48704         * @param {Event} event the done event from the transmuxer
48705         */
48706  
48707      }, {
48708        key: 'done_',
48709        value: function done_(event) {
48710          // Don't process and append data if the mediaSource is closed
48711          if (this.mediaSource_.readyState === 'closed') {
48712            this.pendingBuffers_.length = 0;
48713            return;
48714          } // All buffers should have been flushed from the muxer
48715          // start processing anything we have received
48716  
48717  
48718          this.processPendingSegments_();
48719          return;
48720        }
48721      }, {
48722        key: 'videoSegmentTimingInfo_',
48723        value: function videoSegmentTimingInfo_(timingInfo) {
48724          var timingInfoInSeconds = {
48725            start: {
48726              decode: timingInfo.start.dts / ONE_SECOND_IN_TS$2,
48727              presentation: timingInfo.start.pts / ONE_SECOND_IN_TS$2
48728            },
48729            end: {
48730              decode: timingInfo.end.dts / ONE_SECOND_IN_TS$2,
48731              presentation: timingInfo.end.pts / ONE_SECOND_IN_TS$2
48732            },
48733            baseMediaDecodeTime: timingInfo.baseMediaDecodeTime / ONE_SECOND_IN_TS$2
48734          };
48735  
48736          if (timingInfo.prependedContentDuration) {
48737            timingInfoInSeconds.prependedContentDuration = timingInfo.prependedContentDuration / ONE_SECOND_IN_TS$2;
48738          }
48739  
48740          this.trigger({
48741            type: 'videoSegmentTimingInfo',
48742            videoSegmentTimingInfo: timingInfoInSeconds
48743          });
48744        }
48745        /**
48746         * Create our internal native audio/video source buffers and add
48747         * event handlers to them with the following conditions:
48748         * 1. they do not already exist on the mediaSource
48749         * 2. this VSB has a codec for them
48750         *
48751         * @private
48752         */
48753  
48754      }, {
48755        key: 'createRealSourceBuffers_',
48756        value: function createRealSourceBuffers_() {
48757          var _this2 = this;
48758  
48759          var types = ['audio', 'video'];
48760          types.forEach(function (type) {
48761            // Don't create a SourceBuffer of this type if we don't have a
48762            // codec for it
48763            if (!_this2[type + 'Codec_']) {
48764              return;
48765            } // Do nothing if a SourceBuffer of this type already exists
48766  
48767  
48768            if (_this2[type + 'Buffer_']) {
48769              return;
48770            }
48771  
48772            var buffer = null; // If the mediasource already has a SourceBuffer for the codec
48773            // use that
48774  
48775            if (_this2.mediaSource_[type + 'Buffer_']) {
48776              buffer = _this2.mediaSource_[type + 'Buffer_']; // In multiple audio track cases, the audio source buffer is disabled
48777              // on the main VirtualSourceBuffer by the HTMLMediaSource much earlier
48778              // than createRealSourceBuffers_ is called to create the second
48779              // VirtualSourceBuffer because that happens as a side-effect of
48780              // videojs-contrib-hls starting the audioSegmentLoader. As a result,
48781              // the audioBuffer is essentially "ownerless" and no one will toggle
vendor: 30,123 bytes, lines 48782-49589
48782              // the `updating` state back to false once the `updateend` event is received
48783              //
48784              // Setting `updating` to false manually will work around this
48785              // situation and allow work to continue
48786  
48787              buffer.updating = false;
48788            } else {
48789              var codecProperty = type + 'Codec_';
48790              var mimeType = type + '/mp4;codecs="' + _this2[codecProperty] + '"';
48791              buffer = makeWrappedSourceBuffer(_this2.mediaSource_.nativeMediaSource_, mimeType);
48792              _this2.mediaSource_[type + 'Buffer_'] = buffer;
48793            }
48794  
48795            _this2[type + 'Buffer_'] = buffer; // Wire up the events to the SourceBuffer
48796  
48797            ['update', 'updatestart', 'updateend'].forEach(function (event) {
48798              buffer.addEventListener(event, function () {
48799                // if audio is disabled
48800                if (type === 'audio' && _this2.audioDisabled_) {
48801                  return;
48802                }
48803  
48804                if (event === 'updateend') {
48805                  _this2[type + 'Buffer_'].updating = false;
48806                }
48807  
48808                var shouldTrigger = types.every(function (t) {
48809                  // skip checking audio's updating status if audio
48810                  // is not enabled
48811                  if (t === 'audio' && _this2.audioDisabled_) {
48812                    return true;
48813                  } // if the other type is updating we don't trigger
48814  
48815  
48816                  if (type !== t && _this2[t + 'Buffer_'] && _this2[t + 'Buffer_'].updating) {
48817                    return false;
48818                  }
48819  
48820                  return true;
48821                });
48822  
48823                if (shouldTrigger) {
48824                  return _this2.trigger(event);
48825                }
48826              });
48827            });
48828          });
48829        }
48830        /**
48831         * Emulate the native mediasource function, but our function will
48832         * send all of the proposed segments to the transmuxer so that we
48833         * can transmux them before we append them to our internal
48834         * native source buffers in the correct format.
48835         *
48836         * @link https://developer.mozilla.org/en-US/docs/Web/API/SourceBuffer/appendBuffer
48837         * @param {Uint8Array} segment the segment to append to the buffer
48838         */
48839  
48840      }, {
48841        key: 'appendBuffer',
48842        value: function appendBuffer(segment) {
48843          // Start the internal "updating" state
48844          this.bufferUpdating_ = true;
48845  
48846          if (this.audioBuffer_ && this.audioBuffer_.buffered.length) {
48847            var audioBuffered = this.audioBuffer_.buffered;
48848            this.transmuxer_.postMessage({
48849              action: 'setAudioAppendStart',
48850              appendStart: audioBuffered.end(audioBuffered.length - 1)
48851            });
48852          }
48853  
48854          if (this.videoBuffer_) {
48855            this.transmuxer_.postMessage({
48856              action: 'alignGopsWith',
48857              gopsToAlignWith: gopsSafeToAlignWith(this.gopBuffer_, this.mediaSource_.player_ ? this.mediaSource_.player_.currentTime() : null, this.timeMapping_)
48858            });
48859          }
48860  
48861          this.transmuxer_.postMessage({
48862            action: 'push',
48863            // Send the typed-array of data as an ArrayBuffer so that
48864            // it can be sent as a "Transferable" and avoid the costly
48865            // memory copy
48866            data: segment.buffer,
48867            // To recreate the original typed-array, we need information
48868            // about what portion of the ArrayBuffer it was a view into
48869            byteOffset: segment.byteOffset,
48870            byteLength: segment.byteLength
48871          }, [segment.buffer]);
48872          this.transmuxer_.postMessage({
48873            action: 'flush'
48874          });
48875        }
48876        /**
48877         * Appends gop information (timing and byteLength) received by the transmuxer for the
48878         * gops appended in the last call to appendBuffer
48879         *
48880         * @param {Event} event
48881         *        The gopInfo event from the transmuxer
48882         * @param {Array} event.data.gopInfo
48883         *        List of gop info to append
48884         */
48885  
48886      }, {
48887        key: 'appendGopInfo_',
48888        value: function appendGopInfo_(event) {
48889          this.gopBuffer_ = updateGopBuffer(this.gopBuffer_, event.data.gopInfo, this.safeAppend_);
48890        }
48891        /**
48892         * Emulate the native mediasource function and remove parts
48893         * of the buffer from any of our internal buffers that exist
48894         *
48895         * @link https://developer.mozilla.org/en-US/docs/Web/API/SourceBuffer/remove
48896         * @param {Double} start position to start the remove at
48897         * @param {Double} end position to end the remove at
48898         */
48899  
48900      }, {
48901        key: 'remove',
48902        value: function remove(start, end) {
48903          if (this.videoBuffer_) {
48904            this.videoBuffer_.updating = true;
48905            this.videoBuffer_.remove(start, end);
48906            this.gopBuffer_ = removeGopBuffer(this.gopBuffer_, start, end, this.timeMapping_);
48907          }
48908  
48909          if (!this.audioDisabled_ && this.audioBuffer_) {
48910            this.audioBuffer_.updating = true;
48911            this.audioBuffer_.remove(start, end);
48912          } // Remove Metadata Cues (id3)
48913  
48914  
48915          removeCuesFromTrack(start, end, this.metadataTrack_); // Remove Any Captions
48916  
48917          if (this.inbandTextTracks_) {
48918            for (var track in this.inbandTextTracks_) {
48919              removeCuesFromTrack(start, end, this.inbandTextTracks_[track]);
48920            }
48921          }
48922        }
48923        /**
48924         * Process any segments that the muxer has output
48925         * Concatenate segments together based on type and append them into
48926         * their respective sourceBuffers
48927         *
48928         * @private
48929         */
48930  
48931      }, {
48932        key: 'processPendingSegments_',
48933        value: function processPendingSegments_() {
48934          var sortedSegments = {
48935            video: {
48936              segments: [],
48937              bytes: 0
48938            },
48939            audio: {
48940              segments: [],
48941              bytes: 0
48942            },
48943            captions: [],
48944            metadata: []
48945          };
48946  
48947          if (!this.pendingBuffers_.length) {
48948            // We are no longer in the internal "updating" state
48949            this.trigger('updateend');
48950            this.bufferUpdating_ = false;
48951            return;
48952          } // Sort segments into separate video/audio arrays and
48953          // keep track of their total byte lengths
48954  
48955  
48956          sortedSegments = this.pendingBuffers_.reduce(function (segmentObj, segment) {
48957            var type = segment.type;
48958            var data = segment.data;
48959            var initSegment = segment.initSegment;
48960            segmentObj[type].segments.push(data);
48961            segmentObj[type].bytes += data.byteLength;
48962            segmentObj[type].initSegment = initSegment; // Gather any captions into a single array
48963  
48964            if (segment.captions) {
48965              segmentObj.captions = segmentObj.captions.concat(segment.captions);
48966            }
48967  
48968            if (segment.info) {
48969              segmentObj[type].info = segment.info;
48970            } // Gather any metadata into a single array
48971  
48972  
48973            if (segment.metadata) {
48974              segmentObj.metadata = segmentObj.metadata.concat(segment.metadata);
48975            }
48976  
48977            return segmentObj;
48978          }, sortedSegments); // Create the real source buffers if they don't exist by now since we
48979          // finally are sure what tracks are contained in the source
48980  
48981          if (!this.videoBuffer_ && !this.audioBuffer_) {
48982            // Remove any codecs that may have been specified by default but
48983            // are no longer applicable now
48984            if (sortedSegments.video.bytes === 0) {
48985              this.videoCodec_ = null;
48986            }
48987  
48988            if (sortedSegments.audio.bytes === 0) {
48989              this.audioCodec_ = null;
48990            }
48991  
48992            this.createRealSourceBuffers_();
48993          }
48994  
48995          if (sortedSegments.audio.info) {
48996            this.mediaSource_.trigger({
48997              type: 'audioinfo',
48998              info: sortedSegments.audio.info
48999            });
49000          }
49001  
49002          if (sortedSegments.video.info) {
49003            this.mediaSource_.trigger({
49004              type: 'videoinfo',
49005              info: sortedSegments.video.info
49006            });
49007          }
49008  
49009          if (this.appendAudioInitSegment_) {
49010            if (!this.audioDisabled_ && this.audioBuffer_) {
49011              sortedSegments.audio.segments.unshift(sortedSegments.audio.initSegment);
49012              sortedSegments.audio.bytes += sortedSegments.audio.initSegment.byteLength;
49013            }
49014  
49015            this.appendAudioInitSegment_ = false;
49016          }
49017  
49018          var triggerUpdateend = false; // Merge multiple video and audio segments into one and append
49019  
49020          if (this.videoBuffer_ && sortedSegments.video.bytes) {
49021            sortedSegments.video.segments.unshift(sortedSegments.video.initSegment);
49022            sortedSegments.video.bytes += sortedSegments.video.initSegment.byteLength;
49023            this.concatAndAppendSegments_(sortedSegments.video, this.videoBuffer_);
49024          } else if (this.videoBuffer_ && (this.audioDisabled_ || !this.audioBuffer_)) {
49025            // The transmuxer did not return any bytes of video, meaning it was all trimmed
49026            // for gop alignment. Since we have a video buffer and audio is disabled, updateend
49027            // will never be triggered by this source buffer, which will cause contrib-hls
49028            // to be stuck forever waiting for updateend. If audio is not disabled, updateend
49029            // will be triggered by the audio buffer, which will be sent upwards since the video
49030            // buffer will not be in an updating state.
49031            triggerUpdateend = true;
49032          } // Add text-track data for all
49033  
49034  
49035          addTextTrackData(this, sortedSegments.captions, sortedSegments.metadata);
49036  
49037          if (!this.audioDisabled_ && this.audioBuffer_) {
49038            this.concatAndAppendSegments_(sortedSegments.audio, this.audioBuffer_);
49039          }
49040  
49041          this.pendingBuffers_.length = 0;
49042  
49043          if (triggerUpdateend) {
49044            this.trigger('updateend');
49045          } // We are no longer in the internal "updating" state
49046  
49047  
49048          this.bufferUpdating_ = false;
49049        }
49050        /**
49051         * Combine all segments into a single Uint8Array and then append them
49052         * to the destination buffer
49053         *
49054         * @param {Object} segmentObj
49055         * @param {SourceBuffer} destinationBuffer native source buffer to append data to
49056         * @private
49057         */
49058  
49059      }, {
49060        key: 'concatAndAppendSegments_',
49061        value: function concatAndAppendSegments_(segmentObj, destinationBuffer) {
49062          var offset = 0;
49063          var tempBuffer = void 0;
49064  
49065          if (segmentObj.bytes) {
49066            tempBuffer = new Uint8Array(segmentObj.bytes); // Combine the individual segments into one large typed-array
49067  
49068            segmentObj.segments.forEach(function (segment) {
49069              tempBuffer.set(segment, offset);
49070              offset += segment.byteLength;
49071            });
49072  
49073            try {
49074              destinationBuffer.updating = true;
49075              destinationBuffer.appendBuffer(tempBuffer);
49076            } catch (error) {
49077              if (this.mediaSource_.player_) {
49078                this.mediaSource_.player_.error({
49079                  code: -3,
49080                  type: 'APPEND_BUFFER_ERR',
49081                  message: error.message,
49082                  originalError: error
49083                });
49084              }
49085            }
49086          }
49087        }
49088        /**
49089         * Emulate the native mediasource function. abort any soureBuffer
49090         * actions and throw out any un-appended data.
49091         *
49092         * @link https://developer.mozilla.org/en-US/docs/Web/API/SourceBuffer/abort
49093         */
49094  
49095      }, {
49096        key: 'abort',
49097        value: function abort() {
49098          if (this.videoBuffer_) {
49099            this.videoBuffer_.abort();
49100          }
49101  
49102          if (!this.audioDisabled_ && this.audioBuffer_) {
49103            this.audioBuffer_.abort();
49104          }
49105  
49106          if (this.transmuxer_) {
49107            this.transmuxer_.postMessage({
49108              action: 'reset'
49109            });
49110          }
49111  
49112          this.pendingBuffers_.length = 0;
49113          this.bufferUpdating_ = false;
49114        }
49115      }, {
49116        key: 'dispose',
49117        value: function dispose() {
49118          if (this.transmuxer_) {
49119            this.transmuxer_.terminate();
49120          }
49121  
49122          this.trigger('dispose');
49123          this.off();
49124        }
49125      }]);
49126      return VirtualSourceBuffer;
49127    }(videojs$1.EventTarget);
49128    /**
49129     * @file html-media-source.js
49130     */
49131  
49132    /**
49133     * Our MediaSource implementation in HTML, mimics native
49134     * MediaSource where/if possible.
49135     *
49136     * @link https://developer.mozilla.org/en-US/docs/Web/API/MediaSource
49137     * @class HtmlMediaSource
49138     * @extends videojs.EventTarget
49139     */
49140  
49141  
49142    var HtmlMediaSource = function (_videojs$EventTarget) {
49143      inherits$2(HtmlMediaSource, _videojs$EventTarget);
49144  
49145      function HtmlMediaSource() {
49146        classCallCheck$1(this, HtmlMediaSource);
49147  
49148        var _this = possibleConstructorReturn$1(this, (HtmlMediaSource.__proto__ || Object.getPrototypeOf(HtmlMediaSource)).call(this));
49149  
49150        var property = void 0;
49151        _this.nativeMediaSource_ = new window$3.MediaSource(); // delegate to the native MediaSource's methods by default
49152  
49153        for (property in _this.nativeMediaSource_) {
49154          if (!(property in HtmlMediaSource.prototype) && typeof _this.nativeMediaSource_[property] === 'function') {
49155            _this[property] = _this.nativeMediaSource_[property].bind(_this.nativeMediaSource_);
49156          }
49157        } // emulate `duration` and `seekable` until seeking can be
49158        // handled uniformly for live streams
49159        // see https://github.com/w3c/media-source/issues/5
49160  
49161  
49162        _this.duration_ = NaN;
49163        Object.defineProperty(_this, 'duration', {
49164          get: function get$$1() {
49165            if (this.duration_ === Infinity) {
49166              return this.duration_;
49167            }
49168  
49169            return this.nativeMediaSource_.duration;
49170          },
49171          set: function set$$1(duration) {
49172            this.duration_ = duration;
49173  
49174            if (duration !== Infinity) {
49175              this.nativeMediaSource_.duration = duration;
49176              return;
49177            }
49178          }
49179        });
49180        Object.defineProperty(_this, 'seekable', {
49181          get: function get$$1() {
49182            if (this.duration_ === Infinity) {
49183              return videojs$1.createTimeRanges([[0, this.nativeMediaSource_.duration]]);
49184            }
49185  
49186            return this.nativeMediaSource_.seekable;
49187          }
49188        });
49189        Object.defineProperty(_this, 'readyState', {
49190          get: function get$$1() {
49191            return this.nativeMediaSource_.readyState;
49192          }
49193        });
49194        Object.defineProperty(_this, 'activeSourceBuffers', {
49195          get: function get$$1() {
49196            return this.activeSourceBuffers_;
49197          }
49198        }); // the list of virtual and native SourceBuffers created by this
49199        // MediaSource
49200  
49201        _this.sourceBuffers = [];
49202        _this.activeSourceBuffers_ = [];
49203        /**
49204         * update the list of active source buffers based upon various
49205         * imformation from HLS and video.js
49206         *
49207         * @private
49208         */
49209  
49210        _this.updateActiveSourceBuffers_ = function () {
49211          // Retain the reference but empty the array
49212          _this.activeSourceBuffers_.length = 0; // If there is only one source buffer, then it will always be active and audio will
49213          // be disabled based on the codec of the source buffer
49214  
49215          if (_this.sourceBuffers.length === 1) {
49216            var sourceBuffer = _this.sourceBuffers[0];
49217            sourceBuffer.appendAudioInitSegment_ = true;
49218            sourceBuffer.audioDisabled_ = !sourceBuffer.audioCodec_;
49219  
49220            _this.activeSourceBuffers_.push(sourceBuffer);
49221  
49222            return;
49223          } // There are 2 source buffers, a combined (possibly video only) source buffer and
49224          // and an audio only source buffer.
49225          // By default, the audio in the combined virtual source buffer is enabled
49226          // and the audio-only source buffer (if it exists) is disabled.
49227  
49228  
49229          var disableCombined = false;
49230          var disableAudioOnly = true; // TODO: maybe we can store the sourcebuffers on the track objects?
49231          // safari may do something like this
49232  
49233          for (var i = 0; i < _this.player_.audioTracks().length; i++) {
49234            var track = _this.player_.audioTracks()[i];
49235  
49236            if (track.enabled && track.kind !== 'main') {
49237              // The enabled track is an alternate audio track so disable the audio in
49238              // the combined source buffer and enable the audio-only source buffer.
49239              disableCombined = true;
49240              disableAudioOnly = false;
49241              break;
49242            }
49243          }
49244  
49245          _this.sourceBuffers.forEach(function (sourceBuffer, index) {
49246            /* eslinst-disable */
49247            // TODO once codecs are required, we can switch to using the codecs to determine
49248            //      what stream is the video stream, rather than relying on videoTracks
49249  
49250            /* eslinst-enable */
49251            sourceBuffer.appendAudioInitSegment_ = true;
49252  
49253            if (sourceBuffer.videoCodec_ && sourceBuffer.audioCodec_) {
49254              // combined
49255              sourceBuffer.audioDisabled_ = disableCombined;
49256            } else if (sourceBuffer.videoCodec_ && !sourceBuffer.audioCodec_) {
49257              // If the "combined" source buffer is video only, then we do not want
49258              // disable the audio-only source buffer (this is mostly for demuxed
49259              // audio and video hls)
49260              sourceBuffer.audioDisabled_ = true;
49261              disableAudioOnly = false;
49262            } else if (!sourceBuffer.videoCodec_ && sourceBuffer.audioCodec_) {
49263              // audio only
49264              // In the case of audio only with alternate audio and disableAudioOnly is true
49265              // this means we want to disable the audio on the alternate audio sourcebuffer
49266              // but not the main "combined" source buffer. The "combined" source buffer is
49267              // always at index 0, so this ensures audio won't be disabled in both source
49268              // buffers.
49269              sourceBuffer.audioDisabled_ = index ? disableAudioOnly : !disableAudioOnly;
49270  
49271              if (sourceBuffer.audioDisabled_) {
49272                return;
49273              }
49274            }
49275  
49276            _this.activeSourceBuffers_.push(sourceBuffer);
49277          });
49278        };
49279  
49280        _this.onPlayerMediachange_ = function () {
49281          _this.sourceBuffers.forEach(function (sourceBuffer) {
49282            sourceBuffer.appendAudioInitSegment_ = true;
49283          });
49284        };
49285  
49286        _this.onHlsReset_ = function () {
49287          _this.sourceBuffers.forEach(function (sourceBuffer) {
49288            if (sourceBuffer.transmuxer_) {
49289              sourceBuffer.transmuxer_.postMessage({
49290                action: 'resetCaptions'
49291              });
49292            }
49293          });
49294        };
49295  
49296        _this.onHlsSegmentTimeMapping_ = function (event) {
49297          _this.sourceBuffers.forEach(function (buffer) {
49298            return buffer.timeMapping_ = event.mapping;
49299          });
49300        }; // Re-emit MediaSource events on the polyfill
49301  
49302  
49303        ['sourceopen', 'sourceclose', 'sourceended'].forEach(function (eventName) {
49304          this.nativeMediaSource_.addEventListener(eventName, this.trigger.bind(this));
49305        }, _this); // capture the associated player when the MediaSource is
49306        // successfully attached
49307  
49308        _this.on('sourceopen', function (event) {
49309          // Get the player this MediaSource is attached to
49310          var video = document.querySelector('[src="' + _this.url_ + '"]');
49311  
49312          if (!video) {
49313            return;
49314          }
49315  
49316          _this.player_ = videojs$1(video.parentNode);
49317  
49318          if (!_this.player_) {
49319            return;
49320          } // hls-reset is fired by videojs.Hls on to the tech after the main SegmentLoader
49321          // resets its state and flushes the buffer
49322  
49323  
49324          _this.player_.tech_.on('hls-reset', _this.onHlsReset_); // hls-segment-time-mapping is fired by videojs.Hls on to the tech after the main
49325          // SegmentLoader inspects an MTS segment and has an accurate stream to display
49326          // time mapping
49327  
49328  
49329          _this.player_.tech_.on('hls-segment-time-mapping', _this.onHlsSegmentTimeMapping_);
49330  
49331          if (_this.player_.audioTracks && _this.player_.audioTracks()) {
49332            _this.player_.audioTracks().on('change', _this.updateActiveSourceBuffers_);
49333  
49334            _this.player_.audioTracks().on('addtrack', _this.updateActiveSourceBuffers_);
49335  
49336            _this.player_.audioTracks().on('removetrack', _this.updateActiveSourceBuffers_);
49337          }
49338  
49339          _this.player_.on('mediachange', _this.onPlayerMediachange_);
49340        });
49341  
49342        _this.on('sourceended', function (event) {
49343          var duration = durationOfVideo(_this.duration);
49344  
49345          for (var i = 0; i < _this.sourceBuffers.length; i++) {
49346            var sourcebuffer = _this.sourceBuffers[i];
49347            var cues = sourcebuffer.metadataTrack_ && sourcebuffer.metadataTrack_.cues;
49348  
49349            if (cues && cues.length) {
49350              cues[cues.length - 1].endTime = duration;
49351            }
49352          }
49353        }); // explicitly terminate any WebWorkers that were created
49354        // by SourceHandlers
49355  
49356  
49357        _this.on('sourceclose', function (event) {
49358          this.sourceBuffers.forEach(function (sourceBuffer) {
49359            if (sourceBuffer.transmuxer_) {
49360              sourceBuffer.transmuxer_.terminate();
49361            }
49362          });
49363          this.sourceBuffers.length = 0;
49364  
49365          if (!this.player_) {
49366            return;
49367          }
49368  
49369          if (this.player_.audioTracks && this.player_.audioTracks()) {
49370            this.player_.audioTracks().off('change', this.updateActiveSourceBuffers_);
49371            this.player_.audioTracks().off('addtrack', this.updateActiveSourceBuffers_);
49372            this.player_.audioTracks().off('removetrack', this.updateActiveSourceBuffers_);
49373          } // We can only change this if the player hasn't been disposed of yet
49374          // because `off` eventually tries to use the el_ property. If it has
49375          // been disposed of, then don't worry about it because there are no
49376          // event handlers left to unbind anyway
49377  
49378  
49379          if (this.player_.el_) {
49380            this.player_.off('mediachange', this.onPlayerMediachange_);
49381          }
49382  
49383          if (this.player_.tech_ && this.player_.tech_.el_) {
49384            this.player_.tech_.off('hls-reset', this.onHlsReset_);
49385            this.player_.tech_.off('hls-segment-time-mapping', this.onHlsSegmentTimeMapping_);
49386          }
49387        });
49388  
49389        return _this;
49390      }
49391      /**
49392       * Add a range that that can now be seeked to.
49393       *
49394       * @param {Double} start where to start the addition
49395       * @param {Double} end where to end the addition
49396       * @private
49397       */
49398  
49399  
49400      createClass$1(HtmlMediaSource, [{
49401        key: 'addSeekableRange_',
49402        value: function addSeekableRange_(start, end) {
49403          var error = void 0;
49404  
49405          if (this.duration !== Infinity) {
49406            error = new Error('MediaSource.addSeekableRange() can only be invoked ' + 'when the duration is Infinity');
49407            error.name = 'InvalidStateError';
49408            error.code = 11;
49409            throw error;
49410          }
49411  
49412          if (end > this.nativeMediaSource_.duration || isNaN(this.nativeMediaSource_.duration)) {
49413            this.nativeMediaSource_.duration = end;
49414          }
49415        }
49416        /**
49417         * Add a source buffer to the media source.
49418         *
49419         * @link https://developer.mozilla.org/en-US/docs/Web/API/MediaSource/addSourceBuffer
49420         * @param {String} type the content-type of the content
49421         * @return {Object} the created source buffer
49422         */
49423  
49424      }, {
49425        key: 'addSourceBuffer',
49426        value: function addSourceBuffer(type) {
49427          var buffer = void 0;
49428          var parsedType = parseContentType(type); // Create a VirtualSourceBuffer to transmux MPEG-2 transport
49429          // stream segments into fragmented MP4s
49430  
49431          if (/^(video|audio)\/mp2t$/i.test(parsedType.type)) {
49432            var codecs = [];
49433  
49434            if (parsedType.parameters && parsedType.parameters.codecs) {
49435              codecs = parsedType.parameters.codecs.split(',');
49436              codecs = translateLegacyCodecs(codecs);
49437              codecs = codecs.filter(function (codec) {
49438                return isAudioCodec(codec) || isVideoCodec(codec);
49439              });
49440            }
49441  
49442            if (codecs.length === 0) {
49443              codecs = ['avc1.4d400d', 'mp4a.40.2'];
49444            }
49445  
49446            buffer = new VirtualSourceBuffer(this, codecs);
49447  
49448            if (this.sourceBuffers.length !== 0) {
49449              // If another VirtualSourceBuffer already exists, then we are creating a
49450              // SourceBuffer for an alternate audio track and therefore we know that
49451              // the source has both an audio and video track.
49452              // That means we should trigger the manual creation of the real
49453              // SourceBuffers instead of waiting for the transmuxer to return data
49454              this.sourceBuffers[0].createRealSourceBuffers_();
49455              buffer.createRealSourceBuffers_(); // Automatically disable the audio on the first source buffer if
49456              // a second source buffer is ever created
49457  
49458              this.sourceBuffers[0].audioDisabled_ = true;
49459            }
49460          } else {
49461            // delegate to the native implementation
49462            buffer = this.nativeMediaSource_.addSourceBuffer(type);
49463          }
49464  
49465          this.sourceBuffers.push(buffer);
49466          return buffer;
49467        }
49468      }, {
49469        key: 'dispose',
49470        value: function dispose() {
49471          this.trigger('dispose');
49472          this.off();
49473          this.sourceBuffers.forEach(function (buffer) {
49474            if (buffer.dispose) {
49475              buffer.dispose();
49476            }
49477          });
49478          this.sourceBuffers.length = 0;
49479        }
49480      }]);
49481      return HtmlMediaSource;
49482    }(videojs$1.EventTarget);
49483    /**
49484     * @file videojs-contrib-media-sources.js
49485     */
49486  
49487  
49488    var urlCount = 0; // ------------
49489    // Media Source
49490    // ------------
49491    // store references to the media sources so they can be connected
49492    // to a video element (a swf object)
49493    // TODO: can we store this somewhere local to this module?
49494  
49495    videojs$1.mediaSources = {};
49496    /**
49497     * Provide a method for a swf object to notify JS that a
49498     * media source is now open.
49499     *
49500     * @param {String} msObjectURL string referencing the MSE Object URL
49501     * @param {String} swfId the swf id
49502     */
49503  
49504    var open = function open(msObjectURL, swfId) {
49505      var mediaSource = videojs$1.mediaSources[msObjectURL];
49506  
49507      if (mediaSource) {
49508        mediaSource.trigger({
49509          type: 'sourceopen',
49510          swfId: swfId
49511        });
49512      } else {
49513        throw new Error('Media Source not found (Video.js)');
49514      }
49515    };
49516    /**
49517     * Check to see if the native MediaSource object exists and supports
49518     * an MP4 container with both H.264 video and AAC-LC audio.
49519     *
49520     * @return {Boolean} if  native media sources are supported
49521     */
49522  
49523  
49524    var supportsNativeMediaSources = function supportsNativeMediaSources() {
49525      return !!window$3.MediaSource && !!window$3.MediaSource.isTypeSupported && window$3.MediaSource.isTypeSupported('video/mp4;codecs="avc1.4d400d,mp4a.40.2"');
49526    };
49527    /**
49528     * An emulation of the MediaSource API so that we can support
49529     * native and non-native functionality. returns an instance of
49530     * HtmlMediaSource.
49531     *
49532     * @link https://developer.mozilla.org/en-US/docs/Web/API/MediaSource/MediaSource
49533     */
49534  
49535  
49536    var MediaSource = function MediaSource() {
49537      this.MediaSource = {
49538        open: open,
49539        supportsNativeMediaSources: supportsNativeMediaSources
49540      };
49541  
49542      if (supportsNativeMediaSources()) {
49543        return new HtmlMediaSource();
49544      }
49545  
49546      throw new Error('Cannot use create a virtual MediaSource for this video');
49547    };
49548  
49549    MediaSource.open = open;
49550    MediaSource.supportsNativeMediaSources = supportsNativeMediaSources;
49551    /**
49552     * A wrapper around the native URL for our MSE object
49553     * implementation, this object is exposed under videojs.URL
49554     *
49555     * @link https://developer.mozilla.org/en-US/docs/Web/API/URL/URL
49556     */
49557  
49558    var URL$1 = {
49559      /**
49560       * A wrapper around the native createObjectURL for our objects.
49561       * This function maps a native or emulated mediaSource to a blob
49562       * url so that it can be loaded into video.js
49563       *
49564       * @link https://developer.mozilla.org/en-US/docs/Web/API/URL/createObjectURL
49565       * @param {MediaSource} object the object to create a blob url to
49566       */
49567      createObjectURL: function createObjectURL(object) {
49568        var objectUrlPrefix = 'blob:vjs-media-source/';
49569        var url = void 0; // use the native MediaSource to generate an object URL
49570  
49571        if (object instanceof HtmlMediaSource) {
49572          url = window$3.URL.createObjectURL(object.nativeMediaSource_);
49573          object.url_ = url;
49574          return url;
49575        } // if the object isn't an emulated MediaSource, delegate to the
49576        // native implementation
49577  
49578  
49579        if (!(object instanceof HtmlMediaSource)) {
49580          url = window$3.URL.createObjectURL(object);
49581          object.url_ = url;
49582          return url;
49583        } // build a URL that can be used to map back to the emulated
49584        // MediaSource
49585  
49586  
49587        url = objectUrlPrefix + urlCount;
49588        urlCount++; // setup the mapping back to object
49589  
49590        videojs$1.mediaSources[url] = object;
49591        return url;
49592      }
49593    };
49594    videojs$1.MediaSource = MediaSource;
49595    videojs$1.URL = URL$1;
49596    var EventTarget$1$1 = videojs$1.EventTarget,
49597        mergeOptions$2 = videojs$1.mergeOptions;
49598    /**
49599     * Returns a new master manifest that is the result of merging an updated master manifest
49600     * into the original version.
49601     *
49602     * @param {Object} oldMaster
49603     *        The old parsed mpd object
49604     * @param {Object} newMaster
49605     *        The updated parsed mpd object
49606     * @return {Object}
49607     *         A new object representing the original master manifest with the updated media
49608     *         playlists merged in
49609     */
49610  
49611    var updateMaster$1 = function updateMaster$$1(oldMaster, newMaster) {
49612      var noChanges = void 0;
49613      var update = mergeOptions$2(oldMaster, {
49614        // These are top level properties that can be updated
49615        duration: newMaster.duration,
49616        minimumUpdatePeriod: newMaster.minimumUpdatePeriod
49617      }); // First update the playlists in playlist list
49618  
49619      for (var i = 0; i < newMaster.playlists.length; i++) {
49620        var playlistUpdate = updateMaster(update, newMaster.playlists[i]);
49621  
49622        if (playlistUpdate) {
49623          update = playlistUpdate;
49624        } else {
49625          noChanges = true;
49626        }
49627      } // Then update media group playlists
49628  
49629  
49630      forEachMediaGroup(newMaster, function (properties, type, group, label) {
49631        if (properties.playlists && properties.playlists.length) {
49632          var id = properties.playlists[0].id;
49633  
49634          var _playlistUpdate = updateMaster(update, properties.playlists[0]);
49635  
49636          if (_playlistUpdate) {
49637            update = _playlistUpdate; // update the playlist reference within media groups
49638  
49639            update.mediaGroups[type][group][label].playlists[0] = update.playlists[id];
49640            noChanges = false;
49641          }
49642        }
49643      });
49644  
49645      if (noChanges) {
49646        return null;
49647      }
49648  
49649      return update;
49650    };
49651  
49652    var generateSidxKey = function generateSidxKey(sidxInfo) {
49653      // should be non-inclusive
49654      var sidxByteRangeEnd = sidxInfo.byterange.offset + sidxInfo.byterange.length - 1;
49655      return sidxInfo.uri + '-' + sidxInfo.byterange.offset + '-' + sidxByteRangeEnd;
49656    }; // SIDX should be equivalent if the URI and byteranges of the SIDX match.
49657    // If the SIDXs have maps, the two maps should match,
49658    // both `a` and `b` missing SIDXs is considered matching.
49659    // If `a` or `b` but not both have a map, they aren't matching.
49660  
49661  
49662    var equivalentSidx = function equivalentSidx(a, b) {
49663      var neitherMap = Boolean(!a.map && !b.map);
49664      var equivalentMap = neitherMap || Boolean(a.map && b.map && a.map.byterange.offset === b.map.byterange.offset && a.map.byterange.length === b.map.byterange.length);
49665      return equivalentMap && a.uri === b.uri && a.byterange.offset === b.byterange.offset && a.byterange.length === b.byterange.length;
49666    }; // exported for testing
49667  
49668  
49669    var compareSidxEntry = function compareSidxEntry(playlists, oldSidxMapping) {
49670      var newSidxMapping = {};
49671  
49672      for (var id in playlists) {
49673        var playlist = playlists[id];
49674        var currentSidxInfo = playlist.sidx;
49675  
49676        if (currentSidxInfo) {
49677          var key = generateSidxKey(currentSidxInfo);
49678  
49679          if (!oldSidxMapping[key]) {
49680            break;
49681          }
49682  
49683          var savedSidxInfo = oldSidxMapping[key].sidxInfo;
49684  
49685          if (equivalentSidx(savedSidxInfo, currentSidxInfo)) {
49686            newSidxMapping[key] = oldSidxMapping[key];
49687          }
49688        }
49689      }
49690  
49691      return newSidxMapping;
49692    };
49693    /**
49694     *  A function that filters out changed items as they need to be requested separately.
49695     *
49696     *  The method is exported for testing
49697     *
49698     *  @param {Object} masterXml the mpd XML
49699     *  @param {string} srcUrl the mpd url
49700     *  @param {Date} clientOffset a time difference between server and client (passed through and not used)
49701     *  @param {Object} oldSidxMapping the SIDX to compare against
49702     */
49703  
49704  
49705    var filterChangedSidxMappings = function filterChangedSidxMappings(masterXml, srcUrl, clientOffset, oldSidxMapping) {
49706      // Don't pass current sidx mapping
49707      var master = parse(masterXml, {
49708        manifestUri: srcUrl,
49709        clientOffset: clientOffset
49710      });
49711      var videoSidx = compareSidxEntry(master.playlists, oldSidxMapping);
49712      var mediaGroupSidx = videoSidx;
49713      forEachMediaGroup(master, function (properties, mediaType, groupKey, labelKey) {
49714        if (properties.playlists && properties.playlists.length) {
49715          var playlists = properties.playlists;
49716          mediaGroupSidx = mergeOptions$2(mediaGroupSidx, compareSidxEntry(playlists, oldSidxMapping));
49717        }
49718      });
49719      return mediaGroupSidx;
49720    }; // exported for testing
49721  
49722  
49723    var requestSidx_ = function requestSidx_(sidxRange, playlist, xhr, options, finishProcessingFn) {
49724      var sidxInfo = {
49725        // resolve the segment URL relative to the playlist
49726        uri: resolveManifestRedirect(options.handleManifestRedirects, sidxRange.resolvedUri),
49727        // resolvedUri: sidxRange.resolvedUri,
49728        byterange: sidxRange.byterange,
49729        // the segment's playlist
49730        playlist: playlist
49731      };
49732      var sidxRequestOptions = videojs$1.mergeOptions(sidxInfo, {
49733        responseType: 'arraybuffer',
49734        headers: segmentXhrHeaders(sidxInfo)
49735      });
49736      return xhr(sidxRequestOptions, finishProcessingFn);
49737    };
49738  
49739    var DashPlaylistLoader = function (_EventTarget) {
49740      inherits$2(DashPlaylistLoader, _EventTarget); // DashPlaylistLoader must accept either a src url or a playlist because subsequent
49741      // playlist loader setups from media groups will expect to be able to pass a playlist
49742      // (since there aren't external URLs to media playlists with DASH)
49743  
49744      function DashPlaylistLoader(srcUrlOrPlaylist, hls) {
49745        var options = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
49746        var masterPlaylistLoader = arguments[3];
49747        classCallCheck$1(this, DashPlaylistLoader);
49748  
49749        var _this = possibleConstructorReturn$1(this, (DashPlaylistLoader.__proto__ || Object.getPrototypeOf(DashPlaylistLoader)).call(this));
49750  
49751        var _options$withCredenti = options.withCredentials,
49752            withCredentials = _options$withCredenti === undefined ? false : _options$withCredenti,
49753            _options$handleManife = options.handleManifestRedirects,
49754            handleManifestRedirects = _options$handleManife === undefined ? false : _options$handleManife;
49755        _this.hls_ = hls;
49756        _this.withCredentials = withCredentials;
49757        _this.handleManifestRedirects = handleManifestRedirects;
49758  
49759        if (!srcUrlOrPlaylist) {
49760          throw new Error('A non-empty playlist URL or playlist is required');
49761        } // event naming?
49762  
49763  
49764        _this.on('minimumUpdatePeriod', function () {
49765          _this.refreshXml_();
49766        }); // live playlist staleness timeout
49767  
49768  
49769        _this.on('mediaupdatetimeout', function () {
49770          _this.refreshMedia_(_this.media().id);
49771        });
49772  
49773        _this.state = 'HAVE_NOTHING';
49774        _this.loadedPlaylists_ = {}; // initialize the loader state
49775        // The masterPlaylistLoader will be created with a string
49776  
49777        if (typeof srcUrlOrPlaylist === 'string') {
49778          _this.srcUrl = srcUrlOrPlaylist; // TODO: reset sidxMapping between period changes
49779          // once multi-period is refactored
49780  
49781          _this.sidxMapping_ = {};
49782          return possibleConstructorReturn$1(_this);
49783        }
49784  
49785        _this.setupChildLoader(masterPlaylistLoader, srcUrlOrPlaylist);
49786  
49787        return _this;
49788      }
49789  
49790      createClass$1(DashPlaylistLoader, [{
49791        key: 'setupChildLoader',
49792        value: function setupChildLoader(masterPlaylistLoader, playlist) {
49793          this.masterPlaylistLoader_ = masterPlaylistLoader;
49794          this.childPlaylist_ = playlist;
49795        }
49796      }, {
49797        key: 'dispose',
49798        value: function dispose() {
49799          this.trigger('dispose');
49800          this.stopRequest();
49801          this.loadedPlaylists_ = {};
49802          window$3.clearTimeout(this.minimumUpdatePeriodTimeout_);
49803          window$3.clearTimeout(this.mediaRequest_);
49804          window$3.clearTimeout(this.mediaUpdateTimeout);
49805          this.off();
49806        }
49807      }, {
49808        key: 'hasPendingRequest',
49809        value: function hasPendingRequest() {
49810          return this.request || this.mediaRequest_;
49811        }
49812      }, {
49813        key: 'stopRequest',
49814        value: function stopRequest() {
49815          if (this.request) {
49816            var oldRequest = this.request;
49817            this.request = null;
49818            oldRequest.onreadystatechange = null;
49819            oldRequest.abort();
49820          }
49821        }
49822      }, {
49823        key: 'sidxRequestFinished_',
49824        value: function sidxRequestFinished_(playlist, master, startingState, doneFn) {
49825          var _this2 = this;
49826  
49827          return function (err, request) {
49828            // disposed
49829            if (!_this2.request) {
49830              return;
49831            } // pending request is cleared
49832  
49833  
49834            _this2.request = null;
49835  
49836            if (err) {
49837              _this2.error = {
49838                status: request.status,
49839                message: 'DASH playlist request error at URL: ' + playlist.uri,
49840                response: request.response,
49841                // MEDIA_ERR_NETWORK
49842                code: 2
49843              };
49844  
49845              if (startingState) {
49846                _this2.state = startingState;
49847              }
49848  
49849              _this2.trigger('error');
49850  
49851              return doneFn(master, null);
49852            }
49853  
49854            var bytes = new Uint8Array(request.response);
49855            var sidx = mp4Inspector.parseSidx(bytes.subarray(8));
49856            return doneFn(master, sidx);
49857          };
49858        }
49859      }, {
49860        key: 'media',
49861        value: function media(playlist) {
49862          var _this3 = this; // getter
49863  
49864  
49865          if (!playlist) {
49866            return this.media_;
49867          } // setter
49868  
49869  
49870          if (this.state === 'HAVE_NOTHING') {
49871            throw new Error('Cannot switch media playlist from ' + this.state);
49872          }
49873  
49874          var startingState = this.state; // find the playlist object if the target playlist has been specified by URI
49875  
49876          if (typeof playlist === 'string') {
49877            if (!this.master.playlists[playlist]) {
49878              throw new Error('Unknown playlist URI: ' + playlist);
49879            }
49880  
49881            playlist = this.master.playlists[playlist];
49882          }
49883  
49884          var mediaChange = !this.media_ || playlist.id !== this.media_.id; // switch to previously loaded playlists immediately
49885  
49886          if (mediaChange && this.loadedPlaylists_[playlist.id] && this.loadedPlaylists_[playlist.id].endList) {
49887            this.state = 'HAVE_METADATA';
49888            this.media_ = playlist; // trigger media change if the active media has been updated
49889  
49890            if (mediaChange) {
49891              this.trigger('mediachanging');
49892              this.trigger('mediachange');
49893            }
49894  
49895            return;
49896          } // switching to the active playlist is a no-op
49897  
49898  
49899          if (!mediaChange) {
49900            return;
49901          } // switching from an already loaded playlist
49902  
49903  
49904          if (this.media_) {
49905            this.trigger('mediachanging');
49906          }
49907  
49908          if (!playlist.sidx) {
49909            // Continue asynchronously if there is no sidx
49910            // wait one tick to allow haveMaster to run first on a child loader
49911            this.mediaRequest_ = window$3.setTimeout(this.haveMetadata.bind(this, {
49912              startingState: startingState,
49913              playlist: playlist
49914            }), 0); // exit early and don't do sidx work
49915  
49916            return;
49917          } // we have sidx mappings
49918  
49919  
49920          var oldMaster = void 0;
49921          var sidxMapping = void 0; // sidxMapping is used when parsing the masterXml, so store
49922          // it on the masterPlaylistLoader
49923  
49924          if (this.masterPlaylistLoader_) {
49925            oldMaster = this.masterPlaylistLoader_.master;
49926            sidxMapping = this.masterPlaylistLoader_.sidxMapping_;
49927          } else {
49928            oldMaster = this.master;
49929            sidxMapping = this.sidxMapping_;
49930          }
49931  
49932          var sidxKey = generateSidxKey(playlist.sidx);
49933          sidxMapping[sidxKey] = {
49934            sidxInfo: playlist.sidx
49935          };
49936          this.request = requestSidx_(playlist.sidx, playlist, this.hls_.xhr, {
49937            handleManifestRedirects: this.handleManifestRedirects
49938          }, this.sidxRequestFinished_(playlist, oldMaster, startingState, function (newMaster, sidx) {
49939            if (!newMaster || !sidx) {
49940              throw new Error('failed to request sidx');
49941            } // update loader's sidxMapping with parsed sidx box
49942  
49943  
49944            sidxMapping[sidxKey].sidx = sidx; // everything is ready just continue to haveMetadata
49945  
49946            _this3.haveMetadata({
49947              startingState: startingState,
49948              playlist: newMaster.playlists[playlist.id]
49949            });
49950          }));
49951        }
49952      }, {
49953        key: 'haveMetadata',
49954        value: function haveMetadata(_ref) {
49955          var startingState = _ref.startingState,
49956              playlist = _ref.playlist;
49957          this.state = 'HAVE_METADATA';
49958          this.loadedPlaylists_[playlist.id] = playlist;
49959          this.mediaRequest_ = null; // This will trigger loadedplaylist
49960  
49961          this.refreshMedia_(playlist.id);
vendor: 10,724 bytes, lines 49961-50253
49961 // fire loadedmetadata the first time a media playlist is loaded
49962          // to resolve setup of media groups
49963  
49964          if (startingState === 'HAVE_MASTER') {
49965            this.trigger('loadedmetadata');
49966          } else {
49967            // trigger media change if the active media has been updated
49968            this.trigger('mediachange');
49969          }
49970        }
49971      }, {
49972        key: 'pause',
49973        value: function pause() {
49974          this.stopRequest();
49975          window$3.clearTimeout(this.mediaUpdateTimeout);
49976          window$3.clearTimeout(this.minimumUpdatePeriodTimeout_);
49977  
49978          if (this.state === 'HAVE_NOTHING') {
49979            // If we pause the loader before any data has been retrieved, its as if we never
49980            // started, so reset to an unstarted state.
49981            this.started = false;
49982          }
49983        }
49984      }, {
49985        key: 'load',
49986        value: function load(isFinalRendition) {
49987          var _this4 = this;
49988  
49989          window$3.clearTimeout(this.mediaUpdateTimeout);
49990          window$3.clearTimeout(this.minimumUpdatePeriodTimeout_);
49991          var media = this.media();
49992  
49993          if (isFinalRendition) {
49994            var delay = media ? media.targetDuration / 2 * 1000 : 5 * 1000;
49995            this.mediaUpdateTimeout = window$3.setTimeout(function () {
49996              return _this4.load();
49997            }, delay);
49998            return;
49999          } // because the playlists are internal to the manifest, load should either load the
50000          // main manifest, or do nothing but trigger an event
50001  
50002  
50003          if (!this.started) {
50004            this.start();
50005            return;
50006          }
50007  
50008          if (media && !media.endList) {
50009            this.trigger('mediaupdatetimeout');
50010          } else {
50011            this.trigger('loadedplaylist');
50012          }
50013        }
50014        /**
50015         * Parses the master xml string and updates playlist uri references
50016         *
50017         * @return {Object}
50018         *         The parsed mpd manifest object
50019         */
50020  
50021      }, {
50022        key: 'parseMasterXml',
50023        value: function parseMasterXml() {
50024          var master = parse(this.masterXml_, {
50025            manifestUri: this.srcUrl,
50026            clientOffset: this.clientOffset_,
50027            sidxMapping: this.sidxMapping_
50028          });
50029          master.uri = this.srcUrl; // Set up phony URIs for the playlists since we won't have external URIs for DASH
50030          // but reference playlists by their URI throughout the project
50031          // TODO: Should we create the dummy uris in mpd-parser as well (leaning towards yes).
50032  
50033          for (var i = 0; i < master.playlists.length; i++) {
50034            var phonyUri = 'placeholder-uri-' + i;
50035            master.playlists[i].uri = phonyUri;
50036          } // set up phony URIs for the media group playlists since we won't have external
50037          // URIs for DASH but reference playlists by their URI throughout the project
50038  
50039  
50040          forEachMediaGroup(master, function (properties, mediaType, groupKey, labelKey) {
50041            if (properties.playlists && properties.playlists.length) {
50042              var _phonyUri = 'placeholder-uri-' + mediaType + '-' + groupKey + '-' + labelKey;
50043  
50044              var id = createPlaylistID(0, _phonyUri);
50045              properties.playlists[0].uri = _phonyUri;
50046              properties.playlists[0].id = id; // setup ID and URI references (URI for backwards compatibility)
50047  
50048              master.playlists[id] = properties.playlists[0];
50049              master.playlists[_phonyUri] = properties.playlists[0];
50050            }
50051          });
50052          setupMediaPlaylists(master);
50053          resolveMediaGroupUris(master);
50054          return master;
50055        }
50056      }, {
50057        key: 'start',
50058        value: function start() {
50059          var _this5 = this;
50060  
50061          this.started = true; // We don't need to request the master manifest again
50062          // Call this asynchronously to match the xhr request behavior below
50063  
50064          if (this.masterPlaylistLoader_) {
50065            this.mediaRequest_ = window$3.setTimeout(this.haveMaster_.bind(this), 0);
50066            return;
50067          } // request the specified URL
50068  
50069  
50070          this.request = this.hls_.xhr({
50071            uri: this.srcUrl,
50072            withCredentials: this.withCredentials
50073          }, function (error, req) {
50074            // disposed
50075            if (!_this5.request) {
50076              return;
50077            } // clear the loader's request reference
50078  
50079  
50080            _this5.request = null;
50081  
50082            if (error) {
50083              _this5.error = {
50084                status: req.status,
50085                message: 'DASH playlist request error at URL: ' + _this5.srcUrl,
50086                responseText: req.responseText,
50087                // MEDIA_ERR_NETWORK
50088                code: 2
50089              };
50090  
50091              if (_this5.state === 'HAVE_NOTHING') {
50092                _this5.started = false;
50093              }
50094  
50095              return _this5.trigger('error');
50096            }
50097  
50098            _this5.masterXml_ = req.responseText;
50099  
50100            if (req.responseHeaders && req.responseHeaders.date) {
50101              _this5.masterLoaded_ = Date.parse(req.responseHeaders.date);
50102            } else {
50103              _this5.masterLoaded_ = Date.now();
50104            }
50105  
50106            _this5.srcUrl = resolveManifestRedirect(_this5.handleManifestRedirects, _this5.srcUrl, req);
50107  
50108            _this5.syncClientServerClock_(_this5.onClientServerClockSync_.bind(_this5));
50109          });
50110        }
50111        /**
50112         * Parses the master xml for UTCTiming node to sync the client clock to the server
50113         * clock. If the UTCTiming node requires a HEAD or GET request, that request is made.
50114         *
50115         * @param {Function} done
50116         *        Function to call when clock sync has completed
50117         */
50118  
50119      }, {
50120        key: 'syncClientServerClock_',
50121        value: function syncClientServerClock_(done) {
50122          var _this6 = this;
50123  
50124          var utcTiming = parseUTCTiming(this.masterXml_); // No UTCTiming element found in the mpd. Use Date header from mpd request as the
50125          // server clock
50126  
50127          if (utcTiming === null) {
50128            this.clientOffset_ = this.masterLoaded_ - Date.now();
50129            return done();
50130          }
50131  
50132          if (utcTiming.method === 'DIRECT') {
50133            this.clientOffset_ = utcTiming.value - Date.now();
50134            return done();
50135          }
50136  
50137          this.request = this.hls_.xhr({
50138            uri: resolveUrl$1(this.srcUrl, utcTiming.value),
50139            method: utcTiming.method,
50140            withCredentials: this.withCredentials
50141          }, function (error, req) {
50142            // disposed
50143            if (!_this6.request) {
50144              return;
50145            }
50146  
50147            if (error) {
50148              // sync request failed, fall back to using date header from mpd
50149              // TODO: log warning
50150              _this6.clientOffset_ = _this6.masterLoaded_ - Date.now();
50151              return done();
50152            }
50153  
50154            var serverTime = void 0;
50155  
50156            if (utcTiming.method === 'HEAD') {
50157              if (!req.responseHeaders || !req.responseHeaders.date) {
50158                // expected date header not preset, fall back to using date header from mpd
50159                // TODO: log warning
50160                serverTime = _this6.masterLoaded_;
50161              } else {
50162                serverTime = Date.parse(req.responseHeaders.date);
50163              }
50164            } else {
50165              serverTime = Date.parse(req.responseText);
50166            }
50167  
50168            _this6.clientOffset_ = serverTime - Date.now();
50169            done();
50170          });
50171        }
50172      }, {
50173        key: 'haveMaster_',
50174        value: function haveMaster_() {
50175          this.state = 'HAVE_MASTER'; // clear media request
50176  
50177          this.mediaRequest_ = null;
50178  
50179          if (!this.masterPlaylistLoader_) {
50180            this.master = this.parseMasterXml(); // We have the master playlist at this point, so
50181            // trigger this to allow MasterPlaylistController
50182            // to make an initial playlist selection
50183  
50184            this.trigger('loadedplaylist');
50185          } else if (!this.media_) {
50186            // no media playlist was specifically selected so select
50187            // the one the child playlist loader was created with
50188            this.media(this.childPlaylist_);
50189          }
50190        }
50191        /**
50192         * Handler for after client/server clock synchronization has happened. Sets up
50193         * xml refresh timer if specificed by the manifest.
50194         */
50195  
50196      }, {
50197        key: 'onClientServerClockSync_',
50198        value: function onClientServerClockSync_() {
50199          var _this7 = this;
50200  
50201          this.haveMaster_();
50202  
50203          if (!this.hasPendingRequest() && !this.media_) {
50204            this.media(this.master.playlists[0]);
50205          } // TODO: minimumUpdatePeriod can have a value of 0. Currently the manifest will not
50206          // be refreshed when this is the case. The inter-op guide says that when the
50207          // minimumUpdatePeriod is 0, the manifest should outline all currently available
50208          // segments, but future segments may require an update. I think a good solution
50209          // would be to update the manifest at the same rate that the media playlists
50210          // are "refreshed", i.e. every targetDuration.
50211  
50212  
50213          if (this.master && this.master.minimumUpdatePeriod) {
50214            this.minimumUpdatePeriodTimeout_ = window$3.setTimeout(function () {
50215              _this7.trigger('minimumUpdatePeriod');
50216            }, this.master.minimumUpdatePeriod);
50217          }
50218        }
50219        /**
50220         * Sends request to refresh the master xml and updates the parsed master manifest
50221         * TODO: Does the client offset need to be recalculated when the xml is refreshed?
50222         */
50223  
50224      }, {
50225        key: 'refreshXml_',
50226        value: function refreshXml_() {
50227          var _this8 = this; // The srcUrl here *may* need to pass through handleManifestsRedirects when
50228          // sidx is implemented
50229  
50230  
50231          this.request = this.hls_.xhr({
50232            uri: this.srcUrl,
50233            withCredentials: this.withCredentials
50234          }, function (error, req) {
50235            // disposed
50236            if (!_this8.request) {
50237              return;
50238            } // clear the loader's request reference
50239  
50240  
50241            _this8.request = null;
50242  
50243            if (error) {
50244              _this8.error = {
50245                status: req.status,
50246                message: 'DASH playlist request error at URL: ' + _this8.srcUrl,
50247                responseText: req.responseText,
50248                // MEDIA_ERR_NETWORK
50249                code: 2
50250              };
50251  
50252              if (_this8.state === 'HAVE_NOTHING') {
50253                _this8.started = false;
50254              }
50255  
50256              return _this8.trigger('error');
50257            }
50258  
50259            _this8.masterXml_ = req.responseText; // This will filter out updated sidx info from the mapping
50260  
50261            _this8.sidxMapping_ = filterChangedSidxMappings(_this8.masterXml_, _this8.srcUrl, _this8.clientOffset_, _this8.sidxMapping_);
50262  
50263            var master = _this8.parseMasterXml();
50264  
50265            var updatedMaster = updateMaster$1(_this8.master, master);
50266  
50267            var currentSidxInfo = _this8.media().sidx;
50268  
50269            if (updatedMaster) {
50270              if (currentSidxInfo) {
50271                var sidxKey = generateSidxKey(currentSidxInfo); // the sidx was updated, so the previous mapping was removed
50272  
50273                if (!_this8.sidxMapping_[sidxKey]) {
50274                  var playlist = _this8.media();
50275  
50276                  _this8.request = requestSidx_(playlist.sidx, playlist, _this8.hls_.xhr, {
50277                    handleManifestRedirects: _this8.handleManifestRedirects
50278                  }, _this8.sidxRequestFinished_(playlist, master, _this8.state, function (newMaster, sidx) {
50279                    if (!newMaster || !sidx) {
50280                      throw new Error('failed to request sidx on minimumUpdatePeriod');
50281                    } // update loader's sidxMapping with parsed sidx box
50282  
50283  
50284                    _this8.sidxMapping_[sidxKey].sidx = sidx;
50285                    _this8.minimumUpdatePeriodTimeout_ = window$3.setTimeout(function () {
50286                      _this8.trigger('minimumUpdatePeriod');
50287                    }, _this8.master.minimumUpdatePeriod); // TODO: do we need to reload the current playlist?
50288  
50289                    _this8.refreshMedia_(_this8.media().id);
50290  
50291                    return;
50292                  }));
50293                }
50294              } else {
50295                _this8.master = updatedMaster;
50296              }
50297            }
50298  
50299            _this8.minimumUpdatePeriodTimeout_ = window$3.setTimeout(function () {
50300              _this8.trigger('minimumUpdatePeriod');
50301            }, _this8.master.minimumUpdatePeriod);
50302          });
50303        }
50304        /**
50305         * Refreshes the media playlist by re-parsing the master xml and updating playlist
50306         * references. If this is an alternate loader, the updated parsed manifest is retrieved
50307         * from the master loader.
50308         */
50309  
50310      }, {
50311        key: 'refreshMedia_',
50312        value: function refreshMedia_(mediaID) {
50313          var _this9 = this;
50314  
50315          if (!mediaID) {
50316            throw new Error('refreshMedia_ must take a media id');
50317          }
50318  
50319          var oldMaster = void 0;
50320          var newMaster = void 0;
50321  
50322          if (this.masterPlaylistLoader_) {
50323            oldMaster = this.masterPlaylistLoader_.master;
50324            newMaster = this.masterPlaylistLoader_.parseMasterXml();
50325          } else {
50326            oldMaster = this.master;
50327            newMaster = this.parseMasterXml();
50328          }
50329  
50330          var updatedMaster = updateMaster$1(oldMaster, newMaster);
50331  
50332          if (updatedMaster) {
50333            if (this.masterPlaylistLoader_) {
50334              this.masterPlaylistLoader_.master = updatedMaster;
50335            } else {
50336              this.master = updatedMaster;
50337            }
50338  
50339            this.media_ = updatedMaster.playlists[mediaID];
50340          } else {
50341            this.media_ = newMaster.playlists[mediaID];
50342            this.trigger('playlistunchanged');
50343          }
50344  
50345          if (!this.media().endList) {
50346            this.mediaUpdateTimeout = window$3.setTimeout(function () {
50347              _this9.trigger('mediaupdatetimeout');
50348            }, refreshDelay(this.media(), !!updatedMaster));
50349          }
50350  
50351          this.trigger('loadedplaylist');
50352        }
50353      }]);
50354      return DashPlaylistLoader;
50355    }(EventTarget$1$1);
50356  
50357    var logger = function logger(source) {
50358      if (videojs$1.log.debug) {
50359        return videojs$1.log.debug.bind(videojs$1, 'VHS:', source + ' >');
50360      }
50361  
50362      return function () {};
50363    };
50364  
50365    function noop$1() {}
50366    /**
50367     * @file source-updater.js
50368     */
50369  
50370    /**
50371     * A queue of callbacks to be serialized and applied when a
50372     * MediaSource and its associated SourceBuffers are not in the
50373     * updating state. It is used by the segment loader to update the
50374     * underlying SourceBuffers when new data is loaded, for instance.
50375     *
50376     * @class SourceUpdater
50377     * @param {MediaSource} mediaSource the MediaSource to create the
50378     * SourceBuffer from
50379     * @param {String} mimeType the desired MIME type of the underlying
50380     * SourceBuffer
50381     * @param {Object} sourceBufferEmitter an event emitter that fires when a source buffer is
50382     * added to the media source
50383     */
50384  
50385  
50386    var SourceUpdater = function () {
50387      function SourceUpdater(mediaSource, mimeType, type, sourceBufferEmitter) {
50388        classCallCheck$1(this, SourceUpdater);
50389        this.callbacks_ = [];
50390        this.pendingCallback_ = null;
50391        this.timestampOffset_ = 0;
50392        this.mediaSource = mediaSource;
50393        this.processedAppend_ = false;
50394        this.type_ = type;
50395        this.mimeType_ = mimeType;
50396        this.logger_ = logger('SourceUpdater[' + type + '][' + mimeType + ']');
50397  
50398        if (mediaSource.readyState === 'closed') {
50399          mediaSource.addEventListener('sourceopen', this.createSourceBuffer_.bind(this, mimeType, sourceBufferEmitter));
50400        } else {
50401          this.createSourceBuffer_(mimeType, sourceBufferEmitter);
50402        }
50403      }
50404  
50405      createClass$1(SourceUpdater, [{
50406        key: 'createSourceBuffer_',
50407        value: function createSourceBuffer_(mimeType, sourceBufferEmitter) {
50408          var _this = this;
50409  
50410          this.sourceBuffer_ = this.mediaSource.addSourceBuffer(mimeType);
50411          this.logger_('created SourceBuffer');
50412  
50413          if (sourceBufferEmitter) {
50414            sourceBufferEmitter.trigger('sourcebufferadded');
50415  
50416            if (this.mediaSource.sourceBuffers.length < 2) {
50417              // There's another source buffer we must wait for before we can start updating
50418              // our own (or else we can get into a bad state, i.e., appending video/audio data
50419              // before the other video/audio source buffer is available and leading to a video
50420              // or audio only buffer).
50421              sourceBufferEmitter.on('sourcebufferadded', function () {
50422                _this.start_();
50423              });
50424              return;
50425            }
50426          }
50427  
50428          this.start_();
50429        }
50430      }, {
50431        key: 'start_',
50432        value: function start_() {
50433          var _this2 = this;
50434  
50435          this.started_ = true; // run completion handlers and process callbacks as updateend
50436          // events fire
50437  
50438          this.onUpdateendCallback_ = function () {
50439            var pendingCallback = _this2.pendingCallback_;
50440            _this2.pendingCallback_ = null;
50441            _this2.sourceBuffer_.removing = false;
50442  
50443            _this2.logger_('buffered [' + printableRange(_this2.buffered()) + ']');
50444  
50445            if (pendingCallback) {
50446              pendingCallback();
50447            }
50448  
50449            _this2.runCallback_();
50450          };
50451  
50452          this.sourceBuffer_.addEventListener('updateend', this.onUpdateendCallback_);
50453          this.runCallback_();
50454        }
50455        /**
50456         * Aborts the current segment and resets the segment parser.
50457         *
50458         * @param {Function} done function to call when done
50459         * @see http://w3c.github.io/media-source/#widl-SourceBuffer-abort-void
50460         */
50461  
50462      }, {
50463        key: 'abort',
50464        value: function abort(done) {
50465          var _this3 = this;
50466  
50467          if (this.processedAppend_) {
50468            this.queueCallback_(function () {
50469              _this3.sourceBuffer_.abort();
50470            }, done);
50471          }
50472        }
50473        /**
50474         * Queue an update to append an ArrayBuffer.
50475         *
50476         * @param {ArrayBuffer} bytes
50477         * @param {Function} done the function to call when done
50478         * @see http://www.w3.org/TR/media-source/#widl-SourceBuffer-appendBuffer-void-ArrayBuffer-data
50479         */
50480  
50481      }, {
50482        key: 'appendBuffer',
50483        value: function appendBuffer(config, done) {
50484          var _this4 = this;
50485  
50486          this.processedAppend_ = true;
50487          this.queueCallback_(function () {
50488            if (config.videoSegmentTimingInfoCallback) {
50489              _this4.sourceBuffer_.addEventListener('videoSegmentTimingInfo', config.videoSegmentTimingInfoCallback);
50490            }
50491  
50492            _this4.sourceBuffer_.appendBuffer(config.bytes);
50493          }, function () {
50494            if (config.videoSegmentTimingInfoCallback) {
50495              _this4.sourceBuffer_.removeEventListener('videoSegmentTimingInfo', config.videoSegmentTimingInfoCallback);
50496            }
50497  
50498            done();
50499          });
50500        }
50501        /**
50502         * Indicates what TimeRanges are buffered in the managed SourceBuffer.
50503         *
50504         * @see http://www.w3.org/TR/media-source/#widl-SourceBuffer-buffered
50505         */
50506  
50507      }, {
50508        key: 'buffered',
50509        value: function buffered() {
50510          if (!this.sourceBuffer_) {
50511            return videojs$1.createTimeRanges();
50512          }
50513  
50514          return this.sourceBuffer_.buffered;
50515        }
50516        /**
50517         * Queue an update to remove a time range from the buffer.
50518         *
50519         * @param {Number} start where to start the removal
50520         * @param {Number} end where to end the removal
50521         * @param {Function} [done=noop] optional callback to be executed when the remove
50522         * operation is complete
50523         * @see http://www.w3.org/TR/media-source/#widl-SourceBuffer-remove-void-double-start-unrestricted-double-end
50524         */
50525  
50526      }, {
50527        key: 'remove',
50528        value: function remove(start, end) {
50529          var _this5 = this;
50530  
50531          var done = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : noop$1;
50532  
50533          if (this.processedAppend_) {
50534            this.queueCallback_(function () {
50535              _this5.logger_('remove [' + start + ' => ' + end + ']');
50536  
50537              _this5.sourceBuffer_.removing = true;
50538  
50539              _this5.sourceBuffer_.remove(start, end);
50540            }, done);
50541          }
50542        }
50543        /**
50544         * Whether the underlying sourceBuffer is updating or not
50545         *
50546         * @return {Boolean} the updating status of the SourceBuffer
50547         */
50548  
50549      }, {
50550        key: 'updating',
50551        value: function updating() {
50552          // we are updating if the sourcebuffer is updating or
50553          return !this.sourceBuffer_ || this.sourceBuffer_.updating || // if we have a pending c
vendor: 8,227 bytes, lines 50553-50813
50553allback that is not our internal noop
50554          !!this.pendingCallback_ && this.pendingCallback_ !== noop$1;
50555        }
50556        /**
50557         * Set/get the timestampoffset on the SourceBuffer
50558         *
50559         * @return {Number} the timestamp offset
50560         */
50561  
50562      }, {
50563        key: 'timestampOffset',
50564        value: function timestampOffset(offset) {
50565          var _this6 = this;
50566  
50567          if (typeof offset !== 'undefined') {
50568            this.queueCallback_(function () {
50569              _this6.sourceBuffer_.timestampOffset = offset;
50570  
50571              _this6.runCallback_();
50572            });
50573            this.timestampOffset_ = offset;
50574          }
50575  
50576          return this.timestampOffset_;
50577        }
50578        /**
50579         * Queue a callback to run
50580         */
50581  
50582      }, {
50583        key: 'queueCallback_',
50584        value: function queueCallback_(callback, done) {
50585          this.callbacks_.push([callback.bind(this), done]);
50586          this.runCallback_();
50587        }
50588        /**
50589         * Run a queued callback
50590         */
50591  
50592      }, {
50593        key: 'runCallback_',
50594        value: function runCallback_() {
50595          var callbacks = void 0;
50596  
50597          if (!this.updating() && this.callbacks_.length && this.started_) {
50598            callbacks = this.callbacks_.shift();
50599            this.pendingCallback_ = callbacks[1];
50600            callbacks[0]();
50601          }
50602        }
50603        /**
50604         * dispose of the source updater and the underlying sourceBuffer
50605         */
50606  
50607      }, {
50608        key: 'dispose',
50609        value: function dispose() {
50610          var _this7 = this;
50611  
50612          var disposeFn = function disposeFn() {
50613            if (_this7.sourceBuffer_ && _this7.mediaSource.readyState === 'open') {
50614              _this7.sourceBuffer_.abort();
50615            }
50616  
50617            _this7.sourceBuffer_.removeEventListener('updateend', disposeFn);
50618          };
50619  
50620          this.sourceBuffer_.removeEventListener('updateend', this.onUpdateendCallback_);
50621  
50622          if (this.sourceBuffer_.removing) {
50623            this.sourceBuffer_.addEventListener('updateend', disposeFn);
50624          } else {
50625            disposeFn();
50626          }
50627        }
50628      }]);
50629      return SourceUpdater;
50630    }();
50631  
50632    var Config = {
50633      GOAL_BUFFER_LENGTH: 30,
50634      MAX_GOAL_BUFFER_LENGTH: 60,
50635      GOAL_BUFFER_LENGTH_RATE: 1,
50636      // 0.5 MB/s
50637      INITIAL_BANDWIDTH: 4194304,
50638      // A fudge factor to apply to advertised playlist bitrates to account for
50639      // temporary flucations in client bandwidth
50640      BANDWIDTH_VARIANCE: 1.2,
50641      // How much of the buffer must be filled before we consider upswitching
50642      BUFFER_LOW_WATER_LINE: 0,
50643      MAX_BUFFER_LOW_WATER_LINE: 30,
50644      BUFFER_LOW_WATER_LINE_RATE: 1
50645    };
50646    var REQUEST_ERRORS = {
50647      FAILURE: 2,
50648      TIMEOUT: -101,
50649      ABORTED: -102
50650    };
50651    /**
50652     * Abort all requests
50653     *
50654     * @param {Object} activeXhrs - an object that tracks all XHR requests
50655     */
50656  
50657    var abortAll = function abortAll(activeXhrs) {
50658      activeXhrs.forEach(function (xhr) {
50659        xhr.abort();
50660      });
50661    };
50662    /**
50663     * Gather important bandwidth stats once a request has completed
50664     *
50665     * @param {Object} request - the XHR request from which to gather stats
50666     */
50667  
50668  
50669    var getRequestStats = function getRequestStats(request) {
50670      return {
50671        bandwidth: request.bandwidth,
50672        bytesReceived: request.bytesReceived || 0,
50673        roundTripTime: request.roundTripTime || 0
50674      };
50675    };
50676    /**
50677     * If possible gather bandwidth stats as a request is in
50678     * progress
50679     *
50680     * @param {Event} progressEvent - an event object from an XHR's progress event
50681     */
50682  
50683  
50684    var getProgressStats = function getProgressStats(progressEvent) {
50685      var request = progressEvent.target;
50686      var roundTripTime = Date.now() - request.requestTime;
50687      var stats = {
50688        bandwidth: Infinity,
50689        bytesReceived: 0,
50690        roundTripTime: roundTripTime || 0
50691      };
50692      stats.bytesReceived = progressEvent.loaded; // This can result in Infinity if stats.roundTripTime is 0 but that is ok
50693      // because we should only use bandwidth stats on progress to determine when
50694      // abort a request early due to insufficient bandwidth
50695  
50696      stats.bandwidth = Math.floor(stats.bytesReceived / stats.roundTripTime * 8 * 1000);
50697      return stats;
50698    };
50699    /**
50700     * Handle all error conditions in one place and return an object
50701     * with all the information
50702     *
50703     * @param {Error|null} error - if non-null signals an error occured with the XHR
50704     * @param {Object} request -  the XHR request that possibly generated the error
50705     */
50706  
50707  
50708    var handleErrors = function handleErrors(error, request) {
50709      if (request.timedout) {
50710        return {
50711          status: request.status,
50712          message: 'HLS request timed-out at URL: ' + request.uri,
50713          code: REQUEST_ERRORS.TIMEOUT,
50714          xhr: request
50715        };
50716      }
50717  
50718      if (request.aborted) {
50719        return {
50720          status: request.status,
50721          message: 'HLS request aborted at URL: ' + request.uri,
50722          code: REQUEST_ERRORS.ABORTED,
50723          xhr: request
50724        };
50725      }
50726  
50727      if (error) {
50728        return {
50729          status: request.status,
50730          message: 'HLS request errored at URL: ' + request.uri,
50731          code: REQUEST_ERRORS.FAILURE,
50732          xhr: request
50733        };
50734      }
50735  
50736      return null;
50737    };
50738    /**
50739     * Handle responses for key data and convert the key data to the correct format
50740     * for the decryption step later
50741     *
50742     * @param {Object} segment - a simplified copy of the segmentInfo object
50743     *                           from SegmentLoader
50744     * @param {Function} finishProcessingFn - a callback to execute to continue processing
50745     *                                        this request
50746     */
50747  
50748  
50749    var handleKeyResponse = function handleKeyResponse(segment, finishProcessingFn) {
50750      return function (error, request) {
50751        var response = request.response;
50752        var errorObj = handleErrors(error, request);
50753  
50754        if (errorObj) {
50755          return finishProcessingFn(errorObj, segment);
50756        }
50757  
50758        if (response.byteLength !== 16) {
50759          return finishProcessingFn({
50760            status: request.status,
50761            message: 'Invalid HLS key at URL: ' + request.uri,
50762            code: REQUEST_ERRORS.FAILURE,
50763            xhr: request
50764          }, segment);
50765        }
50766  
50767        var view = new DataView(response);
50768        segment.key.bytes = new Uint32Array([view.getUint32(0), view.getUint32(4), view.getUint32(8), view.getUint32(12)]);
50769        return finishProcessingFn(null, segment);
50770      };
50771    };
50772    /**
50773     * Handle init-segment responses
50774     *
50775     * @param {Object} segment - a simplified copy of the segmentInfo object
50776     *                           from SegmentLoader
50777     * @param {Function} finishProcessingFn - a callback to execute to continue processing
50778     *                                        this request
50779     */
50780  
50781  
50782    var handleInitSegmentResponse = function handleInitSegmentResponse(segment, captionParser, finishProcessingFn) {
50783      return function (error, request) {
50784        var response = request.response;
50785        var errorObj = handleErrors(error, request);
50786  
50787        if (errorObj) {
50788          return finishProcessingFn(errorObj, segment);
50789        } // stop processing if received empty content
50790  
50791  
50792        if (response.byteLength === 0) {
50793          return finishProcessingFn({
50794            status: request.status,
50795            message: 'Empty HLS segment content at URL: ' + request.uri,
50796            code: REQUEST_ERRORS.FAILURE,
50797            xhr: request
50798          }, segment);
50799        }
50800  
50801        segment.map.bytes = new Uint8Array(request.response); // Initialize CaptionParser if it hasn't been yet
50802  
50803        if (captionParser && !captionParser.isInitialized()) {
50804          captionParser.init();
50805        }
50806  
50807        segment.map.timescales = probe.timescale(segment.map.bytes);
50808        segment.map.videoTrackIds = probe.videoTrackIds(segment.map.bytes);
50809        return finishProcessingFn(null, segment);
50810      };
50811    };
50812    /**
50813     * Response handler for segment-requests being sure to set the correct
50814     * property depending on whether the segment is encryped or not
50815     * Also records and keeps track of stats that are used for ABR purposes
50816     *
50817     * @param {Object} segment - a simplified copy of the segmentInfo object
50818     *                           from SegmentLoader
50819     * @param {Function} finishProcessingFn - a callback to execute to continue processing
50820     *                                        this request
50821     */
50822  
50823  
50824    var handleSegmentResponse = function handleSegmentResponse(segment, captionParser, finishProcessingFn) {
50825      return function (error, request) {
50826        var response = request.response;
50827        var errorObj = handleErrors(error, request);
50828        var parsed = void 0;
50829  
50830        if (errorObj) {
50831          return finishProcessingFn(errorObj, segment);
50832        } // stop processing if received empty content
50833  
50834  
50835        if (response.byteLength === 0) {
50836          return finishProcessingFn({
50837            status: request.status,
50838            message: 'Empty HLS segment content at URL: ' + request.uri,
50839            code: REQUEST_ERRORS.FAILURE,
50840            xhr: request
50841          }, segment);
50842        }
50843  
50844        segment.stats = getRequestStats(request);
50845  
50846        if (segment.key) {
50847          segment.encryptedBytes = new Uint8Array(request.response);
50848        } else {
50849          segment.bytes = new Uint8Array(request.response);
50850        } // This is likely an FMP4 and has the init segment.
50851        // Run through the CaptionParser in case there are captions.
50852  
50853  
50854        if (captionParser && segment.map && segment.map.bytes) {
50855          // Initialize CaptionParser if it hasn't been yet
50856          if (!captionParser.isInitialized()) {
50857            captionParser.init();
50858          }
50859  
50860          parsed = captionParser.parse(segment.bytes, segment.map.videoTrackIds, segment.map.timescales);
50861  
50862          if (parsed && parsed.captions) {
50863            segment.captionStreams = parsed.captionStreams;
50864            segment.fmp4Captions = parsed.captions;
50865          }
50866        }
50867  
50868        return finishProcessingFn(null, segment);
50869      };
50870    };
50871    /**
50872     * Decrypt the segment via the decryption web worker
50873     *
50874     * @param {WebWorker} decrypter - a WebWorker interface to AES-128 decryption routines
50875     * @param {Object} segment - a simplified copy of the segmentInfo object
50876     *                           from SegmentLoader
50877     * @param {Function} doneFn - a callback that is executed after decryption has completed
50878     */
50879  
50880  
50881    var decryptSegment = function decryptSegment(decrypter, segment, doneFn) {
50882      var decryptionHandler = function decryptionHandler(event) {
50883        if (event.data.source === segment.requestId) {
50884          decrypter.removeEventListener('message', decryptionHandler);
50885          var decrypted = event.data.decrypted;
50886          segment.bytes = new Uint8Array(decrypted.bytes, decrypted.byteOffset, decrypted.byteLength);
50887          return doneFn(null, segment);
50888        }
50889      };
50890  
50891      decrypter.addEventListener('message', decryptionHandler);
50892      var keyBytes = void 0;
50893  
50894      if (segment.key.bytes.slice) {
50895        keyBytes = segment.key.bytes.slice();
50896      } else {
50897        keyBytes = new Uint32Array(Array.prototype.slice.call(segment.key.bytes));
50898      } // this is an encrypted segment
50899      // incrementally decrypt the segment
50900  
50901  
50902      decrypter.postMessage(createTransferableMessage({
50903        source: segment.requestId,
50904        encrypted: segment.encryptedBytes,
50905        key: keyBytes,
50906        iv: segment.key.iv
50907      }), [segment.encryptedBytes.buffer, keyBytes.buffer]);
50908    };
50909    /**
50910     * This function waits for all XHRs to finish (with either success or failure)
50911     * before continueing processing via it's callback. The function gathers errors
50912     * from each request into a single errors array so that the error status for
50913     * each request can be examined later.
50914     *
50915     * @param {Object} activeXhrs - an object that tracks all XHR requests
50916     * @param {WebWorker} decrypter - a WebWorker interface to AES-128 decryption routines
50917     * @param {Function} doneFn - a callback that is executed after all resources have been
50918     *                            downloaded and any decryption completed
50919     */
50920  
50921  
50922    var waitForCompletion = function waitForCompletion(activeXhrs, decrypter, doneFn) {
50923      var count = 0;
50924      var didError = false;
50925      return function (error, segment) {
50926        if (didError) {
50927          return;
50928        }
50929  
50930        if (error) {
50931          didError = true; // If there are errors, we have to abort any outstanding requests
50932  
50933          abortAll(activeXhrs); // Even though the requests above are aborted, and in theory we could wait until we
50934          // handle the aborted events from those requests, there are some cases where we may
50935          // never get an aborted event. For instance, if the network connection is lost and
50936          // there were two requests, the first may have triggered an error immediately, while
50937          // the second request remains unsent. In that case, the aborted algorithm will not
50938          // trigger an abort: see https://xhr.spec.whatwg.org/#the-abort()-method
50939          //
50940          // We also can't rely on the ready state of the XHR, since the request that
50941          // triggered the connection error may also show as a ready state of 0 (unsent).
50942          // Therefore, we have to finish this group of requests immediately after the first
50943          // seen error.
50944  
50945          return doneFn(error, segment);
50946        }
50947  
50948        count += 1;
50949  
50950        if (count === activeXhrs.length) {
50951          // Keep track of when *all* of the requests have completed
50952          segment.endOfAllRequests = Date.now();
50953  
50954          if (segment.encryptedBytes) {
50955            return decryptSegment(decrypter, segment, doneFn);
50956          } // Otherwise, everything is ready just continue
50957  
50958  
50959          return doneFn(null, segment);
50960        }
50961      };
50962    };
50963    /**
50964     * Simple progress event callback handler that gathers some stats before
50965     * executing a provided callback with the `segment` object
50966     *
50967     * @param {Object} segment - a simplified copy of the segmentInfo object
50968     *                           from SegmentLoader
50969     * @param {Function} progressFn - a callback that is executed each time a progress event
50970     *                                is received
50971     * @param {Event} event - the progress event object from XMLHttpRequest
50972     */
50973  
50974  
50975    var handleProgress = function handleProgress(segment, progressFn) {
50976      return function (event) {
50977        segment.stats = videojs$1.mergeOptions(segment.stats, getProgressStats(event)); // record the time that we receive the first byte of data
50978  
50979        if (!segment.stats.firstBytesReceivedAt && segment.stats.bytesReceived) {
50980          segment.stats.firstBytesReceivedAt = Date.now();
50981        }
50982  
50983        return progressFn(event, segment);
50984      };
50985    };
50986    /**
50987     * Load all resources and does any processing necessary for a media-segment
50988     *
50989     * Features:
50990     *   decrypts the media-segment if it has a key uri and an iv
50991     *   aborts *all* requests if *any* one request fails
50992     *
50993     * The segment object, at minimum, has the following format:
50994     * {
50995     *   resolvedUri: String,
50996     *   [byterange]: {
50997     *     offset: Number,
50998     *     length: Number
50999     *   },
51000     *   [key]: {
51001     *     resolvedUri: String
51002     *     [byterange]: {
51003     *       offset: Number,
51004     *       length: Number
51005     *     },
51006     *     iv: {
51007     *       bytes: Uint32Array
51008     *     }
51009     *   },
51010     *   [map]: {
51011     *     resolvedUri: String,
51012     *     [byterange]: {
51013     *       offset: Number,
51014     *       length: Number
51015     *     },
51016     *     [bytes]: Uint8Array
51017     *   }
51018     * }
51019     * ...where [name] denotes optional properties
51020     *
51021     * @param {Function} xhr - an instance of the xhr wrapper in xhr.js
51022     * @param {Object} xhrOptions - the base options to provide to all xhr requests
51023     * @param {WebWorker} decryptionWorker - a WebWorker interface to AES-128
51024     *                                       decryption routines
51025     * @param {Object} segment - a simplified copy of the segmentInfo object
51026     *                           from SegmentLoader
51027     * @param {Function} progressFn - a callback that receives progress events from the main
51028     *                                segment's xhr request
51029     * @param {Function} doneFn - a callback that is executed only once all requests have
51030     *                            succeeded or failed
51031     * @returns {Function} a function that, when invoked, immediately aborts all
51032     *                     outstanding requests
51033     */
51034  
51035  
51036    var mediaSegmentRequest = function mediaSegmentRequest(xhr, xhrOptions, decryptionWorker, captionParser, segment, progressFn, doneFn) {
51037      var activeXhrs = [];
vendor: 172,335 bytes, lines 51038-55523
51038      var finishProcessingFn = waitForCompletion(activeXhrs, decryptionWorker, doneFn); // optionally, request the decryption key
51039  
51040      if (segment.key && !segment.key.bytes) {
51041        var keyRequestOptions = videojs$1.mergeOptions(xhrOptions, {
51042          uri: segment.key.resolvedUri,
51043          responseType: 'arraybuffer'
51044        });
51045        var keyRequestCallback = handleKeyResponse(segment, finishProcessingFn);
51046        var keyXhr = xhr(keyRequestOptions, keyRequestCallback);
51047        activeXhrs.push(keyXhr);
51048      } // optionally, request the associated media init segment
51049  
51050  
51051      if (segment.map && !segment.map.bytes) {
51052        var initSegmentOptions = videojs$1.mergeOptions(xhrOptions, {
51053          uri: segment.map.resolvedUri,
51054          responseType: 'arraybuffer',
51055          headers: segmentXhrHeaders(segment.map)
51056        });
51057        var initSegmentRequestCallback = handleInitSegmentResponse(segment, captionParser, finishProcessingFn);
51058        var initSegmentXhr = xhr(initSegmentOptions, initSegmentRequestCallback);
51059        activeXhrs.push(initSegmentXhr);
51060      }
51061  
51062      var segmentRequestOptions = videojs$1.mergeOptions(xhrOptions, {
51063        uri: segment.resolvedUri,
51064        responseType: 'arraybuffer',
51065        headers: segmentXhrHeaders(segment)
51066      });
51067      var segmentRequestCallback = handleSegmentResponse(segment, captionParser, finishProcessingFn);
51068      var segmentXhr = xhr(segmentRequestOptions, segmentRequestCallback);
51069      segmentXhr.addEventListener('progress', handleProgress(segment, progressFn));
51070      activeXhrs.push(segmentXhr);
51071      return function () {
51072        return abortAll(activeXhrs);
51073      };
51074    }; // Utilities
51075  
51076    /**
51077     * Returns the CSS value for the specified property on an element
51078     * using `getComputedStyle`. Firefox has a long-standing issue where
51079     * getComputedStyle() may return null when running in an iframe with
51080     * `display: none`.
51081     *
51082     * @see https://bugzilla.mozilla.org/show_bug.cgi?id=548397
51083     * @param {HTMLElement} el the htmlelement to work on
51084     * @param {string} the proprety to get the style for
51085     */
51086  
51087  
51088    var safeGetComputedStyle = function safeGetComputedStyle(el, property) {
51089      var result = void 0;
51090  
51091      if (!el) {
51092        return '';
51093      }
51094  
51095      result = window$3.getComputedStyle(el);
51096  
51097      if (!result) {
51098        return '';
51099      }
51100  
51101      return result[property];
51102    };
51103    /**
51104     * Resuable stable sort function
51105     *
51106     * @param {Playlists} array
51107     * @param {Function} sortFn Different comparators
51108     * @function stableSort
51109     */
51110  
51111  
51112    var stableSort = function stableSort(array, sortFn) {
51113      var newArray = array.slice();
51114      array.sort(function (left, right) {
51115        var cmp = sortFn(left, right);
51116  
51117        if (cmp === 0) {
51118          return newArray.indexOf(left) - newArray.indexOf(right);
51119        }
51120  
51121        return cmp;
51122      });
51123    };
51124    /**
51125     * A comparator function to sort two playlist object by bandwidth.
51126     *
51127     * @param {Object} left a media playlist object
51128     * @param {Object} right a media playlist object
51129     * @return {Number} Greater than zero if the bandwidth attribute of
51130     * left is greater than the corresponding attribute of right. Less
51131     * than zero if the bandwidth of right is greater than left and
51132     * exactly zero if the two are equal.
51133     */
51134  
51135  
51136    var comparePlaylistBandwidth = function comparePlaylistBandwidth(left, right) {
51137      var leftBandwidth = void 0;
51138      var rightBandwidth = void 0;
51139  
51140      if (left.attributes.BANDWIDTH) {
51141        leftBandwidth = left.attributes.BANDWIDTH;
51142      }
51143  
51144      leftBandwidth = leftBandwidth || window$3.Number.MAX_VALUE;
51145  
51146      if (right.attributes.BANDWIDTH) {
51147        rightBandwidth = right.attributes.BANDWIDTH;
51148      }
51149  
51150      rightBandwidth = rightBandwidth || window$3.Number.MAX_VALUE;
51151      return leftBandwidth - rightBandwidth;
51152    };
51153    /**
51154     * A comparator function to sort two playlist object by resolution (width).
51155     * @param {Object} left a media playlist object
51156     * @param {Object} right a media playlist object
51157     * @return {Number} Greater than zero if the resolution.width attribute of
51158     * left is greater than the corresponding attribute of right. Less
51159     * than zero if the resolution.width of right is greater than left and
51160     * exactly zero if the two are equal.
51161     */
51162  
51163  
51164    var comparePlaylistResolution = function comparePlaylistResolution(left, right) {
51165      var leftWidth = void 0;
51166      var rightWidth = void 0;
51167  
51168      if (left.attributes.RESOLUTION && left.attributes.RESOLUTION.width) {
51169        leftWidth = left.attributes.RESOLUTION.width;
51170      }
51171  
51172      leftWidth = leftWidth || window$3.Number.MAX_VALUE;
51173  
51174      if (right.attributes.RESOLUTION && right.attributes.RESOLUTION.width) {
51175        rightWidth = right.attributes.RESOLUTION.width;
51176      }
51177  
51178      rightWidth = rightWidth || window$3.Number.MAX_VALUE; // NOTE - Fallback to bandwidth sort as appropriate in cases where multiple renditions
51179      // have the same media dimensions/ resolution
51180  
51181      if (leftWidth === rightWidth && left.attributes.BANDWIDTH && right.attributes.BANDWIDTH) {
51182        return left.attributes.BANDWIDTH - right.attributes.BANDWIDTH;
51183      }
51184  
51185      return leftWidth - rightWidth;
51186    };
51187    /**
51188     * Chooses the appropriate media playlist based on bandwidth and player size
51189     *
51190     * @param {Object} master
51191     *        Object representation of the master manifest
51192     * @param {Number} playerBandwidth
51193     *        Current calculated bandwidth of the player
51194     * @param {Number} playerWidth
51195     *        Current width of the player element (should account for the device pixel ratio)
51196     * @param {Number} playerHeight
51197     *        Current height of the player element (should account for the device pixel ratio)
51198     * @param {Boolean} limitRenditionByPlayerDimensions
51199     *        True if the player width and height should be used during the selection, false otherwise
51200     * @return {Playlist} the highest bitrate playlist less than the
51201     * currently detected bandwidth, accounting for some amount of
51202     * bandwidth variance
51203     */
51204  
51205  
51206    var simpleSelector = function simpleSelector(master, playerBandwidth, playerWidth, playerHeight, limitRenditionByPlayerDimensions) {
51207      // convert the playlists to an intermediary representation to make comparisons easier
51208      var sortedPlaylistReps = master.playlists.map(function (playlist) {
51209        var width = void 0;
51210        var height = void 0;
51211        var bandwidth = void 0;
51212        width = playlist.attributes.RESOLUTION && playlist.attributes.RESOLUTION.width;
51213        height = playlist.attributes.RESOLUTION && playlist.attributes.RESOLUTION.height;
51214        bandwidth = playlist.attributes.BANDWIDTH;
51215        bandwidth = bandwidth || window$3.Number.MAX_VALUE;
51216        return {
51217          bandwidth: bandwidth,
51218          width: width,
51219          height: height,
51220          playlist: playlist
51221        };
51222      });
51223      stableSort(sortedPlaylistReps, function (left, right) {
51224        return left.bandwidth - right.bandwidth;
51225      }); // filter out any playlists that have been excluded due to
51226      // incompatible configurations
51227  
51228      sortedPlaylistReps = sortedPlaylistReps.filter(function (rep) {
51229        return !Playlist.isIncompatible(rep.playlist);
51230      }); // filter out any playlists that have been disabled manually through the representations
51231      // api or blacklisted temporarily due to playback errors.
51232  
51233      var enabledPlaylistReps = sortedPlaylistReps.filter(function (rep) {
51234        return Playlist.isEnabled(rep.playlist);
51235      });
51236  
51237      if (!enabledPlaylistReps.length) {
51238        // if there are no enabled playlists, then they have all been blacklisted or disabled
51239        // by the user through the representations api. In this case, ignore blacklisting and
51240        // fallback to what the user wants by using playlists the user has not disabled.
51241        enabledPlaylistReps = sortedPlaylistReps.filter(function (rep) {
51242          return !Playlist.isDisabled(rep.playlist);
51243        });
51244      } // filter out any variant that has greater effective bitrate
51245      // than the current estimated bandwidth
51246  
51247  
51248      var bandwidthPlaylistReps = enabledPlaylistReps.filter(function (rep) {
51249        return rep.bandwidth * Config.BANDWIDTH_VARIANCE < playerBandwidth;
51250      });
51251      var highestRemainingBandwidthRep = bandwidthPlaylistReps[bandwidthPlaylistReps.length - 1]; // get all of the renditions with the same (highest) bandwidth
51252      // and then taking the very first element
51253  
51254      var bandwidthBestRep = bandwidthPlaylistReps.filter(function (rep) {
51255        return rep.bandwidth === highestRemainingBandwidthRep.bandwidth;
51256      })[0]; // if we're not going to limit renditions by player size, make an early decision.
51257  
51258      if (limitRenditionByPlayerDimensions === false) {
51259        var _chosenRep = bandwidthBestRep || enabledPlaylistReps[0] || sortedPlaylistReps[0];
51260  
51261        return _chosenRep ? _chosenRep.playlist : null;
51262      } // filter out playlists without resolution information
51263  
51264  
51265      var haveResolution = bandwidthPlaylistReps.filter(function (rep) {
51266        return rep.width && rep.height;
51267      }); // sort variants by resolution
51268  
51269      stableSort(haveResolution, function (left, right) {
51270        return left.width - right.width;
51271      }); // if we have the exact resolution as the player use it
51272  
51273      var resolutionBestRepList = haveResolution.filter(function (rep) {
51274        return rep.width === playerWidth && rep.height === playerHeight;
51275      });
51276      highestRemainingBandwidthRep = resolutionBestRepList[resolutionBestRepList.length - 1]; // ensure that we pick the highest bandwidth variant that have exact resolution
51277  
51278      var resolutionBestRep = resolutionBestRepList.filter(function (rep) {
51279        return rep.bandwidth === highestRemainingBandwidthRep.bandwidth;
51280      })[0];
51281      var resolutionPlusOneList = void 0;
51282      var resolutionPlusOneSmallest = void 0;
51283      var resolutionPlusOneRep = void 0; // find the smallest variant that is larger than the player
51284      // if there is no match of exact resolution
51285  
51286      if (!resolutionBestRep) {
51287        resolutionPlusOneList = haveResolution.filter(function (rep) {
51288          return rep.width > playerWidth || rep.height > playerHeight;
51289        }); // find all the variants have the same smallest resolution
51290  
51291        resolutionPlusOneSmallest = resolutionPlusOneList.filter(function (rep) {
51292          return rep.width === resolutionPlusOneList[0].width && rep.height === resolutionPlusOneList[0].height;
51293        }); // ensure that we also pick the highest bandwidth variant that
51294        // is just-larger-than the video player
51295  
51296        highestRemainingBandwidthRep = resolutionPlusOneSmallest[resolutionPlusOneSmallest.length - 1];
51297        resolutionPlusOneRep = resolutionPlusOneSmallest.filter(function (rep) {
51298          return rep.bandwidth === highestRemainingBandwidthRep.bandwidth;
51299        })[0];
51300      } // fallback chain of variants
51301  
51302  
51303      var chosenRep = resolutionPlusOneRep || resolutionBestRep || bandwidthBestRep || enabledPlaylistReps[0] || sortedPlaylistReps[0];
51304      return chosenRep ? chosenRep.playlist : null;
51305    }; // Playlist Selectors
51306  
51307    /**
51308     * Chooses the appropriate media playlist based on the most recent
51309     * bandwidth estimate and the player size.
51310     *
51311     * Expects to be called within the context of an instance of HlsHandler
51312     *
51313     * @return {Playlist} the highest bitrate playlist less than the
51314     * currently detected bandwidth, accounting for some amount of
51315     * bandwidth variance
51316     */
51317  
51318  
51319    var lastBandwidthSelector = function lastBandwidthSelector() {
51320      var pixelRatio = this.useDevicePixelRatio ? window$3.devicePixelRatio || 1 : 1;
51321      return simpleSelector(this.playlists.master, this.systemBandwidth, parseInt(safeGetComputedStyle(this.tech_.el(), 'width'), 10) * pixelRatio, parseInt(safeGetComputedStyle(this.tech_.el(), 'height'), 10) * pixelRatio, this.limitRenditionByPlayerDimensions);
51322    };
51323    /**
51324     * Chooses the appropriate media playlist based on the potential to rebuffer
51325     *
51326     * @param {Object} settings
51327     *        Object of information required to use this selector
51328     * @param {Object} settings.master
51329     *        Object representation of the master manifest
51330     * @param {Number} settings.currentTime
51331     *        The current time of the player
51332     * @param {Number} settings.bandwidth
51333     *        Current measured bandwidth
51334     * @param {Number} settings.duration
51335     *        Duration of the media
51336     * @param {Number} settings.segmentDuration
51337     *        Segment duration to be used in round trip time calculations
51338     * @param {Number} settings.timeUntilRebuffer
51339     *        Time left in seconds until the player has to rebuffer
51340     * @param {Number} settings.currentTimeline
51341     *        The current timeline segments are being loaded from
51342     * @param {SyncController} settings.syncController
51343     *        SyncController for determining if we have a sync point for a given playlist
51344     * @return {Object|null}
51345     *         {Object} return.playlist
51346     *         The highest bandwidth playlist with the least amount of rebuffering
51347     *         {Number} return.rebufferingImpact
51348     *         The amount of time in seconds switching to this playlist will rebuffer. A
51349     *         negative value means that switching will cause zero rebuffering.
51350     */
51351  
51352  
51353    var minRebufferMaxBandwidthSelector = function minRebufferMaxBandwidthSelector(settings) {
51354      var master = settings.master,
51355          currentTime = settings.currentTime,
51356          bandwidth = settings.bandwidth,
51357          duration$$1 = settings.duration,
51358          segmentDuration = settings.segmentDuration,
51359          timeUntilRebuffer = settings.timeUntilRebuffer,
51360          currentTimeline = settings.currentTimeline,
51361          syncController = settings.syncController; // filter out any playlists that have been excluded due to
51362      // incompatible configurations
51363  
51364      var compatiblePlaylists = master.playlists.filter(function (playlist) {
51365        return !Playlist.isIncompatible(playlist);
51366      }); // filter out any playlists that have been disabled manually through the representations
51367      // api or blacklisted temporarily due to playback errors.
51368  
51369      var enabledPlaylists = compatiblePlaylists.filter(Playlist.isEnabled);
51370  
51371      if (!enabledPlaylists.length) {
51372        // if there are no enabled playlists, then they have all been blacklisted or disabled
51373        // by the user through the representations api. In this case, ignore blacklisting and
51374        // fallback to what the user wants by using playlists the user has not disabled.
51375        enabledPlaylists = compatiblePlaylists.filter(function (playlist) {
51376          return !Playlist.isDisabled(playlist);
51377        });
51378      }
51379  
51380      var bandwidthPlaylists = enabledPlaylists.filter(Playlist.hasAttribute.bind(null, 'BANDWIDTH'));
51381      var rebufferingEstimates = bandwidthPlaylists.map(function (playlist) {
51382        var syncPoint = syncController.getSyncPoint(playlist, duration$$1, currentTimeline, currentTime); // If there is no sync point for this playlist, switching to it will require a
51383        // sync request first. This will double the request time
51384  
51385        var numRequests = syncPoint ? 1 : 2;
51386        var requestTimeEstimate = Playlist.estimateSegmentRequestTime(segmentDuration, bandwidth, playlist);
51387        var rebufferingImpact = requestTimeEstimate * numRequests - timeUntilRebuffer;
51388        return {
51389          playlist: playlist,
51390          rebufferingImpact: rebufferingImpact
51391        };
51392      });
51393      var noRebufferingPlaylists = rebufferingEstimates.filter(function (estimate) {
51394        return estimate.rebufferingImpact <= 0;
51395      }); // Sort by bandwidth DESC
51396  
51397      stableSort(noRebufferingPlaylists, function (a, b) {
51398        return comparePlaylistBandwidth(b.playlist, a.playlist);
51399      });
51400  
51401      if (noRebufferingPlaylists.length) {
51402        return noRebufferingPlaylists[0];
51403      }
51404  
51405      stableSort(rebufferingEstimates, function (a, b) {
51406        return a.rebufferingImpact - b.rebufferingImpact;
51407      });
51408      return rebufferingEstimates[0] || null;
51409    };
51410    /**
51411     * Chooses the appropriate media playlist, which in this case is the lowest bitrate
51412     * one with video.  If no renditions with video exist, return the lowest audio rendition.
51413     *
51414     * Expects to be called within the context of an instance of HlsHandler
51415     *
51416     * @return {Object|null}
51417     *         {Object} return.playlist
51418     *         The lowest bitrate playlist that contains a video codec.  If no such rendition
51419     *         exists pick the lowest audio rendition.
51420     */
51421  
51422  
51423    var lowestBitrateCompatibleVariantSelector = function lowestBitrateCompatibleVariantSelector() {
51424      // filter out any playlists that have been excluded due to
51425      // incompatible configurations or playback errors
51426      var playlists = this.playlists.master.playlists.filter(Playlist.isEnabled); // Sort ascending by bitrate
51427  
51428      stableSort(playlists, function (a, b) {
51429        return comparePlaylistBandwidth(a, b);
51430      }); // Parse and assume that playlists with no video codec have no video
51431      // (this is not necessarily true, although it is generally true).
51432      //
51433      // If an entire manifest has no valid videos everything will get filtered
51434      // out.
51435  
51436      var playlistsWithVideo = playlists.filter(function (playlist) {
51437        return parseCodecs(playlist.attributes.CODECS).videoCodec;
51438      });
51439      return playlistsWithVideo[0] || null;
51440    };
51441    /**
51442     * Create captions text tracks on video.js if they do not exist
51443     *
51444     * @param {Object} inbandTextTracks a reference to current inbandTextTracks
51445     * @param {Object} tech the video.js tech
51446     * @param {Object} captionStreams the caption streams to create
51447     * @private
51448     */
51449  
51450  
51451    var createCaptionsTrackIfNotExists = function createCaptionsTrackIfNotExists(inbandTextTracks, tech, captionStreams) {
51452      for (var trackId in captionStreams) {
51453        if (!inbandTextTracks[trackId]) {
51454          tech.trigger({
51455            type: 'usage',
51456            name: 'hls-608'
51457          });
51458          var track = tech.textTracks().getTrackById(trackId);
51459  
51460          if (track) {
51461            // Resuse an existing track with a CC# id because this was
51462            // very likely created by videojs-contrib-hls from information
51463            // in the m3u8 for us to use
51464            inbandTextTracks[trackId] = track;
51465          } else {
51466            // Otherwise, create a track with the default `CC#` label and
51467            // without a language
51468            inbandTextTracks[trackId] = tech.addRemoteTextTrack({
51469              kind: 'captions',
51470              id: trackId,
51471              label: trackId
51472            }, false).track;
51473          }
51474        }
51475      }
51476    };
51477  
51478    var addCaptionData = function addCaptionData(_ref) {
51479      var inbandTextTracks = _ref.inbandTextTracks,
51480          captionArray = _ref.captionArray,
51481          timestampOffset = _ref.timestampOffset;
51482  
51483      if (!captionArray) {
51484        return;
51485      }
51486  
51487      var Cue = window.WebKitDataCue || window.VTTCue;
51488      captionArray.forEach(function (caption) {
51489        var track = caption.stream;
51490        var startTime = caption.startTime;
51491        var endTime = caption.endTime;
51492  
51493        if (!inbandTextTracks[track]) {
51494          return;
51495        }
51496  
51497        startTime += timestampOffset;
51498        endTime += timestampOffset;
51499        inbandTextTracks[track].addCue(new Cue(startTime, endTime, caption.text));
51500      });
51501    };
51502    /**
51503     * @file segment-loader.js
51504     */
51505    // in ms
51506  
51507  
51508    var CHECK_BUFFER_DELAY = 500;
51509    /**
51510     * Determines if we should call endOfStream on the media source based
51511     * on the state of the buffer or if appened segment was the final
51512     * segment in the playlist.
51513     *
51514     * @param {Object} playlist a media playlist object
51515     * @param {Object} mediaSource the MediaSource object
51516     * @param {Number} segmentIndex the index of segment we last appended
51517     * @returns {Boolean} do we need to call endOfStream on the MediaSource
51518     */
51519  
51520    var detectEndOfStream = function detectEndOfStream(playlist, mediaSource, segmentIndex) {
51521      if (!playlist || !mediaSource) {
51522        return false;
51523      }
51524  
51525      var segments = playlist.segments; // determine a few boolean values to help make the branch below easier
51526      // to read
51527  
51528      var appendedLastSegment = segmentIndex === segments.length; // if we've buffered to the end of the video, we need to call endOfStream
51529      // so that MediaSources can trigger the `ended` event when it runs out of
51530      // buffered data instead of waiting for me
51531  
51532      return playlist.endList && mediaSource.readyState === 'open' && appendedLastSegment;
51533    };
51534  
51535    var finite = function finite(num) {
51536      return typeof num === 'number' && isFinite(num);
51537    };
51538  
51539    var illegalMediaSwitch = function illegalMediaSwitch(loaderType, startingMedia, newSegmentMedia) {
51540      // Although these checks should most likely cover non 'main' types, for now it narrows
51541      // the scope of our checks.
51542      if (loaderType !== 'main' || !startingMedia || !newSegmentMedia) {
51543        return null;
51544      }
51545  
51546      if (!newSegmentMedia.containsAudio && !newSegmentMedia.containsVideo) {
51547        return 'Neither audio nor video found in segment.';
51548      }
51549  
51550      if (startingMedia.containsVideo && !newSegmentMedia.containsVideo) {
51551        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.';
51552      }
51553  
51554      if (!startingMedia.containsVideo && newSegmentMedia.containsVideo) {
51555        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.';
51556      }
51557  
51558      return null;
51559    };
51560    /**
51561     * Calculates a time value that is safe to remove from the back buffer without interupting
51562     * playback.
51563     *
51564     * @param {TimeRange} seekable
51565     *        The current seekable range
51566     * @param {Number} currentTime
51567     *        The current time of the player
51568     * @param {Number} targetDuration
51569     *        The target duration of the current playlist
51570     * @return {Number}
51571     *         Time that is safe to remove from the back buffer without interupting playback
51572     */
51573  
51574  
51575    var safeBackBufferTrimTime = function safeBackBufferTrimTime(seekable$$1, currentTime, targetDuration) {
51576      // 30 seconds before the playhead provides a safe default for trimming.
51577      //
51578      // Choosing a reasonable default is particularly important for high bitrate content and
51579      // VOD videos/live streams with large windows, as the buffer may end up overfilled and
51580      // throw an APPEND_BUFFER_ERR.
51581      var trimTime = currentTime - 30;
51582  
51583      if (seekable$$1.length) {
51584        // Some live playlists may have a shorter window of content than the full allowed back
51585        // buffer. For these playlists, don't save content that's no longer within the window.
51586        trimTime = Math.max(trimTime, seekable$$1.start(0));
51587      } // Don't remove within target duration of the current time to avoid the possibility of
51588      // removing the GOP currently being played, as removing it can cause playback stalls.
51589  
51590  
51591      var maxTrimTime = currentTime - targetDuration;
51592      return Math.min(maxTrimTime, trimTime);
51593    };
51594  
51595    var segmentInfoString = function segmentInfoString(segmentInfo) {
51596      var _segmentInfo$segment = segmentInfo.segment,
51597          start = _segmentInfo$segment.start,
51598          end = _segmentInfo$segment.end,
51599          _segmentInfo$playlist = segmentInfo.playlist,
51600          seq = _segmentInfo$playlist.mediaSequence,
51601          id = _segmentInfo$playlist.id,
51602          _segmentInfo$playlist2 = _segmentInfo$playlist.segments,
51603          segments = _segmentInfo$playlist2 === undefined ? [] : _segmentInfo$playlist2,
51604          index = segmentInfo.mediaIndex,
51605          timeline = segmentInfo.timeline;
51606      return ['appending [' + index + '] of [' + seq + ', ' + (seq + segments.length) + '] from playlist [' + id + ']', '[' + start + ' => ' + end + '] in timeline [' + timeline + ']'].join(' ');
51607    };
51608    /**
51609     * An object that manages segment loading and appending.
51610     *
51611     * @class SegmentLoader
51612     * @param {Object} options required and optional options
51613     * @extends videojs.EventTarget
51614     */
51615  
51616  
51617    var SegmentLoader = function (_videojs$EventTarget) {
51618      inherits$2(SegmentLoader, _videojs$EventTarget);
51619  
51620      function SegmentLoader(settings) {
51621        classCallCheck$1(this, SegmentLoader); // check pre-conditions
51622  
51623        var _this = possibleConstructorReturn$1(this, (SegmentLoader.__proto__ || Object.getPrototypeOf(SegmentLoader)).call(this));
51624  
51625        if (!settings) {
51626          throw new TypeError('Initialization settings are required');
51627        }
51628  
51629        if (typeof settings.currentTime !== 'function') {
51630          throw new TypeError('No currentTime getter specified');
51631        }
51632  
51633        if (!settings.mediaSource) {
51634          throw new TypeError('No MediaSource specified');
51635        } // public properties
51636  
51637  
51638        _this.bandwidth = settings.bandwidth;
51639        _this.throughput = {
51640          rate: 0,
51641          count: 0
51642        };
51643        _this.roundTrip = NaN;
51644  
51645        _this.resetStats_();
51646  
51647        _this.mediaIndex = null; // private settings
51648  
51649        _this.hasPlayed_ = settings.hasPlayed;
51650        _this.currentTime_ = settings.currentTime;
51651        _this.seekable_ = settings.seekable;
51652        _this.seeking_ = settings.seeking;
51653        _this.duration_ = settings.duration;
51654        _this.mediaSource_ = settings.mediaSource;
51655        _this.hls_ = settings.hls;
51656        _this.loaderType_ = settings.loaderType;
51657        _this.startingMedia_ = void 0;
51658        _this.segmentMetadataTrack_ = settings.segmentMetadataTrack;
51659        _this.goalBufferLength_ = settings.goalBufferLength;
51660        _this.sourceType_ = settings.sourceType;
51661        _this.inbandTextTracks_ = settings.inbandTextTracks;
51662        _this.state_ = 'INIT'; // private instance variables
51663  
51664        _this.checkBufferTimeout_ = null;
51665        _this.error_ = void 0;
51666        _this.currentTimeline_ = -1;
51667        _this.pendingSegment_ = null;
51668        _this.mimeType_ = null;
51669        _this.sourceUpdater_ = null;
51670        _this.xhrOptions_ = null; // Fragmented mp4 playback
51671  
51672        _this.activeInitSegmentId_ = null;
51673        _this.initSegments_ = {}; // HLSe playback
51674  
51675        _this.cacheEncryptionKeys_ = settings.cacheEncryptionKeys;
51676        _this.keyCache_ = {}; // Fmp4 CaptionParser
51677  
51678        if (_this.loaderType_ === 'main') {
51679          _this.captionParser_ = new captionParser();
51680        } else {
51681          _this.captionParser_ = null;
51682        }
51683  
51684        _this.decrypter_ = settings.decrypter; // Manages the tracking and generation of sync-points, mappings
51685        // between a time in the display time and a segment index within
51686        // a playlist
51687  
51688        _this.syncController_ = settings.syncController;
51689        _this.syncPoint_ = {
51690          segmentIndex: 0,
51691          time: 0
51692        };
51693  
51694        _this.triggerSyncInfoUpdate_ = function () {
51695          return _this.trigger('syncinfoupdate');
51696        };
51697  
51698        _this.syncController_.on('syncinfoupdate', _this.triggerSyncInfoUpdate_);
51699  
51700        _this.mediaSource_.addEventListener('sourceopen', function () {
51701          return _this.ended_ = false;
51702        }); // ...for determining the fetch location
51703  
51704  
51705        _this.fetchAtBuffer_ = false;
51706        _this.logger_ = logger('SegmentLoader[' + _this.loaderType_ + ']');
51707        Object.defineProperty(_this, 'state', {
51708          get: function get$$1() {
51709            return this.state_;
51710          },
51711          set: function set$$1(newState) {
51712            if (newState !== this.state_) {
51713              this.logger_(this.state_ + ' -> ' + newState);
51714              this.state_ = newState;
51715            }
51716          }
51717        });
51718        return _this;
51719      }
51720      /**
51721       * reset all of our media stats
51722       *
51723       * @private
51724       */
51725  
51726  
51727      createClass$1(SegmentLoader, [{
51728        key: 'resetStats_',
51729        value: function resetStats_() {
51730          this.mediaBytesTransferred = 0;
51731          this.mediaRequests = 0;
51732          this.mediaRequestsAborted = 0;
51733          this.mediaRequestsTimedout = 0;
51734          this.mediaRequestsErrored = 0;
51735          this.mediaTransferDuration = 0;
51736          this.mediaSecondsLoaded = 0;
51737        }
51738        /**
51739         * dispose of the SegmentLoader and reset to the default state
51740         */
51741  
51742      }, {
51743        key: 'dispose',
51744        value: function dispose() {
51745          this.trigger('dispose');
51746          this.state = 'DISPOSED';
51747          this.pause();
51748          this.abort_();
51749  
51750          if (this.sourceUpdater_) {
51751            this.sourceUpdater_.dispose();
51752          }
51753  
51754          this.resetStats_();
51755  
51756          if (this.captionParser_) {
51757            this.captionParser_.reset();
51758          }
51759  
51760          if (this.checkBufferTimeout_) {
51761            window$3.clearTimeout(this.checkBufferTimeout_);
51762          }
51763  
51764          if (this.syncController_ && this.triggerSyncInfoUpdate_) {
51765            this.syncController_.off('syncinfoupdate', this.triggerSyncInfoUpdate_);
51766          }
51767  
51768          this.off();
51769        }
51770        /**
51771         * abort anything that is currently doing on with the SegmentLoader
51772         * and reset to a default state
51773         */
51774  
51775      }, {
51776        key: 'abort',
51777        value: function abort() {
51778          if (this.state !== 'WAITING') {
51779            if (this.pendingSegment_) {
51780              this.pendingSegment_ = null;
51781            }
51782  
51783            return;
51784          }
51785  
51786          this.abort_(); // We aborted the requests we were waiting on, so reset the loader's state to READY
51787          // since we are no longer "waiting" on any requests. XHR callback is not always run
51788          // when the request is aborted. This will prevent the loader from being stuck in the
51789          // WAITING state indefinitely.
51790  
51791          this.state = 'READY'; // don't wait for buffer check timeouts to begin fetching the
51792          // next segment
51793  
51794          if (!this.paused()) {
51795            this.monitorBuffer_();
51796          }
51797        }
51798        /**
51799         * abort all pending xhr requests and null any pending segements
51800         *
51801         * @private
51802         */
51803  
51804      }, {
51805        key: 'abort_',
51806        value: function abort_() {
51807          if (this.pendingSegment_) {
51808            this.pendingSegment_.abortRequests();
51809          } // clear out the segment being processed
51810  
51811  
51812          this.pendingSegment_ = null;
51813        }
51814        /**
51815         * set an error on the segment loader and null out any pending segements
51816         *
51817         * @param {Error} error the error to set on the SegmentLoader
51818         * @return {Error} the error that was set or that is currently set
51819         */
51820  
51821      }, {
51822        key: 'error',
51823        value: function error(_error) {
51824          if (typeof _error !== 'undefined') {
51825            this.error_ = _error;
51826          }
51827  
51828          this.pendingSegment_ = null;
51829          return this.error_;
51830        }
51831      }, {
51832        key: 'endOfStream',
51833        value: function endOfStream() {
51834          this.ended_ = true;
51835          this.pause();
51836          this.trigger('ended');
51837        }
51838        /**
51839         * Indicates which time ranges are buffered
51840         *
51841         * @return {TimeRange}
51842         *         TimeRange object representing the current buffered ranges
51843         */
51844  
51845      }, {
51846        key: 'buffered_',
51847        value: function buffered_() {
51848          if (!this.sourceUpdater_) {
51849            return videojs$1.createTimeRanges();
51850          }
51851  
51852          return this.sourceUpdater_.buffered();
51853        }
51854        /**
51855         * Gets and sets init segment for the provided map
51856         *
51857         * @param {Object} map
51858         *        The map object representing the init segment to get or set
51859         * @param {Boolean=} set
51860         *        If true, the init segment for the provided map should be saved
51861         * @return {Object}
51862         *         map object for desired init segment
51863         */
51864  
51865      }, {
51866        key: 'initSegment',
51867        value: function initSegment(map) {
51868          var set$$1 = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false;
51869  
51870          if (!map) {
51871            return null;
51872          }
51873  
51874          var id = initSegmentId(map);
51875          var storedMap = this.initSegments_[id];
51876  
51877          if (set$$1 && !storedMap && map.bytes) {
51878            this.initSegments_[id] = storedMap = {
51879              resolvedUri: map.resolvedUri,
51880              byterange: map.byterange,
51881              bytes: map.bytes,
51882              timescales: map.timescales,
51883              videoTrackIds: map.videoTrackIds
51884            };
51885          }
51886  
51887          return storedMap || map;
51888        }
51889        /**
51890         * Gets and sets key for the provided key
51891         *
51892         * @param {Object} key
51893         *        The key object representing the key to get or set
51894         * @param {Boolean=} set
51895         *        If true, the key for the provided key should be saved
51896         * @return {Object}
51897         *         Key object for desired key
51898         */
51899  
51900      }, {
51901        key: 'segmentKey',
51902        value: function segmentKey(key) {
51903          var set$$1 = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false;
51904  
51905          if (!key) {
51906            return null;
51907          }
51908  
51909          var id = segmentKeyId(key);
51910          var storedKey = this.keyCache_[id]; // TODO: We should use the HTTP Expires header to invalidate our cache per
51911          // https://tools.ietf.org/html/draft-pantos-http-live-streaming-23#section-6.2.3
51912  
51913          if (this.cacheEncryptionKeys_ && set$$1 && !storedKey && key.bytes) {
51914            this.keyCache_[id] = storedKey = {
51915              resolvedUri: key.resolvedUri,
51916              bytes: key.bytes
51917            };
51918          }
51919  
51920          var result = {
51921            resolvedUri: (storedKey || key).resolvedUri
51922          };
51923  
51924          if (storedKey) {
51925            result.bytes = storedKey.bytes;
51926          }
51927  
51928          return result;
51929        }
51930        /**
51931         * Returns true if all configuration required for loading is present, otherwise false.
51932         *
51933         * @return {Boolean} True if the all configuration is ready for loading
51934         * @private
51935         */
51936  
51937      }, {
51938        key: 'couldBeginLoading_',
51939        value: function couldBeginLoading_() {
51940          return this.playlist_ && ( // the source updater is created when init_ is called, so either having a
51941          // source updater or being in the INIT state with a mimeType is enough
51942          // to say we have all the needed configuration to start loading.
51943          this.sourceUpdater_ || this.mimeType_ && this.state === 'INIT') && !this.paused();
51944        }
51945        /**
51946         * load a playlist and start to fill the buffer
51947         */
51948  
51949      }, {
51950        key: 'load',
51951        value: function load() {
51952          // un-pause
51953          this.monitorBuffer_(); // if we don't have a playlist yet, keep waiting for one to be
51954          // specified
51955  
51956          if (!this.playlist_) {
51957            return;
51958          } // not sure if this is the best place for this
51959  
51960  
51961          this.syncController_.setDateTimeMapping(this.playlist_); // if all the configuration is ready, initialize and begin loading
51962  
51963          if (this.state === 'INIT' && this.couldBeginLoading_()) {
51964            return this.init_();
51965          } // if we're in the middle of processing a segment already, don't
51966          // kick off an additional segment request
51967  
51968  
51969          if (!this.couldBeginLoading_() || this.state !== 'READY' && this.state !== 'INIT') {
51970            return;
51971          }
51972  
51973          this.state = 'READY';
51974        }
51975        /**
51976         * Once all the starting parameters have been specified, begin
51977         * operation. This method should only be invoked from the INIT
51978         * state.
51979         *
51980         * @private
51981         */
51982  
51983      }, {
51984        key: 'init_',
51985        value: function init_() {
51986          this.state = 'READY';
51987          this.sourceUpdater_ = new SourceUpdater(this.mediaSource_, this.mimeType_, this.loaderType_, this.sourceBufferEmitter_);
51988          this.resetEverything();
51989          return this.monitorBuffer_();
51990        }
51991        /**
51992         * set a playlist on the segment loader
51993         *
51994         * @param {PlaylistLoader} media the playlist to set on the segment loader
51995         */
51996  
51997      }, {
51998        key: 'playlist',
51999        value: function playlist(newPlaylist) {
52000          var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
52001  
52002          if (!newPlaylist) {
52003            return;
52004          }
52005  
52006          var oldPlaylist = this.playlist_;
52007          var segmentInfo = this.pendingSegment_;
52008          this.playlist_ = newPlaylist;
52009          this.xhrOptions_ = options; // when we haven't started playing yet, the start of a live playlist
52010          // is always our zero-time so force a sync update each time the playlist
52011          // is refreshed from the server
52012          //
52013          // Use the INIT state to determine if playback has started, as the playlist sync info
52014          // should be fixed once requests begin (as sync points are generated based on sync
52015          // info), but not before then.
52016  
52017          if (this.state === 'INIT') {
52018            newPlaylist.syncInfo = {
52019              mediaSequence: newPlaylist.mediaSequence,
52020              time: 0
52021            };
52022          }
52023  
52024          var oldId = null;
52025  
52026          if (oldPlaylist) {
52027            if (oldPlaylist.id) {
52028              oldId = oldPlaylist.id;
52029            } else if (oldPlaylist.uri) {
52030              oldId = oldPlaylist.uri;
52031            }
52032          }
52033  
52034          this.logger_('playlist update [' + oldId + ' => ' + (newPlaylist.id || newPlaylist.uri) + ']'); // in VOD, this is always a rendition switch (or we updated our syncInfo above)
52035          // in LIVE, we always want to update with new playlists (including refreshes)
52036  
52037          this.trigger('syncinfoupdate'); // if we were unpaused but waiting for a playlist, start
52038          // buffering now
52039  
52040          if (this.state === 'INIT' && this.couldBeginLoading_()) {
52041            return this.init_();
52042          }
52043  
52044          if (!oldPlaylist || oldPlaylist.uri !== newPlaylist.uri) {
52045            if (this.mediaIndex !== null) {
52046              // we must "resync" the segment loader when we switch renditions and
52047              // the segment loader is already synced to the previous rendition
52048              this.resyncLoader();
52049            } // the rest of this function depends on `oldPlaylist` being defined
52050  
52051  
52052            return;
52053          } // we reloaded the same playlist so we are in a live scenario
52054          // and we will likely need to adjust the mediaIndex
52055  
52056  
52057          var mediaSequenceDiff = newPlaylist.mediaSequence - oldPlaylist.mediaSequence;
52058          this.logger_('live window shift [' + mediaSequenceDiff + ']'); // update the mediaIndex on the SegmentLoader
52059          // this is important because we can abort a request and this value must be
52060          // equal to the last appended mediaIndex
52061  
52062          if (this.mediaIndex !== null) {
52063            this.mediaIndex -= mediaSequenceDiff;
52064          } // update the mediaIndex on the SegmentInfo object
52065          // this is important because we will update this.mediaIndex with this value
52066          // in `handleUpdateEnd_` after the segment has been successfully appended
52067  
52068  
52069          if (segmentInfo) {
52070            segmentInfo.mediaIndex -= mediaSequenceDiff; // we need to update the referenced segment so that timing information is
52071            // saved for the new playlist's segment, however, if the segment fell off the
52072            // playlist, we can leave the old reference and just lose the timing info
52073  
52074            if (segmentInfo.mediaIndex >= 0) {
52075              segmentInfo.segment = newPlaylist.segments[segmentInfo.mediaIndex];
52076            }
52077          }
52078  
52079          this.syncController_.saveExpiredSegmentInfo(oldPlaylist, newPlaylist);
52080        }
52081        /**
52082         * Prevent the loader from fetching additional segments. If there
52083         * is a segment request outstanding, it will finish processing
52084         * before the loader halts. A segment loader can be unpaused by
52085         * calling load().
52086         */
52087  
52088      }, {
52089        key: 'pause',
52090        value: function pause() {
52091          if (this.checkBufferTimeout_) {
52092            window$3.clearTimeout(this.checkBufferTimeout_);
52093            this.checkBufferTimeout_ = null;
52094          }
52095        }
52096        /**
52097         * Returns whether the segment loader is fetching additional
52098         * segments when given the opportunity. This property can be
52099         * modified through calls to pause() and load().
52100         */
52101  
52102      }, {
52103        key: 'paused',
52104        value: function paused() {
52105          return this.checkBufferTimeout_ === null;
52106        }
52107        /**
52108         * create/set the following mimetype on the SourceBuffer through a
52109         * SourceUpdater
52110         *
52111         * @param {String} mimeType the mime type string to use
52112         * @param {Object} sourceBufferEmitter an event emitter that fires when a source buffer
52113         * is added to the media source
52114         */
52115  
52116      }, {
52117        key: 'mimeType',
52118        value: function mimeType(_mimeType, sourceBufferEmitter) {
52119          if (this.mimeType_) {
52120            return;
52121          }
52122  
52123          this.mimeType_ = _mimeType;
52124          this.sourceBufferEmitter_ = sourceBufferEmitter; // if we were unpaused but waiting for a sourceUpdater, start
52125          // buffering now
52126  
52127          if (this.state === 'INIT' && this.couldBeginLoading_()) {
52128            this.init_();
52129          }
52130        }
52131        /**
52132         * Delete all the buffered data and reset the SegmentLoader
52133         * @param {Function} [done] an optional callback to be executed when the remove
52134         * operation is complete
52135         */
52136  
52137      }, {
52138        key: 'resetEverything',
52139        value: function resetEverything(done) {
52140          this.ended_ = false;
52141          this.resetLoader(); // remove from 0, the earliest point, to Infinity, to signify removal of everything.
52142          // VTT Segment Loader doesn't need to do anything but in the regular SegmentLoader,
52143          // we then clamp the value to duration if necessary.
52144  
52145          this.remove(0, Infinity, done); // clears fmp4 captions
52146  
52147          if (this.captionParser_) {
52148            this.captionParser_.clearAllCaptions();
52149          }
52150  
52151          this.trigger('reseteverything');
52152        }
52153        /**
52154         * Force the SegmentLoader to resync and start loading around the currentTime instead
52155         * of starting at the end of the buffer
52156         *
52157         * Useful for fast quality changes
52158         */
52159  
52160      }, {
52161        key: 'resetLoader',
52162        value: function resetLoader() {
52163          this.fetchAtBuffer_ = false;
52164          this.resyncLoader();
52165        }
52166        /**
52167         * Force the SegmentLoader to restart synchronization and make a conservative guess
52168         * before returning to the simple walk-forward method
52169         */
52170  
52171      }, {
52172        key: 'resyncLoader',
52173        value: function resyncLoader() {
52174          this.mediaIndex = null;
52175          this.syncPoint_ = null;
52176          this.abort();
52177        }
52178        /**
52179         * Remove any data in the source buffer between start and end times
52180         * @param {Number} start - the start time of the region to remove from the buffer
52181         * @param {Number} end - the end time of the region to remove from the buffer
52182         * @param {Function} [done] - an optional callback to be executed when the remove
52183         * operation is complete
52184         */
52185  
52186      }, {
52187        key: 'remove',
52188        value: function remove(start, end, done) {
52189          // clamp end to duration if we need to remove everything.
52190          // This is due to a browser bug that causes issues if we remove to Infinity.
52191          // videojs/videojs-contrib-hls#1225
52192          if (end === Infinity) {
52193            end = this.duration_();
52194          }
52195  
52196          if (this.sourceUpdater_) {
52197            this.sourceUpdater_.remove(start, end, done);
52198          }
52199  
52200          removeCuesFromTrack(start, end, this.segmentMetadataTrack_);
52201  
52202          if (this.inbandTextTracks_) {
52203            for (var id in this.inbandTextTracks_) {
52204              removeCuesFromTrack(start, end, this.inbandTextTracks_[id]);
52205            }
52206          }
52207        }
52208        /**
52209         * (re-)schedule monitorBufferTick_ to run as soon as possible
52210         *
52211         * @private
52212         */
52213  
52214      }, {
52215        key: 'monitorBuffer_',
52216        value: function monitorBuffer_() {
52217          if (this.checkBufferTimeout_) {
52218            window$3.clearTimeout(this.checkBufferTimeout_);
52219          }
52220  
52221          this.checkBufferTimeout_ = window$3.setTimeout(this.monitorBufferTick_.bind(this), 1);
52222        }
52223        /**
52224         * As long as the SegmentLoader is in the READY state, periodically
52225         * invoke fillBuffer_().
52226         *
52227         * @private
52228         */
52229  
52230      }, {
52231        key: 'monitorBufferTick_',
52232        value: function monitorBufferTick_() {
52233          if (this.state === 'READY') {
52234            this.fillBuffer_();
52235          }
52236  
52237          if (this.checkBufferTimeout_) {
52238            window$3.clearTimeout(this.checkBufferTimeout_);
52239          }
52240  
52241          this.checkBufferTimeout_ = window$3.setTimeout(this.monitorBufferTick_.bind(this), CHECK_BUFFER_DELAY);
52242        }
52243        /**
52244         * fill the buffer with segements unless the sourceBuffers are
52245         * currently updating
52246         *
52247         * Note: this function should only ever be called by monitorBuffer_
52248         * and never directly
52249         *
52250         * @private
52251         */
52252  
52253      }, {
52254        key: 'fillBuffer_',
52255        value: function fillBuffer_() {
52256          if (this.sourceUpdater_.updating()) {
52257            return;
52258          }
52259  
52260          if (!this.syncPoint_) {
52261            this.syncPoint_ = this.syncController_.getSyncPoint(this.playlist_, this.duration_(), this.currentTimeline_, this.currentTime_());
52262          } // see if we need to begin loading immediately
52263  
52264  
52265          var segmentInfo = this.checkBuffer_(this.buffered_(), this.playlist_, this.mediaIndex, this.hasPlayed_(), this.currentTime_(), this.syncPoint_);
52266  
52267          if (!segmentInfo) {
52268            return;
52269          }
52270  
52271          if (this.isEndOfStream_(segmentInfo.mediaIndex)) {
52272            this.endOfStream();
52273            return;
52274          }
52275  
52276          if (segmentInfo.mediaIndex === this.playlist_.segments.length - 1 && this.mediaSource_.readyState === 'ended' && !this.seeking_()) {
52277            return;
52278          } // We will need to change timestampOffset of the sourceBuffer if:
52279          // - The segment.timeline !== this.currentTimeline
52280          //   (we are crossing a discontinuity somehow)
52281          // - The "timestampOffset" for the start of this segment is less than
52282          //   the currently set timestampOffset
52283          // Also, clear captions if we are crossing a discontinuity boundary
52284          // Previously, we changed the timestampOffset if the start of this segment
52285          // is less than the currently set timestampOffset but this isn't wanted
52286          // as it can produce bad behavior, especially around long running
52287          // live streams
52288  
52289  
52290          if (segmentInfo.timeline !== this.currentTimeline_) {
52291            this.syncController_.reset();
52292            segmentInfo.timestampOffset = segmentInfo.startOfSegment;
52293  
52294            if (this.captionParser_) {
52295              this.captionParser_.clearAllCaptions();
52296            }
52297          }
52298  
52299          this.loadSegment_(segmentInfo);
52300        }
52301        /**
52302         * Determines if this segment loader is at the end of it's stream.
52303         *
52304         * @param {Number} mediaIndex the index of segment we last appended
52305         * @param {Object} [playlist=this.playlist_] a media playlist object
52306         * @returns {Boolean} true if at end of stream, false otherwise.
52307         */
52308  
52309      }, {
52310        key: 'isEndOfStream_',
52311        value: function isEndOfStream_(mediaIndex) {
52312          var playlist = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : this.playlist_;
52313          return detectEndOfStream(playlist, this.mediaSource_, mediaIndex) && !this.sourceUpdater_.updating();
52314        }
52315        /**
52316         * Determines what segment request should be made, given current playback
52317         * state.
52318         *
52319         * @param {TimeRanges} buffered - the state of the buffer
52320         * @param {Object} playlist - the playlist object to fetch segments from
52321         * @param {Number} mediaIndex - the previous mediaIndex fetched or null
52322         * @param {Boolean} hasPlayed - a flag indicating whether we have played or not
52323         * @param {Number} currentTime - the playback position in seconds
52324         * @param {Object} syncPoint - a segment info object that describes the
52325         * @returns {Object} a segment request object that describes the segment to load
52326         */
52327  
52328      }, {
52329        key: 'checkBuffer_',
52330        value: function checkBuffer_(buffered, playlist, mediaIndex, hasPlayed, currentTime, syncPoint) {
52331          var lastBufferedEnd = 0;
52332          var startOfSegment = void 0;
52333  
52334          if (buffered.length) {
52335            lastBufferedEnd = buffered.end(buffered.length - 1);
52336          }
52337  
52338          var bufferedTime = Math.max(0, lastBufferedEnd - currentTime);
52339  
52340          if (!playlist.segments.length) {
52341            return null;
52342          } // if there is plenty of content buffered, and the video has
52343          // been played before relax for awhile
52344  
52345  
52346          if (bufferedTime >= this.goalBufferLength_()) {
52347            return null;
52348          } // if the video has not yet played once, and we already have
52349          // one segment downloaded do nothing
52350  
52351  
52352          if (!hasPlayed && bufferedTime >= 1) {
52353            return null;
52354          } // When the syncPoint is null, there is no way of determining a good
52355          // conservative segment index to fetch from
52356          // The best thing to do here is to get the kind of sync-point data by
52357          // making a request
52358  
52359  
52360          if (syncPoint === null) {
52361            mediaIndex = this.getSyncSegmentCandidate_(playlist);
52362            return this.generateSegmentInfo_(playlist, mediaIndex, null, true);
52363          } // Under normal playback conditions fetching is a simple walk forward
52364  
52365  
52366          if (mediaIndex !== null) {
52367            var segment = playlist.segments[mediaIndex];
52368            startOfSegment = lastBufferedEnd;
52369            return this.generateSegmentInfo_(playlist, mediaIndex + 1, startOfSegment, false);
52370          } // There is a sync-point but the lack of a mediaIndex indicates that
52371          // we need to make a good conservative guess about which segment to
52372          // fetch
52373  
52374  
52375          if (this.fetchAtBuffer_) {
52376            // Find the segment containing the end of the buffer
52377            var mediaSourceInfo = Playlist.getMediaInfoForTime(playlist, lastBufferedEnd, syncPoint.segmentIndex, syncPoint.time);
52378            mediaIndex = mediaSourceInfo.mediaIndex;
52379            startOfSegment = mediaSourceInfo.startTime;
52380          } else {
52381            // Find the segment containing currentTime
52382            var _mediaSourceInfo = Playlist.getMediaInfoForTime(playlist, currentTime, syncPoint.segmentIndex, syncPoint.time);
52383  
52384            mediaIndex = _mediaSourceInfo.mediaIndex;
52385            startOfSegment = _mediaSourceInfo.startTime;
52386          }
52387  
52388          return this.generateSegmentInfo_(playlist, mediaIndex, startOfSegment, false);
52389        }
52390        /**
52391         * The segment loader has no recourse except to fetch a segment in the
52392         * current playlist and use the internal timestamps in that segment to
52393         * generate a syncPoint. This function returns a good candidate index
52394         * for that process.
52395         *
52396         * @param {Object} playlist - the playlist object to look for a
52397         * @returns {Number} An index of a segment from the playlist to load
52398         */
52399  
52400      }, {
52401        key: 'getSyncSegmentCandidate_',
52402        value: function getSyncSegmentCandidate_(playlist) {
52403          var _this2 = this;
52404  
52405          if (this.currentTimeline_ === -1) {
52406            return 0;
52407          }
52408  
52409          var segmentIndexArray = playlist.segments.map(function (s, i) {
52410            return {
52411              timeline: s.timeline,
52412              segmentIndex: i
52413            };
52414          }).filter(function (s) {
52415            return s.timeline === _this2.currentTimeline_;
52416          });
52417  
52418          if (segmentIndexArray.length) {
52419            return segmentIndexArray[Math.min(segmentIndexArray.length - 1, 1)].segmentIndex;
52420          }
52421  
52422          return Math.max(playlist.segments.length - 1, 0);
52423        }
52424      }, {
52425        key: 'generateSegmentInfo_',
52426        value: function generateSegmentInfo_(playlist, mediaIndex, startOfSegment, isSyncRequest) {
52427          if (mediaIndex < 0 || mediaIndex >= playlist.segments.length) {
52428            return null;
52429          }
52430  
52431          var segment = playlist.segments[mediaIndex];
52432          return {
52433            requestId: 'segment-loader-' + Math.random(),
52434            // resolve the segment URL relative to the playlist
52435            uri: segment.resolvedUri,
52436            // the segment's mediaIndex at the time it was requested
52437            mediaIndex: mediaIndex,
52438            // whether or not to update the SegmentLoader's state with this
52439            // segment's mediaIndex
52440            isSyncRequest: isSyncRequest,
52441            startOfSegment: startOfSegment,
52442            // the segment's playlist
52443            playlist: playlist,
52444            // unencrypted bytes of the segment
52445            bytes: null,
52446            // when a key is defined for this segment, the encrypted bytes
52447            encryptedBytes: null,
52448            // The target timestampOffset for this segment when we append it
52449            // to the source buffer
52450            timestampOffset: null,
52451            // The timeline that the segment is in
52452            timeline: segment.timeline,
52453            // The expected duration of the segment in seconds
52454            duration: segment.duration,
52455            // retain the segment in case the playlist updates while doing an async process
52456            segment: segment
52457          };
52458        }
52459        /**
52460         * Determines if the network has enough bandwidth to complete the current segment
52461         * request in a timely manner. If not, the request will be aborted early and bandwidth
52462         * updated to trigger a playlist switch.
52463         *
52464         * @param {Object} stats
52465         *        Object containing stats about the request timing and size
52466         * @return {Boolean} True if the request was aborted, false otherwise
52467         * @private
52468         */
52469  
52470      }, {
52471        key: 'abortRequestEarly_',
52472        value: function abortRequestEarly_(stats) {
52473          if (this.hls_.tech_.paused() || // Don't abort if the current playlist is on the lowestEnabledRendition
52474          // TODO: Replace using timeout with a boolean indicating whether this playlist is
52475          //       the lowestEnabledRendition.
52476          !this.xhrOptions_.timeout || // Don't abort if we have no bandwidth information to estimate segment sizes
52477          !this.playlist_.attributes.BANDWIDTH) {
52478            return false;
52479          } // Wait at least 1 second since the first byte of data has been received before
52480          // using the calculated bandwidth from the progress event to allow the bitrate
52481          // to stabilize
52482  
52483  
52484          if (Date.now() - (stats.firstBytesReceivedAt || Date.now()) < 1000) {
52485            return false;
52486          }
52487  
52488          var currentTime = this.currentTime_();
52489          var measuredBandwidth = stats.bandwidth;
52490          var segmentDuration = this.pendingSegment_.duration;
52491          var requestTimeRemaining = Playlist.estimateSegmentRequestTime(segmentDuration, measuredBandwidth, this.playlist_, stats.bytesReceived); // Subtract 1 from the timeUntilRebuffer so we still consider an early abort
52492          // if we are only left with less than 1 second when the request completes.
52493          // A negative timeUntilRebuffering indicates we are already rebuffering
52494  
52495          var timeUntilRebuffer$$1 = timeUntilRebuffer(this.buffered_(), currentTime, this.hls_.tech_.playbackRate()) - 1; // Only consider aborting early if the estimated time to finish the download
52496          // is larger than the estimated time until the player runs out of forward buffer
52497  
52498          if (requestTimeRemaining <= timeUntilRebuffer$$1) {
52499            return false;
52500          }
52501  
52502          var switchCandidate = minRebufferMaxBandwidthSelector({
52503            master: this.hls_.playlists.master,
52504            currentTime: currentTime,
52505            bandwidth: measuredBandwidth,
52506            duration: this.duration_(),
52507            segmentDuration: segmentDuration,
52508            timeUntilRebuffer: timeUntilRebuffer$$1,
52509            currentTimeline: this.currentTimeline_,
52510            syncController: this.syncController_
52511          });
52512  
52513          if (!switchCandidate) {
52514            return;
52515          }
52516  
52517          var rebufferingImpact = requestTimeRemaining - timeUntilRebuffer$$1;
52518          var timeSavedBySwitching = rebufferingImpact - switchCandidate.rebufferingImpact;
52519          var minimumTimeSaving = 0.5; // If we are already rebuffering, increase the amount of variance we add to the
52520          // potential round trip time of the new request so that we are not too aggressive
52521          // with switching to a playlist that might save us a fraction of a second.
52522  
52523          if (timeUntilRebuffer$$1 <= TIME_FUDGE_FACTOR) {
52524            minimumTimeSaving = 1;
52525          }
52526  
52527          if (!switchCandidate.playlist || switchCandidate.playlist.uri === this.playlist_.uri || timeSavedBySwitching < minimumTimeSaving) {
52528            return false;
52529          } // set the bandwidth to that of the desired playlist being sure to scale by
52530          // BANDWIDTH_VARIANCE and add one so the playlist selector does not exclude it
52531          // don't trigger a bandwidthupdate as the bandwidth is artifial
52532  
52533  
52534          this.bandwidth = switchCandidate.playlist.attributes.BANDWIDTH * Config.BANDWIDTH_VARIANCE + 1;
52535          this.abort();
52536          this.trigger('earlyabort');
52537          return true;
52538        }
52539        /**
52540         * XHR `progress` event handler
52541         *
52542         * @param {Event}
52543         *        The XHR `progress` event
52544         * @param {Object} simpleSegment
52545         *        A simplified segment object copy
52546         * @private
52547         */
52548  
52549      }, {
52550        key: 'handleProgress_',
52551        value: function handleProgress_(event, simpleSegment) {
52552          if (!this.pendingSegment_ || simpleSegment.requestId !== this.pendingSegment_.requestId || this.abortRequestEarly_(simpleSegment.stats)) {
52553            return;
52554          }
52555  
52556          this.trigger('progress');
52557        }
52558        /**
52559         * load a specific segment from a request into the buffer
52560         *
52561         * @private
52562         */
52563  
52564      }, {
52565        key: 'loadSegment_',
52566        value: function loadSegment_(segmentInfo) {
52567          this.state = 'WAITING';
52568          this.pendingSegment_ = segmentInfo;
52569          this.trimBackBuffer_(segmentInfo);
52570          segmentInfo.abortRequests = mediaSegmentRequest(this.hls_.xhr, this.xhrOptions_, this.decrypter_, this.captionParser_, this.createSimplifiedSegmentObj_(segmentInfo), // progress callback
52571          this.handleProgress_.bind(this), this.segmentRequestFinished_.bind(this));
52572        }
52573        /**
52574         * trim the back buffer so that we don't have too much data
52575         * in the source buffer
52576         *
52577         * @private
52578         *
52579         * @param {Object} segmentInfo - the current segment
52580         */
52581  
52582      }, {
52583        key: 'trimBackBuffer_',
52584        value: function trimBackBuffer_(segmentInfo) {
52585          var removeToTime = safeBackBufferTrimTime(this.seekable_(), this.currentTime_(), this.playlist_.targetDuration || 10); // Chrome has a hard limit of 150MB of
52586          // buffer and a very conservative "garbage collector"
52587          // We manually clear out the old buffer to ensure
52588          // we don't trigger the QuotaExceeded error
52589          // on the source buffer during subsequent appends
52590  
52591          if (removeToTime > 0) {
52592            this.remove(0, removeToTime);
52593          }
52594        }
52595        /**
52596         * created a simplified copy of the segment object with just the
52597         * information necessary to perform the XHR and decryption
52598         *
52599         * @private
52600         *
52601         * @param {Object} segmentInfo - the current segment
52602         * @returns {Object} a simplified segment object copy
52603         */
52604  
52605      }, {
52606        key: 'createSimplifiedSegmentObj_',
52607        value: function createSimplifiedSegmentObj_(segmentInfo) {
52608          var segment = segmentInfo.segment;
52609          var simpleSegment = {
52610            resolvedUri: segment.resolvedUri,
52611            byterange: segment.byterange,
52612            requestId: segmentInfo.requestId
52613          };
52614  
52615          if (segment.key) {
52616            // if the media sequence is greater than 2^32, the IV will be incorrect
52617            // assuming 10s segments, that would be about 1300 years
52618            var iv = segment.key.iv || new Uint32Array([0, 0, 0, segmentInfo.mediaIndex + segmentInfo.playlist.mediaSequence]);
52619            simpleSegment.key = this.segmentKey(segment.key);
52620            simpleSegment.key.iv = iv;
52621          }
52622  
52623          if (segment.map) {
52624            simpleSegment.map = this.initSegment(segment.map);
52625          }
52626  
52627          return simpleSegment;
52628        }
52629        /**
52630         * Handle the callback from the segmentRequest function and set the
52631         * associated SegmentLoader state and errors if necessary
52632         *
52633         * @private
52634         */
52635  
52636      }, {
52637        key: 'segmentRequestFinished_',
52638        value: function segmentRequestFinished_(error, simpleSegment) {
52639          // every request counts as a media request even if it has been aborted
52640          // or canceled due to a timeout
52641          this.mediaRequests += 1;
52642  
52643          if (simpleSegment.stats) {
52644            this.mediaBytesTransferred += simpleSegment.stats.bytesReceived;
52645            this.mediaTransferDuration += simpleSegment.stats.roundTripTime;
52646          } // The request was aborted and the SegmentLoader has already been reset
52647  
52648  
52649          if (!this.pendingSegment_) {
52650            this.mediaRequestsAborted += 1;
52651            return;
52652          } // the request was aborted and the SegmentLoader has already started
52653          // another request. this can happen when the timeout for an aborted
52654          // request triggers due to a limitation in the XHR library
52655          // do not count this as any sort of request or we risk double-counting
52656  
52657  
52658          if (simpleSegment.requestId !== this.pendingSegment_.requestId) {
52659            return;
52660          } // an error occurred from the active pendingSegment_ so reset everything
52661  
52662  
52663          if (error) {
52664            this.pendingSegment_ = null;
52665            this.state = 'READY'; // the requests were aborted just record the aborted stat and exit
52666            // this is not a true error condition and nothing corrective needs
52667            // to be done
52668  
52669            if (error.code === REQUEST_ERRORS.ABORTED) {
52670              this.mediaRequestsAborted += 1;
52671              return;
52672            }
52673  
52674            this.pause(); // the error is really just that at least one of the requests timed-out
52675            // set the bandwidth to a very low value and trigger an ABR switch to
52676            // take emergency action
52677  
52678            if (error.code === REQUEST_ERRORS.TIMEOUT) {
52679              this.mediaRequestsTimedout += 1;
52680              this.bandwidth = 1;
52681              this.roundTrip = NaN;
52682              this.trigger('bandwidthupdate');
52683              return;
52684            } // if control-flow has arrived here, then the error is real
52685            // emit an error event to blacklist the current playlist
52686  
52687  
52688            this.mediaRequestsErrored += 1;
52689            this.error(error);
52690            this.trigger('error');
52691            return;
52692          } // the response was a success so set any bandwidth stats the request
52693          // generated for ABR purposes
52694  
52695  
52696          this.bandwidth = simpleSegment.stats.bandwidth;
52697          this.roundTrip = simpleSegment.stats.roundTripTime; // if this request included an initialization segment, save that data
52698          // to the initSegment cache
52699  
52700          if (simpleSegment.map) {
52701            simpleSegment.map = this.initSegment(simpleSegment.map, true);
52702          } // if this request included a segment key, save that data in the cache
52703  
52704  
52705          if (simpleSegment.key) {
52706            this.segmentKey(simpleSegment.key, true);
52707          }
52708  
52709          this.processSegmentResponse_(simpleSegment);
52710        }
52711        /**
52712         * Move any important data from the simplified segment object
52713         * back to the real segment object for future phases
52714         *
52715         * @private
52716         */
52717  
52718      }, {
52719        key: 'processSegmentResponse_',
52720        value: function processSegmentResponse_(simpleSegment) {
52721          var segmentInfo = this.pendingSegment_;
52722          segmentInfo.bytes = simpleSegment.bytes;
52723  
52724          if (simpleSegment.map) {
52725            segmentInfo.segment.map.bytes = simpleSegment.map.bytes;
52726          }
52727  
52728          segmentInfo.endOfAllRequests = simpleSegment.endOfAllRequests; // This has fmp4 captions, add them to text tracks
52729  
52730          if (simpleSegment.fmp4Captions) {
52731            createCaptionsTrackIfNotExists(this.inbandTextTracks_, this.hls_.tech_, simpleSegment.captionStreams);
52732            addCaptionData({
52733              inbandTextTracks: this.inbandTextTracks_,
52734              captionArray: simpleSegment.fmp4Captions,
52735              // fmp4s will not have a timestamp offset
52736              timestampOffset: 0
52737            }); // Reset stored captions since we added parsed
52738            // captions to a text track at this point
52739  
52740            if (this.captionParser_) {
52741              this.captionParser_.clearParsedCaptions();
52742            }
52743          }
52744  
52745          this.handleSegment_();
52746        }
52747        /**
52748         * append a decrypted segement to the SourceBuffer through a SourceUpdater
52749         *
52750         * @private
52751         */
52752  
52753      }, {
52754        key: 'handleSegment_',
52755        value: function handleSegment_() {
52756          var _this3 = this;
52757  
52758          if (!this.pendingSegment_) {
52759            this.state = 'READY';
52760            return;
52761          }
52762  
52763          var segmentInfo = this.pendingSegment_;
52764          var segment = segmentInfo.segment;
52765          var timingInfo = this.syncController_.probeSegmentInfo(segmentInfo); // When we have our first timing info, determine what media types this loader is
52766          // dealing with. Although we're maintaining extra state, it helps to preserve the
52767          // separation of segment loader from the actual source buffers.
52768  
52769          if (typeof this.startingMedia_ === 'undefined' && timingInfo && ( // Guard against cases where we're not getting timing info at all until we are
52770          // certain that all streams will provide it.
52771          timingInfo.containsAudio || timingInfo.containsVideo)) {
52772            this.startingMedia_ = {
52773              containsAudio: timingInfo.containsAudio,
52774              containsVideo: timingInfo.containsVideo
52775            };
52776          }
52777  
52778          var illegalMediaSwitchError = illegalMediaSwitch(this.loaderType_, this.startingMedia_, timingInfo);
52779  
52780          if (illegalMediaSwitchError) {
52781            this.error({
52782              message: illegalMediaSwitchError,
52783              blacklistDuration: Infinity
52784            });
52785            this.trigger('error');
52786            return;
52787          }
52788  
52789          if (segmentInfo.isSyncRequest) {
52790            this.trigger('syncinfoupdate');
52791            this.pendingSegment_ = null;
52792            this.state = 'READY';
52793            return;
52794          }
52795  
52796          if (segmentInfo.timestampOffset !== null && segmentInfo.timestampOffset !== this.sourceUpdater_.timestampOffset()) {
52797            // Subtract any difference between the PTS and DTS times of the first frame
52798            // from the timeStampOffset (which currently equals the buffered.end) to prevent
52799            // creating any gaps in the buffer
52800            if (timingInfo && timingInfo.segmentTimestampInfo) {
52801              var ptsStartTime = timingInfo.segmentTimestampInfo[0].ptsTime;
52802              var dtsStartTime = timingInfo.segmentTimestampInfo[0].dtsTime;
52803              segmentInfo.timestampOffset -= ptsStartTime - dtsStartTime;
52804            }
52805  
52806            this.sourceUpdater_.timestampOffset(segmentInfo.timestampOffset); // fired when a timestamp offset is set in HLS (can also identify discontinuities)
52807  
52808            this.trigger('timestampoffset');
52809          }
52810  
52811          var timelineMapping = this.syncController_.mappingForTimeline(segmentInfo.timeline);
52812  
52813          if (timelineMapping !== null) {
52814            this.trigger({
52815              type: 'segmenttimemapping',
52816              mapping: timelineMapping
52817            });
52818          }
52819  
52820          this.state = 'APPENDING'; // if the media initialization segment is changing, append it
52821          // before the content segment
52822  
52823          if (segment.map) {
52824            var initId = initSegmentId(segment.map);
52825  
52826            if (!this.activeInitSegmentId_ || this.activeInitSegmentId_ !== initId) {
52827              var initSegment = this.initSegment(segment.map);
52828              this.sourceUpdater_.appendBuffer({
52829                bytes: initSegment.bytes
52830              }, function () {
52831                _this3.activeInitSegmentId_ = initId;
52832              });
52833            }
52834          }
52835  
52836          segmentInfo.byteLength = segmentInfo.bytes.byteLength;
52837  
52838          if (typeof segment.start === 'number' && typeof segment.end === 'number') {
52839            this.mediaSecondsLoaded += segment.end - segment.start;
52840          } else {
52841            this.mediaSecondsLoaded += segment.duration;
52842          }
52843  
52844          this.logger_(segmentInfoString(segmentInfo));
52845          this.sourceUpdater_.appendBuffer({
52846            bytes: segmentInfo.bytes,
52847            videoSegmentTimingInfoCallback: this.handleVideoSegmentTimingInfo_.bind(this, segmentInfo.requestId)
52848          }, this.handleUpdateEnd_.bind(this));
52849        }
52850      }, {
52851        key: 'handleVideoSegmentTimingInfo_',
52852        value: function handleVideoSegmentTimingInfo_(requestId, event) {
52853          if (!this.pendingSegment_ || requestId !== this.pendingSegment_.requestId) {
52854            return;
52855          }
52856  
52857          var segment = this.pendingSegment_.segment;
52858  
52859          if (!segment.videoTimingInfo) {
52860            segment.videoTimingInfo = {};
52861          }
52862  
52863          segment.videoTimingInfo.transmuxerPrependedSeconds = event.videoSegmentTimingInfo.prependedContentDuration || 0;
52864          segment.videoTimingInfo.transmuxedPresentationStart = event.videoSegmentTimingInfo.start.presentation;
52865          segment.videoTimingInfo.transmuxedPresentationEnd = event.videoSegmentTimingInfo.end.presentation; // mainly used as a reference for debugging
52866  
52867          segment.videoTimingInfo.baseMediaDecodeTime = event.videoSegmentTimingInfo.baseMediaDecodeTime;
52868        }
52869        /**
52870         * callback to run when appendBuffer is finished. detects if we are
52871         * in a good state to do things with the data we got, or if we need
52872         * to wait for more
52873         *
52874         * @private
52875         */
52876  
52877      }, {
52878        key: 'handleUpdateEnd_',
52879        value: function handleUpdateEnd_() {
52880          if (!this.pendingSegment_) {
52881            this.state = 'READY';
52882  
52883            if (!this.paused()) {
52884              this.monitorBuffer_();
52885            }
52886  
52887            return;
52888          }
52889  
52890          var segmentInfo = this.pendingSegment_;
52891          var segment = segmentInfo.segment;
52892          var isWalkingForward = this.mediaIndex !== null;
52893          this.pendingSegment_ = null;
52894          this.recordThroughput_(segmentInfo);
52895          this.addSegmentMetadataCue_(segmentInfo);
52896          this.state = 'READY';
52897          this.mediaIndex = segmentInfo.mediaIndex;
52898          this.fetchAtBuffer_ = true;
52899          this.currentTimeline_ = segmentInfo.timeline; // We must update the syncinfo to recalculate the seekable range before
52900          // the following conditional otherwise it may consider this a bad "guess"
52901          // and attempt to resync when the post-update seekable window and live
52902          // point would mean that this was the perfect segment to fetch
52903  
52904          this.trigger('syncinfoupdate'); // If we previously appended a segment that ends more than 3 targetDurations before
52905          // the currentTime_ that means that our conservative guess was too conservative.
52906          // In that case, reset the loader state so that we try to use any information gained
52907          // from the previous request to create a new, more accurate, sync-point.
52908  
52909          if (segment.end && this.currentTime_() - segment.end > segmentInfo.playlist.targetDuration * 3) {
52910            this.resetEverything();
52911            return;
52912          } // Don't do a rendition switch unless we have enough time to get a sync segment
52913          // and conservatively guess
52914  
52915  
52916          if (isWalkingForward) {
52917            this.trigger('bandwidthupdate');
52918          }
52919  
52920          this.trigger('progress'); // any time an update finishes and the last segment is in the
52921          // buffer, end the stream. this ensures the "ended" event will
52922          // fire if playback reaches that point.
52923  
52924          if (this.isEndOfStream_(segmentInfo.mediaIndex + 1, segmentInfo.playlist)) {
52925            this.endOfStream();
52926          }
52927  
52928          if (!this.paused()) {
52929            this.monitorBuffer_();
52930          }
52931        }
52932        /**
52933         * Records the current throughput of the decrypt, transmux, and append
52934         * portion of the semgment pipeline. `throughput.rate` is a the cumulative
52935         * moving average of the throughput. `throughput.count` is the number of
52936         * data points in the average.
52937         *
52938         * @private
52939         * @param {Object} segmentInfo the object returned by loadSegment
52940         */
52941  
52942      }, {
52943        key: 'recordThroughput_',
52944        value: function recordThroughput_(segmentInfo) {
52945          var rate = this.throughput.rate; // Add one to the time to ensure that we don't accidentally attempt to divide
52946          // by zero in the case where the throughput is ridiculously high
52947  
52948          var segmentProcessingTime = Date.now() - segmentInfo.endOfAllRequests + 1; // Multiply by 8000 to convert from bytes/millisecond to bits/second
52949  
52950          var segmentProcessingThroughput = Math.floor(segmentInfo.byteLength / segmentProcessingTime * 8 * 1000); // This is just a cumulative moving average calculation:
52951          //   newAvg = oldAvg + (sample - oldAvg) / (sampleCount + 1)
52952  
52953          this.throughput.rate += (segmentProcessingThroughput - rate) / ++this.throughput.count;
52954        }
52955        /**
52956         * Adds a cue to the segment-metadata track with some metadata information about the
52957         * segment
52958         *
52959         * @private
52960         * @param {Object} segmentInfo
52961         *        the object returned by loadSegment
52962         * @method addSegmentMetadataCue_
52963         */
52964  
52965      }, {
52966        key: 'addSegmentMetadataCue_',
52967        value: function addSegmentMetadataCue_(segmentInfo) {
52968          if (!this.segmentMetadataTrack_) {
52969            return;
52970          }
52971  
52972          var segment = segmentInfo.segment;
52973          var start = segment.start;
52974          var end = segment.end; // Do not try adding the cue if the start and end times are invalid.
52975  
52976          if (!finite(start) || !finite(end)) {
52977            return;
52978          }
52979  
52980          removeCuesFromTrack(start, end, this.segmentMetadataTrack_);
52981          var Cue = window$3.WebKitDataCue || window$3.VTTCue;
52982          var value = {
52983            custom: segment.custom,
52984            dateTimeObject: segment.dateTimeObject,
52985            dateTimeString: segment.dateTimeString,
52986            bandwidth: segmentInfo.playlist.attributes.BANDWIDTH,
52987            resolution: segmentInfo.playlist.attributes.RESOLUTION,
52988            codecs: segmentInfo.playlist.attributes.CODECS,
52989            byteLength: segmentInfo.byteLength,
52990            uri: segmentInfo.uri,
52991            timeline: segmentInfo.timeline,
52992            playlist: segmentInfo.playlist.id,
52993            start: start,
52994            end: end
52995          };
52996          var data = JSON.stringify(value);
52997          var cue = new Cue(start, end, data); // Attach the metadata to the value property of the cue to keep consistency between
52998          // the differences of WebKitDataCue in safari and VTTCue in other browsers
52999  
53000          cue.value = value;
53001          this.segmentMetadataTrack_.addCue(cue);
53002        }
53003      }]);
53004      return SegmentLoader;
53005    }(videojs$1.EventTarget);
53006  
53007    var uint8ToUtf8 = function uint8ToUtf8(uintArray) {
53008      return decodeURIComponent(escape(String.fromCharCode.apply(null, uintArray)));
53009    };
53010    /**
53011     * @file vtt-segment-loader.js
53012     */
53013  
53014  
53015    var VTT_LINE_TERMINATORS = new Uint8Array('\n\n'.split('').map(function (_char2) {
53016      return _char2.charCodeAt(0);
53017    }));
53018    /**
53019     * An object that manages segment loading and appending.
53020     *
53021     * @class VTTSegmentLoader
53022     * @param {Object} options required and optional options
53023     * @extends videojs.EventTarget
53024     */
53025  
53026    var VTTSegmentLoader = function (_SegmentLoader) {
53027      inherits$2(VTTSegmentLoader, _SegmentLoader);
53028  
53029      function VTTSegmentLoader(settings) {
53030        var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
53031        classCallCheck$1(this, VTTSegmentLoader); // SegmentLoader requires a MediaSource be specified or it will throw an error;
53032        // however, VTTSegmentLoader has no need of a media source, so delete the reference
53033  
53034        var _this = possibleConstructorReturn$1(this, (VTTSegmentLoader.__proto__ || Object.getPrototypeOf(VTTSegmentLoader)).call(this, settings, options));
53035  
53036        _this.mediaSource_ = null;
53037        _this.subtitlesTrack_ = null;
53038        _this.featuresNativeTextTracks_ = settings.featuresNativeTextTracks;
53039        return _this;
53040      }
53041      /**
53042       * Indicates which time ranges are buffered
53043       *
53044       * @return {TimeRange}
53045       *         TimeRange object representing the current buffered ranges
53046       */
53047  
53048  
53049      createClass$1(VTTSegmentLoader, [{
53050        key: 'buffered_',
53051        value: function buffered_() {
53052          if (!this.subtitlesTrack_ || !this.subtitlesTrack_.cues.length) {
53053            return videojs$1.createTimeRanges();
53054          }
53055  
53056          var cues = this.subtitlesTrack_.cues;
53057          var start = cues[0].startTime;
53058          var end = cues[cues.length - 1].startTime;
53059          return videojs$1.createTimeRanges([[start, end]]);
53060        }
53061        /**
53062         * Gets and sets init segment for the provided map
53063         *
53064         * @param {Object} map
53065         *        The map object representing the init segment to get or set
53066         * @param {Boolean=} set
53067         *        If true, the init segment for the provided map should be saved
53068         * @return {Object}
53069         *         map object for desired init segment
53070         */
53071  
53072      }, {
53073        key: 'initSegment',
53074        value: function initSegment(map) {
53075          var set$$1 = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false;
53076  
53077          if (!map) {
53078            return null;
53079          }
53080  
53081          var id = initSegmentId(map);
53082          var storedMap = this.initSegments_[id];
53083  
53084          if (set$$1 && !storedMap && map.bytes) {
53085            // append WebVTT line terminators to the media initialization segment if it exists
53086            // to follow the WebVTT spec (https://w3c.github.io/webvtt/#file-structure) that
53087            // requires two or more WebVTT line terminators between the WebVTT header and the
53088            // rest of the file
53089            var combinedByteLength = VTT_LINE_TERMINATORS.byteLength + map.bytes.byteLength;
53090            var combinedSegment = new Uint8Array(combinedByteLength);
53091            combinedSegment.set(map.bytes);
53092            combinedSegment.set(VTT_LINE_TERMINATORS, map.bytes.byteLength);
53093            this.initSegments_[id] = storedMap = {
53094              resolvedUri: map.resolvedUri,
53095              byterange: map.byterange,
53096              bytes: combinedSegment
53097            };
53098          }
53099  
53100          return storedMap || map;
53101        }
53102        /**
53103         * Returns true if all configuration required for loading is present, otherwise false.
53104         *
53105         * @return {Boolean} True if the all configuration is ready for loading
53106         * @private
53107         */
53108  
53109      }, {
53110        key: 'couldBeginLoading_',
53111        value: function couldBeginLoading_() {
53112          return this.playlist_ && this.subtitlesTrack_ && !this.paused();
53113        }
53114        /**
53115         * Once all the starting parameters have been specified, begin
53116         * operation. This method should only be invoked from the INIT
53117         * state.
53118         *
53119         * @private
53120         */
53121  
53122      }, {
53123        key: 'init_',
53124        value: function init_() {
53125          this.state = 'READY';
53126          this.resetEverything();
53127          return this.monitorBuffer_();
53128        }
53129        /**
53130         * Set a subtitle track on the segment loader to add subtitles to
53131         *
53132         * @param {TextTrack=} track
53133         *        The text track to add loaded subtitles to
53134         * @return {TextTrack}
53135         *        Returns the subtitles track
53136         */
53137  
53138      }, {
53139        key: 'track',
53140        value: function track(_track) {
53141          if (typeof _track === 'undefined') {
53142            return this.subtitlesTrack_;
53143          }
53144  
53145          this.subtitlesTrack_ = _track; // if we were unpaused but waiting for a sourceUpdater, start
53146          // buffering now
53147  
53148          if (this.state === 'INIT' && this.couldBeginLoading_()) {
53149            this.init_();
53150          }
53151  
53152          return this.subtitlesTrack_;
53153        }
53154        /**
53155         * Remove any data in the source buffer between start and end times
53156         * @param {Number} start - the start time of the region to remove from the buffer
53157         * @param {Number} end - the end time of the region to remove from the buffer
53158         */
53159  
53160      }, {
53161        key: 'remove',
53162        value: function remove(start, end) {
53163          removeCuesFromTrack(start, end, this.subtitlesTrack_);
53164        }
53165        /**
53166         * fill the buffer with segements unless the sourceBuffers are
53167         * currently updating
53168         *
53169         * Note: this function should only ever be called by monitorBuffer_
53170         * and never directly
53171         *
53172         * @private
53173         */
53174  
53175      }, {
53176        key: 'fillBuffer_',
53177        value: function fillBuffer_() {
53178          var _this2 = this;
53179  
53180          if (!this.syncPoint_) {
53181            this.syncPoint_ = this.syncController_.getSyncPoint(this.playlist_, this.duration_(), this.currentTimeline_, this.currentTime_());
53182          } // see if we need to begin loading immediately
53183  
53184  
53185          var segmentInfo = this.checkBuffer_(this.buffered_(), this.playlist_, this.mediaIndex, this.hasPlayed_(), this.currentTime_(), this.syncPoint_);
53186          segmentInfo = this.skipEmptySegments_(segmentInfo);
53187  
53188          if (!segmentInfo) {
53189            return;
53190          }
53191  
53192          if (this.syncController_.timestampOffsetForTimeline(segmentInfo.timeline) === null) {
53193            // We don't have the timestamp offset that we need to sync subtitles.
53194            // Rerun on a timestamp offset or user interaction.
53195            var checkTimestampOffset = function checkTimestampOffset() {
53196              _this2.state = 'READY';
53197  
53198              if (!_this2.paused()) {
53199                // if not paused, queue a buffer check as soon as possible
53200                _this2.monitorBuffer_();
53201              }
53202            };
53203  
53204            this.syncController_.one('timestampoffset', checkTimestampOffset);
53205            this.state = 'WAITING_ON_TIMELINE';
53206            return;
53207          }
53208  
53209          this.loadSegment_(segmentInfo);
53210        }
53211        /**
53212         * Prevents the segment loader from requesting segments we know contain no subtitles
53213         * by walking forward until we find the next segment that we don't know whether it is
53214         * empty or not.
53215         *
53216         * @param {Object} segmentInfo
53217         *        a segment info object that describes the current segment
53218         * @return {Object}
53219         *         a segment info object that describes the current segment
53220         */
53221  
53222      }, {
53223        key: 'skipEmptySegments_',
53224        value: function skipEmptySegments_(segmentInfo) {
53225          while (segmentInfo && segmentInfo.segment.empty) {
53226            segmentInfo = this.generateSegmentInfo_(segmentInfo.playlist, segmentInfo.mediaIndex + 1, segmentInfo.startOfSegment + segmentInfo.duration, segmentInfo.isSyncRequest);
53227          }
53228  
53229          return segmentInfo;
53230        }
53231        /**
53232         * append a decrypted segement to the SourceBuffer through a SourceUpdater
53233         *
53234         * @private
53235         */
53236  
53237      }, {
53238        key: 'handleSegment_',
53239        value: function handleSegment_() {
53240          var _this3 = this;
53241  
53242          if (!this.pendingSegment_ || !this.subtitlesTrack_) {
53243            this.state = 'READY';
53244            return;
53245          }
53246  
53247          this.state = 'APPENDING';
53248          var segmentInfo = this.pendingSegment_;
53249          var segment = segmentInfo.segment; // Make sure that vttjs has loaded, otherwise, wait till it finished loading
53250  
53251          if (typeof window$3.WebVTT !== 'function' && this.subtitlesTrack_ && this.subtitlesTrack_.tech_) {
53252            var loadHandler = void 0;
53253  
53254            var errorHandler = function errorHandler() {
53255              _this3.subtitlesTrack_.tech_.off('vttjsloaded', loadHandler);
53256  
53257              _this3.error({
53258                message: 'Error loading vtt.js'
53259              });
53260  
53261              _this3.state = 'READY';
53262  
53263              _this3.pause();
53264  
53265              _this3.trigger('error');
53266            };
53267  
53268            loadHandler = function loadHandler() {
53269              _this3.subtitlesTrack_.tech_.off('vttjserror', errorHandler);
53270  
53271              _this3.handleSegment_();
53272            };
53273  
53274            this.state = 'WAITING_ON_VTTJS';
53275            this.subtitlesTrack_.tech_.one('vttjsloaded', loadHandler);
53276            this.subtitlesTrack_.tech_.one('vttjserror', errorHandler);
53277            return;
53278          }
53279  
53280          segment.requested = true;
53281  
53282          try {
53283            this.parseVTTCues_(segmentInfo);
53284          } catch (e) {
53285            this.error({
53286              message: e.message
53287            });
53288            this.state = 'READY';
53289            this.pause();
53290            return this.trigger('error');
53291          }
53292  
53293          this.updateTimeMapping_(segmentInfo, this.syncController_.timelines[segmentInfo.timeline], this.playlist_);
53294  
53295          if (segmentInfo.isSyncRequest) {
53296            this.trigger('syncinfoupdate');
53297            this.pendingSegment_ = null;
53298            this.state = 'READY';
53299            return;
53300          }
53301  
53302          segmentInfo.byteLength = segmentInfo.bytes.byteLength;
53303          this.mediaSecondsLoaded += segment.duration;
53304  
53305          if (segmentInfo.cues.length) {
53306            // remove any overlapping cues to prevent doubling
53307            this.remove(segmentInfo.cues[0].endTime, segmentInfo.cues[segmentInfo.cues.length - 1].endTime);
53308          }
53309  
53310          segmentInfo.cues.forEach(function (cue) {
53311            _this3.subtitlesTrack_.addCue(_this3.featuresNativeTextTracks_ ? new window$3.VTTCue(cue.startTime, cue.endTime, cue.text) : cue);
53312          });
53313          this.handleUpdateEnd_();
53314        }
53315        /**
53316         * Uses the WebVTT parser to parse the segment response
53317         *
53318         * @param {Object} segmentInfo
53319         *        a segment info object that describes the current segment
53320         * @private
53321         */
53322  
53323      }, {
53324        key: 'parseVTTCues_',
53325        value: function parseVTTCues_(segmentInfo) {
53326          var decoder = void 0;
53327          var decodeBytesToString = false;
53328  
53329          if (typeof window$3.TextDecoder === 'function') {
53330            decoder = new window$3.TextDecoder('utf8');
53331          } else {
53332            decoder = window$3.WebVTT.StringDecoder();
53333            decodeBytesToString = true;
53334          }
53335  
53336          var parser = new window$3.WebVTT.Parser(window$3, window$3.vttjs, decoder);
53337          segmentInfo.cues = [];
53338          segmentInfo.timestampmap = {
53339            MPEGTS: 0,
53340            LOCAL: 0
53341          };
53342          parser.oncue = segmentInfo.cues.push.bind(segmentInfo.cues);
53343  
53344          parser.ontimestampmap = function (map) {
53345            return segmentInfo.timestampmap = map;
53346          };
53347  
53348          parser.onparsingerror = function (error) {
53349            videojs$1.log.warn('Error encountered when parsing cues: ' + error.message);
53350          };
53351  
53352          if (segmentInfo.segment.map) {
53353            var mapData = segmentInfo.segment.map.bytes;
53354  
53355            if (decodeBytesToString) {
53356              mapData = uint8ToUtf8(mapData);
53357            }
53358  
53359            parser.parse(mapData);
53360          }
53361  
53362          var segmentData = segmentInfo.bytes;
53363  
53364          if (decodeBytesToString) {
53365            segmentData = uint8ToUtf8(segmentData);
53366          }
53367  
53368          parser.parse(segmentData);
53369          parser.flush();
53370        }
53371        /**
53372         * Updates the start and end times of any cues parsed by the WebVTT parser using
53373         * the information parsed from the X-TIMESTAMP-MAP header and a TS to media time mapping
53374         * from the SyncController
53375         *
53376         * @param {Object} segmentInfo
53377         *        a segment info object that describes the current segment
53378         * @param {Object} mappingObj
53379         *        object containing a mapping from TS to media time
53380         * @param {Object} playlist
53381         *        the playlist object containing the segment
53382         * @private
53383         */
53384  
53385      }, {
53386        key: 'updateTimeMapping_',
53387        value: function updateTimeMapping_(segmentInfo, mappingObj, playlist) {
53388          var segment = segmentInfo.segment;
53389  
53390          if (!mappingObj) {
53391            // If the sync controller does not have a mapping of TS to Media Time for the
53392            // timeline, then we don't have enough information to update the cue
53393            // start/end times
53394            return;
53395          }
53396  
53397          if (!segmentInfo.cues.length) {
53398            // If there are no cues, we also do not have enough information to figure out
53399            // segment timing. Mark that the segment contains no cues so we don't re-request
53400            // an empty segment.
53401            segment.empty = true;
53402            return;
53403          }
53404  
53405          var timestampmap = segmentInfo.timestampmap;
53406          var diff = timestampmap.MPEGTS / 90000 - timestampmap.LOCAL + mappingObj.mapping;
53407          segmentInfo.cues.forEach(function (cue) {
53408            // First convert cue time to TS time using the timestamp-map provided within the vtt
53409            cue.startTime += diff;
53410            cue.endTime += diff;
53411          });
53412  
53413          if (!playlist.syncInfo) {
53414            var firstStart = segmentInfo.cues[0].startTime;
53415            var lastStart = segmentInfo.cues[segmentInfo.cues.length - 1].startTime;
53416            playlist.syncInfo = {
53417              mediaSequence: playlist.mediaSequence + segmentInfo.mediaIndex,
53418              time: Math.min(firstStart, lastStart - segment.duration)
53419            };
53420          }
53421        }
53422      }]);
53423      return VTTSegmentLoader;
53424    }(SegmentLoader);
53425    /**
53426     * @file ad-cue-tags.js
53427     */
53428  
53429    /**
53430     * Searches for an ad cue that overlaps with the given mediaTime
53431     */
53432  
53433  
53434    var findAdCue = function findAdCue(track, mediaTime) {
53435      var cues = track.cues;
53436  
53437      for (var i = 0; i < cues.length; i++) {
53438        var cue = cues[i];
53439  
53440        if (mediaTime >= cue.adStartTime && mediaTime <= cue.adEndTime) {
53441          return cue;
53442        }
53443      }
53444  
53445      return null;
53446    };
53447  
53448    var updateAdCues = function updateAdCues(media, track) {
53449      var offset = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 0;
53450  
53451      if (!media.segments) {
53452        return;
53453      }
53454  
53455      var mediaTime = offset;
53456      var cue = void 0;
53457  
53458      for (var i = 0; i < media.segments.length; i++) {
53459        var segment = media.segments[i];
53460  
53461        if (!cue) {
53462          // Since the cues will span for at least the segment duration, adding a fudge
53463          // factor of half segment duration will prevent duplicate cues from being
53464          // created when timing info is not exact (e.g. cue start time initialized
53465          // at 10.006677, but next call mediaTime is 10.003332 )
53466          cue = findAdCue(track, mediaTime + segment.duration / 2);
53467        }
53468  
53469        if (cue) {
53470          if ('cueIn' in segment) {
53471            // Found a CUE-IN so end the cue
53472            cue.endTime = mediaTime;
53473            cue.adEndTime = mediaTime;
53474            mediaTime += segment.duration;
53475            cue = null;
53476            continue;
53477          }
53478  
53479          if (mediaTime < cue.endTime) {
53480            // Already processed this mediaTime for this cue
53481            mediaTime += segment.duration;
53482            continue;
53483          } // otherwise extend cue until a CUE-IN is found
53484  
53485  
53486          cue.endTime += segment.duration;
53487        } else {
53488          if ('cueOut' in segment) {
53489            cue = new window$3.VTTCue(mediaTime, mediaTime + segment.duration, segment.cueOut);
53490            cue.adStartTime = mediaTime; // Assumes tag format to be
53491            // #EXT-X-CUE-OUT:30
53492  
53493            cue.adEndTime = mediaTime + parseFloat(segment.cueOut);
53494            track.addCue(cue);
53495          }
53496  
53497          if ('cueOutCont' in segment) {
53498            // Entered into the middle of an ad cue
53499            var adOffset = void 0;
53500            var adTotal = void 0; // Assumes tag formate to be
53501            // #EXT-X-CUE-OUT-CONT:10/30
53502  
53503            var _segment$cueOutCont$s = segment.cueOutCont.split('/').map(parseFloat);
53504  
53505            var _segment$cueOutCont$s2 = slicedToArray(_segment$cueOutCont$s, 2);
53506  
53507            adOffset = _segment$cueOutCont$s2[0];
53508            adTotal = _segment$cueOutCont$s2[1];
53509            cue = new window$3.VTTCue(mediaTime, mediaTime + segment.duration, '');
53510            cue.adStartTime = mediaTime - adOffset;
53511            cue.adEndTime = cue.adStartTime + adTotal;
53512            track.addCue(cue);
53513          }
53514        }
53515  
53516        mediaTime += segment.duration;
53517      }
53518    };
53519    /**
53520     * @file sync-controller.js
53521     */
53522  
53523  
53524    var tsprobe = tsInspector.inspect;
53525    var syncPointStrategies = [// Stategy "VOD": Handle the VOD-case where the sync-point is *always*
53526    //                the equivalence display-time 0 === segment-index 0
53527    {
53528      name: 'VOD',
53529      run: function run(syncController, playlist, duration$$1, currentTimeline, currentTime) {
53530        if (duration$$1 !== Infinity) {
53531          var syncPoint = {
53532            time: 0,
53533            segmentIndex: 0
53534          };
53535          return syncPoint;
53536        }
53537  
53538        return null;
53539      }
53540    }, // Stategy "ProgramDateTime": We have a program-date-time tag in this playlist
53541    {
53542      name: 'ProgramDateTime',
53543      run: function run(syncController, playlist, duration$$1, currentTimeline, currentTime) {
53544        if (!syncController.datetimeToDisplayTime) {
53545          return null;
53546        }
53547  
53548        var segments = playlist.segments || [];
53549        var syncPoint = null;
53550        var lastDistance = null;
53551        currentTime = currentTime || 0;
53552  
53553        for (var i = 0; i < segments.length; i++) {
53554          var segment = segments[i];
53555  
53556          if (segment.dateTimeObject) {
53557            var segmentTime = segment.dateTimeObject.getTime() / 1000;
53558            var segmentStart = segmentTime + syncController.datetimeToDisplayTime;
53559            var distance = Math.abs(currentTime - segmentStart); // Once the distance begins to increase, or if distance is 0, we have passed
53560            // currentTime and can stop looking for better candidates
53561  
53562            if (lastDistance !== null && (distance === 0 || lastDistance < distance)) {
53563              break;
53564            }
53565  
53566            lastDistance = distance;
53567            syncPoint = {
53568              time: segmentStart,
53569              segmentIndex: i
53570            };
53571          }
53572        }
53573  
53574        return syncPoint;
53575      }
53576    }, // Stategy "Segment": We have a known time mapping for a timeline and a
53577    //                    segment in the current timeline with timing data
53578    {
53579      name: 'Segment',
53580      run: function run(syncController, playlist, duration$$1, currentTimeline, currentTime) {
53581        var segments = playlist.segments || [];
53582        var syncPoint = null;
53583        var lastDistance = null;
53584        currentTime = currentTime || 0;
53585  
53586        for (var i = 0; i < segments.length; i++) {
53587          var segment = segments[i];
53588  
53589          if (segment.timeline === currentTimeline && typeof segment.start !== 'undefined') {
53590            var distance = Math.abs(currentTime - segment.start); // Once the distance begins to increase, we have passed
53591            // currentTime and can stop looking for better candidates
53592  
53593            if (lastDistance !== null && lastDistance < distance) {
53594              break;
53595            }
53596  
53597            if (!syncPoint || lastDistance === null || lastDistance >= distance) {
53598              lastDistance = distance;
53599              syncPoint = {
53600                time: segment.start,
53601                segmentIndex: i
53602              };
53603            }
53604          }
53605        }
53606  
53607        return syncPoint;
53608      }
53609    }, // Stategy "Discontinuity": We have a discontinuity with a known
53610    //                          display-time
53611    {
53612      name: 'Discontinuity',
53613      run: function run(syncController, playlist, duration$$1, currentTimeline, currentTime) {
53614        var syncPoint = null;
53615        currentTime = currentTime || 0;
53616  
53617        if (playlist.discontinuityStarts && playlist.discontinuityStarts.length) {
53618          var lastDistance = null;
53619  
53620          for (var i = 0; i < playlist.discontinuityStarts.length; i++) {
53621            var segmentIndex = playlist.discontinuityStarts[i];
53622            var discontinuity = playlist.discontinuitySequence + i + 1;
53623            var discontinuitySync = syncController.discontinuities[discontinuity];
53624  
53625            if (discontinuitySync) {
53626              var distance = Math.abs(currentTime - discontinuitySync.time); // Once the distance begins to increase, we have passed
53627              // currentTime and can stop looking for better candidates
53628  
53629              if (lastDistance !== null && lastDistance < distance) {
53630                break;
53631              }
53632  
53633              if (!syncPoint || lastDistance === null || lastDistance >= distance) {
53634                lastDistance = distance;
53635                syncPoint = {
53636                  time: discontinuitySync.time,
53637                  segmentIndex: segmentIndex
53638                };
53639              }
53640            }
53641          }
53642        }
53643  
53644        return syncPoint;
53645      }
53646    }, // Stategy "Playlist": We have a playlist with a known mapping of
53647    //                     segment index to display time
53648    {
53649      name: 'Playlist',
53650      run: function run(syncController, playlist, duration$$1, currentTimeline, currentTime) {
53651        if (playlist.syncInfo) {
53652          var syncPoint = {
53653            time: playlist.syncInfo.time,
53654            segmentIndex: playlist.syncInfo.mediaSequence - playlist.mediaSequence
53655          };
53656          return syncPoint;
53657        }
53658  
53659        return null;
53660      }
53661    }];
53662  
53663    var SyncController = function (_videojs$EventTarget) {
53664      inherits$2(SyncController, _videojs$EventTarget);
53665  
53666      function SyncController() {
53667        classCallCheck$1(this, SyncController); // Segment Loader state variables...
53668        // ...for synching across variants
53669  
53670        var _this = possibleConstructorReturn$1(this, (SyncController.__proto__ || Object.getPrototypeOf(SyncController)).call(this));
53671  
53672        _this.inspectCache_ = undefined; // ...for synching across variants
53673  
53674        _this.timelines = [];
53675        _this.discontinuities = [];
53676        _this.datetimeToDisplayTime = null;
53677        _this.logger_ = logger('SyncController');
53678        return _this;
53679      }
53680      /**
53681       * Find a sync-point for the playlist specified
53682       *
53683       * A sync-point is defined as a known mapping from display-time to
53684       * a segment-index in the current playlist.
53685       *
53686       * @param {Playlist} playlist
53687       *        The playlist that needs a sync-point
53688       * @param {Number} duration
53689       *        Duration of the MediaSource (Infinite if playing a live source)
53690       * @param {Number} currentTimeline
53691       *        The last timeline from which a segment was loaded
53692       * @returns {Object}
53693       *          A sync-point object
53694       */
53695  
53696  
53697      createClass$1(SyncController, [{
53698        key: 'getSyncPoint',
53699        value: function getSyncPoint(playlist, duration$$1, currentTimeline, currentTime) {
53700          var syncPoints = this.runStrategies_(playlist, duration$$1, currentTimeline, currentTime);
53701  
53702          if (!syncPoints.length) {
53703            // Signal that we need to attempt to get a sync-point manually
53704            // by fetching a segment in the playlist and constructing
53705            // a sync-point from that information
53706            return null;
53707          } // Now find the sync-point that is closest to the currentTime because
53708          // that should result in the most accurate guess about which segment
53709          // to fetch
53710  
53711  
53712          return this.selectSyncPoint_(syncPoints, {
53713            key: 'time',
53714            value: currentTime
53715          });
53716        }
53717        /**
53718         * Calculate the amount of time that has expired off the playlist during playback
53719         *
53720         * @param {Playlist} playlist
53721         *        Playlist object to calculate expired from
53722         * @param {Number} duration
53723         *        Duration of the MediaSource (Infinity if playling a live source)
53724         * @returns {Number|null}
53725         *          The amount of time that has expired off the playlist during playback. Null
53726         *          if no sync-points for the playlist can be found.
53727         */
53728  
53729      }, {
53730        key: 'getExpiredTime',
53731        value: function getExpiredTime(playlist, duration$$1) {
53732          if (!playlist || !playlist.segments) {
53733            return null;
53734          }
53735  
53736          var syncPoints = this.runStrategies_(playlist, duration$$1, playlist.discontinuitySequence, 0); // Without sync-points, there is not enough information to determine the expired time
53737  
53738          if (!syncPoints.length) {
53739            return null;
53740          }
53741  
53742          var syncPoint = this.selectSyncPoint_(syncPoints, {
53743            key: 'segmentIndex',
53744            value: 0
53745          }); // If the sync-point is beyond the start of the playlist, we want to subtract the
53746          // duration from index 0 to syncPoint.segmentIndex instead of adding.
53747  
53748          if (syncPoint.segmentIndex > 0) {
53749            syncPoint.time *= -1;
53750          }
53751  
53752          return Math.abs(syncPoint.time + sumDurations(playlist, syncPoint.segmentIndex, 0));
53753        }
53754        /**
53755         * Runs each sync-point strategy and returns a list of sync-points returned by the
53756         * strategies
53757         *
53758         * @private
53759         * @param {Playlist} playlist
53760         *        The playlist that needs a sync-point
53761         * @param {Number} duration
53762         *        Duration of the MediaSource (Infinity if playing a live source)
53763         * @param {Number} currentTimeline
53764         *        The last timeline from which a segment was loaded
53765         * @returns {Array}
53766         *          A list of sync-point objects
53767         */
53768  
53769      }, {
53770        key: 'runStrategies_',
53771        value: function runStrategies_(playlist, duration$$1, currentTimeline, currentTime) {
53772          var syncPoints = []; // Try to find a sync-point in by utilizing various strategies...
53773  
53774          for (var i = 0; i < syncPointStrategies.length; i++) {
53775            var strategy = syncPointStrategies[i];
53776            var syncPoint = strategy.run(this, playlist, duration$$1, currentTimeline, currentTime);
53777  
53778            if (syncPoint) {
53779              syncPoint.strategy = strategy.name;
53780              syncPoints.push({
53781                strategy: strategy.name,
53782                syncPoint: syncPoint
53783              });
53784            }
53785          }
53786  
53787          return syncPoints;
53788        }
53789        /**
53790         * Selects the sync-point nearest the specified target
53791         *
53792         * @private
53793         * @param {Array} syncPoints
53794         *        List of sync-points to select from
53795         * @param {Object} target
53796         *        Object specifying the property and value we are targeting
53797         * @param {String} target.key
53798         *        Specifies the property to target. Must be either 'time' or 'segmentIndex'
53799         * @param {Number} target.value
53800         *        The value to target for the specified key.
53801         * @returns {Object}
53802         *          The sync-point nearest the target
53803         */
53804  
53805      }, {
53806        key: 'selectSyncPoint_',
53807        value: function selectSyncPoint_(syncPoints, target) {
53808          var bestSyncPoint = syncPoints[0].syncPoint;
53809          var bestDistance = Math.abs(syncPoints[0].syncPoint[target.key] - target.value);
53810          var bestStrategy = syncPoints[0].strategy;
53811  
53812          for (var i = 1; i < syncPoints.length; i++) {
53813            var newDistance = Math.abs(syncPoints[i].syncPoint[target.key] - target.value);
53814  
53815            if (newDistance < bestDistance) {
53816              bestDistance = newDistance;
53817              bestSyncPoint = syncPoints[i].syncPoint;
53818              bestStrategy = syncPoints[i].strategy;
53819            }
53820          }
53821  
53822          this.logger_('syncPoint for [' + target.key + ': ' + target.value + '] chosen with strategy' + (' [' + bestStrategy + ']: [time:' + bestSyncPoint.time + ',') + (' segmentIndex:' + bestSyncPoint.segmentIndex + ']'));
53823          return bestSyncPoint;
53824        }
53825        /**
53826         * Save any meta-data present on the segments when segments leave
53827         * the live window to the playlist to allow for synchronization at the
53828         * playlist level later.
53829         *
53830         * @param {Playlist} oldPlaylist - The previous active playlist
53831         * @param {Playlist} newPlaylist - The updated and most current playlist
53832         */
53833  
53834      }, {
53835        key: 'saveExpiredSegmentInfo',
53836        value: function saveExpiredSegmentInfo(oldPlaylist, newPlaylist) {
53837          var mediaSequenceDiff = newPlaylist.mediaSequence - oldPlaylist.mediaSequence; // When a segment expires from the playlist and it has a start time
53838          // save that information as a possible sync-point reference in future
53839  
53840          for (var i = mediaSequenceDiff - 1; i >= 0; i--) {
53841            var lastRemovedSegment = oldPlaylist.segments[i];
53842  
53843            if (lastRemovedSegment && typeof lastRemovedSegment.start !== 'undefined') {
53844              newPlaylist.syncInfo = {
53845                mediaSequence: oldPlaylist.mediaSequence + i,
53846                time: lastRemovedSegment.start
53847              };
53848              this.logger_('playlist refresh sync: [time:' + newPlaylist.syncInfo.time + ',' + (' mediaSequence: ' + newPlaylist.syncInfo.mediaSequence + ']'));
53849              this.trigger('syncinfoupdate');
53850              break;
53851            }
53852          }
53853        }
53854        /**
53855         * Save the mapping from playlist's ProgramDateTime to display. This should
53856         * only ever happen once at the start of playback.
53857         *
53858         * @param {Playlist} playlist - The currently active playlist
53859         */
53860  
53861      }, {
53862        key: 'setDateTimeMapping',
53863        value: function setDateTimeMapping(playlist) {
53864          if (!this.datetimeToDisplayTime && playlist.segments && playlist.segments.length && playlist.segments[0].dateTimeObject) {
53865            var playlistTimestamp = playlist.segments[0].dateTimeObject.getTime() / 1000;
53866            this.datetimeToDisplayTime = -playlistTimestamp;
53867          }
53868        }
53869        /**
53870         * Reset the state of the inspection cache when we do a rendition
53871         * switch
53872         */
53873  
53874      }, {
53875        key: 'reset',
53876        value: function reset() {
53877          this.inspectCache_ = undefined;
53878        }
53879        /**
53880         * Probe or inspect a fmp4 or an mpeg2-ts segment to determine the start
53881         * and end of the segment in it's internal "media time". Used to generate
53882         * mappings from that internal "media time" to the display time that is
53883         * shown on the player.
53884         *
53885         * @param {SegmentInfo} segmentInfo - The current active request information
53886         */
53887  
53888      }, {
53889        key: 'probeSegmentInfo',
53890        value: function probeSegmentInfo(segmentInfo) {
53891          var segment = segmentInfo.segment;
53892          var playlist = segmentInfo.playlist;
53893          var timingInfo = void 0;
53894  
53895          if (segment.map) {
53896            timingInfo = this.probeMp4Segment_(segmentInfo);
53897          } else {
53898            timingInfo = this.probeTsSegment_(segmentInfo);
53899          }
53900  
53901          if (timingInfo) {
53902            if (this.calculateSegmentTimeMapping_(segmentInfo, timingInfo)) {
53903              this.saveDiscontinuitySyncInfo_(segmentInfo); // If the playlist does not have sync information yet, record that information
53904              // now with segment timing information
53905  
53906              if (!playlist.syncInfo) {
53907                playlist.syncInfo = {
53908                  mediaSequence: playlist.mediaSequence + segmentInfo.mediaIndex,
53909                  time: segment.start
53910                };
53911              }
53912            }
53913          }
53914  
53915          return timingInfo;
53916        }
53917        /**
53918         * Probe an fmp4 segment to determine the start of the segment
53919         * in it's internal "composition time", which is equal to the base
53920         * media decode time plus the composition time offset value
53921         *
53922         * @private
53923         * @param {SegmentInfo} segmentInfo - The current active request information
53924         * @return {object} The start and end time of the current segment in "composition time"
53925         */
53926  
53927      }, {
53928        key: 'probeMp4Segment_',
53929        value: function probeMp4Segment_(segmentInfo) {
53930          var segment = segmentInfo.segment; // get timescales from init segment
53931  
53932          var timescales = probe.timescale(segment.map.bytes); // calculate composition start time using the timescales and information
53933          // contained within the media segment
53934  
53935          var compositionStartTime = probe.compositionStartTime(timescales, segmentInfo.bytes);
53936  
53937          if (segmentInfo.timestampOffset !== null) {
53938            segmentInfo.timestampOffset -= compositionStartTime;
53939          }
53940  
53941          return {
53942            start: compositionStartTime,
53943            end: compositionStartTime + segment.duration
53944          };
53945        }
53946        /**
53947         * Probe an mpeg2-ts segment to determine the start and end of the segment
53948         * in it's internal "media time".
53949         *
53950         * @private
53951         * @param {SegmentInfo} segmentInfo - The current active request information
53952         * @return {object} The start and end time of the current segment in "media time"
53953         */
53954  
53955      }, {
53956        key: 'probeTsSegment_',
53957        value: function probeTsSegment_(segmentInfo) {
53958          var timeInfo = tsprobe(segmentInfo.bytes, this.inspectCache_);
53959          var segmentStartTime = void 0;
53960          var segmentEndTime = void 0;
53961          var segmentTimestampInfo = void 0;
53962  
53963          if (!timeInfo) {
53964            return null;
53965          }
53966  
53967          if (timeInfo.video && timeInfo.video.length === 2) {
53968            this.inspectCache_ = timeInfo.video[1].dts;
53969            segmentStartTime = timeInfo.video[0].dtsTime;
53970            segmentEndTime = timeInfo.video[1].dtsTime;
53971            segmentTimestampInfo = timeInfo.video;
53972          } else if (timeInfo.audio && timeInfo.audio.length === 2) {
53973            this.inspectCache_ = timeInfo.audio[1].dts;
53974            segmentStartTime = timeInfo.audio[0].dtsTime;
53975            segmentEndTime = timeInfo.audio[1].dtsTime;
53976            segmentTimestampInfo = timeInfo.audio;
53977          }
53978  
53979          var probedInfo = {
53980            segmentTimestampInfo: segmentTimestampInfo,
53981            start: segmentStartTime,
53982            end: segmentEndTime,
53983            containsVideo: timeInfo.video && timeInfo.video.length === 2,
53984            containsAudio: timeInfo.audio && timeInfo.audio.length === 2
53985          };
53986          return probedInfo;
53987        }
53988      }, {
53989        key: 'timestampOffsetForTimeline',
53990        value: function timestampOffsetForTimeline(timeline) {
53991          if (typeof this.timelines[timeline] === 'undefined') {
53992            return null;
53993          }
53994  
53995          return this.timelines[timeline].time;
53996        }
53997      }, {
53998        key: 'mappingForTimeline',
53999        value: function mappingForTimeline(timeline) {
54000          if (typeof this.timelines[timeline] === 'undefined') {
54001            return null;
54002          }
54003  
54004          return this.timelines[timeline].mapping;
54005        }
54006        /**
54007         * Use the "media time" for a segment to generate a mapping to "display time" and
54008         * save that display time to the segment.
54009         *
54010         * @private
54011         * @param {SegmentInfo} segmentInfo
54012         *        The current active request information
54013         * @param {object} timingInfo
54014         *        The start and end time of the current segment in "media time"
54015         * @returns {Boolean}
54016         *          Returns false if segment time mapping could not be calculated
54017         */
54018  
54019      }, {
54020        key: 'calculateSegmentTimeMapping_',
54021        value: function calculateSegmentTimeMapping_(segmentInfo, timingInfo) {
54022          var segment = segmentInfo.segment;
54023          var mappingObj = this.timelines[segmentInfo.timeline];
54024  
54025          if (segmentInfo.timestampOffset !== null) {
54026            mappingObj = {
54027              time: segmentInfo.startOfSegment,
54028              mapping: segmentInfo.startOfSegment - timingInfo.start
54029            };
54030            this.timelines[segmentInfo.timeline] = mappingObj;
54031            this.trigger('timestampoffset');
54032            this.logger_('time mapping for timeline ' + segmentInfo.timeline + ': ' + ('[time: ' + mappingObj.time + '] [mapping: ' + mappingObj.mapping + ']'));
54033            segment.start = segmentInfo.startOfSegment;
54034            segment.end = timingInfo.end + mappingObj.mapping;
54035          } else if (mappingObj) {
54036            segment.start = timingInfo.start + mappingObj.mapping;
54037            segment.end = timingInfo.end + mappingObj.mapping;
54038          } else {
54039            return false;
54040          }
54041  
54042          return true;
54043        }
54044        /**
54045         * Each time we have discontinuity in the playlist, attempt to calculate the location
54046         * in display of the start of the discontinuity and save that. We also save an accuracy
54047         * value so that we save values with the most accuracy (closest to 0.)
54048         *
54049         * @private
54050         * @param {SegmentInfo} segmentInfo - The current active request information
54051         */
54052  
54053      }, {
54054        key: 'saveDiscontinuitySyncInfo_',
54055        value: function saveDiscontinuitySyncInfo_(segmentInfo) {
54056          var playlist = segmentInfo.playlist;
54057          var segment = segmentInfo.segment; // If the current segment is a discontinuity then we know exactly where
54058          // the start of the range and it's accuracy is 0 (greater accuracy values
54059          // mean more approximation)
54060  
54061          if (segment.discontinuity) {
54062            this.discontinuities[segment.timeline] = {
54063              time: segment.start,
54064              accuracy: 0
54065            };
54066          } else if (playlist.discontinuityStarts && playlist.discontinuityStarts.length) {
54067            // Search for future discontinuities that we can provide better timing
54068            // information for and save that information for sync purposes
54069            for (var i = 0; i < playlist.discontinuityStarts.length; i++) {
54070              var segmentIndex = playlist.discontinuityStarts[i];
54071              var discontinuity = playlist.discontinuitySequence + i + 1;
54072              var mediaIndexDiff = segmentIndex - segmentInfo.mediaIndex;
54073              var accuracy = Math.abs(mediaIndexDiff);
54074  
54075              if (!this.discontinuities[discontinuity] || this.discontinuities[discontinuity].accuracy > accuracy) {
54076                var time = void 0;
54077  
54078                if (mediaIndexDiff < 0) {
54079                  time = segment.start - sumDurations(playlist, segmentInfo.mediaIndex, segmentIndex);
54080                } else {
54081                  time = segment.end + sumDurations(playlist, segmentInfo.mediaIndex + 1, segmentIndex);
54082                }
54083  
54084                this.discontinuities[discontinuity] = {
54085                  time: time,
54086                  accuracy: accuracy
54087                };
54088              }
54089            }
54090          }
54091        }
54092      }, {
54093        key: 'dispose',
54094        value: function dispose() {
54095          this.trigger('dispose');
54096          this.off();
54097        }
54098      }]);
54099      return SyncController;
54100    }(videojs$1.EventTarget);
54101  
54102    var Decrypter$1 = new shimWorker("./decrypter-worker.worker.js", function (window, document$$1) {
54103      var self = this;
54104  
54105      var decrypterWorker = function () {
54106        /*
54107         * pkcs7.pad
54108         * https://github.com/brightcove/pkcs7
54109         *
54110         * Copyright (c) 2014 Brightcove
54111         * Licensed under the apache2 license.
54112         */
54113  
54114        /**
54115         * Returns the subarray of a Uint8Array without PKCS#7 padding.
54116         * @param padded {Uint8Array} unencrypted bytes that have been padded
54117         * @return {Uint8Array} the unpadded bytes
54118         * @see http://tools.ietf.org/html/rfc5652
54119         */
54120        function unpad(padded) {
54121          return padded.subarray(0, padded.byteLength - padded[padded.byteLength - 1]);
54122        }
54123  
54124        var classCallCheck = function classCallCheck(instance, Constructor) {
54125          if (!(instance instanceof Constructor)) {
54126            throw new TypeError("Cannot call a class as a function");
54127          }
54128        };
54129  
54130        var createClass = function () {
54131          function defineProperties(target, props) {
54132            for (var i = 0; i < props.length; i++) {
54133              var descriptor = props[i];
54134              descriptor.enumerable = descriptor.enumerable || false;
54135              descriptor.configurable = true;
54136              if ("value" in descriptor) descriptor.writable = true;
54137              Object.defineProperty(target, descriptor.key, descriptor);
54138            }
54139          }
54140  
54141          return function (Constructor, protoProps, staticProps) {
54142            if (protoProps) defineProperties(Constructor.prototype, protoProps);
54143            if (staticProps) defineProperties(Constructor, staticProps);
54144            return Constructor;
54145          };
54146        }();
54147  
54148        var inherits = function inherits(subClass, superClass) {
54149          if (typeof superClass !== "function" && superClass !== null) {
54150            throw new TypeError("Super expression must either be null or a function, not " + typeof superClass);
54151          }
54152  
54153          subClass.prototype = Object.create(superClass && superClass.prototype, {
54154            constructor: {
54155              value: subClass,
54156              enumerable: false,
54157              writable: true,
54158              configurable: true
54159            }
54160          });
54161          if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass;
54162        };
54163  
54164        var possibleConstructorReturn = function possibleConstructorReturn(self, call) {
54165          if (!self) {
54166            throw new ReferenceError("this hasn't been initialised - super() hasn't been called");
54167          }
54168  
54169          return call && (typeof call === "object" || typeof call === "function") ? call : self;
54170        };
54171        /**
54172         * @file aes.js
54173         *
54174         * This file contains an adaptation of the AES decryption algorithm
54175         * from the Standford Javascript Cryptography Library. That work is
54176         * covered by the following copyright and permissions notice:
54177         *
54178         * Copyright 2009-2010 Emily Stark, Mike Hamburg, Dan Boneh.
54179         * All rights reserved.
54180         *
54181         * Redistribution and use in source and binary forms, with or without
54182         * modification, are permitted provided that the following conditions are
54183         * met:
54184         *
54185         * 1. Redistributions of source code must retain the above copyright
54186         *    notice, this list of conditions and the following disclaimer.
54187         *
54188         * 2. Redistributions in binary form must reproduce the above
54189         *    copyright notice, this list of conditions and the following
54190         *    disclaimer in the documentation and/or other materials provided
54191         *    with the distribution.
54192         *
54193         * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR
54194         * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
54195         * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
54196         * DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> OR CONTRIBUTORS BE
54197         * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
54198         * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
54199         * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
54200         * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
54201         * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
54202         * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
54203         * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
54204         *
54205         * The views and conclusions contained in the software and documentation
54206         * are those of the authors and should not be interpreted as representing
54207         * official policies, either expressed or implied, of the authors.
54208         */
54209  
54210        /**
54211         * Expand the S-box tables.
54212         *
54213         * @private
54214         */
54215  
54216  
54217        var precompute = function precompute() {
54218          var tables = [[[], [], [], [], []], [[], [], [], [], []]];
54219          var encTable = tables[0];
54220          var decTable = tables[1];
54221          var sbox = encTable[4];
54222          var sboxInv = decTable[4];
54223          var i = void 0;
54224          var x = void 0;
54225          var xInv = void 0;
54226          var d = [];
54227          var th = [];
54228          var x2 = void 0;
54229          var x4 = void 0;
54230          var x8 = void 0;
54231          var s = void 0;
54232          var tEnc = void 0;
54233          var tDec = void 0; // Compute double and third tables
54234  
54235          for (i = 0; i < 256; i++) {
54236            th[(d[i] = i << 1 ^ (i >> 7) * 283) ^ i] = i;
54237          }
54238  
54239          for (x = xInv = 0; !sbox[x]; x ^= x2 || 1, xInv = th[xInv] || 1) {
54240            // Compute sbox
54241            s = xInv ^ xInv << 1 ^ xInv << 2 ^ xInv << 3 ^ xInv << 4;
54242            s = s >> 8 ^ s & 255 ^ 99;
54243            sbox[x] = s;
54244            sboxInv[s] = x; // Compute MixColumns
54245  
54246            x8 = d[x4 = d[x2 = d[x]]];
54247            tDec = x8 * 0x1010101 ^ x4 * 0x10001 ^ x2 * 0x101 ^ x * 0x1010100;
54248            tEnc = d[s] * 0x101 ^ s * 0x1010100;
54249  
54250            for (i = 0; i < 4; i++) {
54251              encTable[i][x] = tEnc = tEnc << 24 ^ tEnc >>> 8;
54252              decTable[i][s] = tDec = tDec << 24 ^ tDec >>> 8;
54253            }
54254          } // Compactify. Considerable speedup on Firefox.
54255  
54256  
54257          for (i = 0; i < 5; i++) {
54258            encTable[i] = encTable[i].slice(0);
54259            decTable[i] = decTable[i].slice(0);
54260          }
54261  
54262          return tables;
54263        };
54264  
54265        var aesTables = null;
54266        /**
54267         * Schedule out an AES key for both encryption and decryption. This
54268         * is a low-level class. Use a cipher mode to do bulk encryption.
54269         *
54270         * @class AES
54271         * @param key {Array} The key as an array of 4, 6 or 8 words.
54272         */
54273  
54274        var AES = function () {
54275          function AES(key) {
54276            classCallCheck(this, AES);
54277            /**
54278             * The expanded S-box and inverse S-box tables. These will be computed
54279             * on the client so that we don't have to send them down the wire.
54280             *
54281             * There are two tables, _tables[0] is for encryption and
54282             * _tables[1] is for decryption.
54283             *
54284             * The first 4 sub-tables are the expanded S-box with MixColumns. The
54285             * last (_tables[01][4]) is the S-box itself.
54286             *
54287             * @private
54288             */
54289            // if we have yet to precompute the S-box tables
54290            // do so now
54291  
54292            if (!aesTables) {
54293              aesTables = precompute();
54294            } // then make a copy of that object for use
54295  
54296  
54297            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()]];
54298            var i = void 0;
54299            var j = void 0;
54300            var tmp = void 0;
54301            var encKey = void 0;
54302            var decKey = void 0;
54303            var sbox = this._tables[0][4];
54304            var decTable = this._tables[1];
54305            var keyLen = key.length;
54306            var rcon = 1;
54307  
54308            if (keyLen !== 4 && keyLen !== 6 && keyLen !== 8) {
54309              throw new Error('Invalid aes key size');
54310            }
54311  
54312            encKey = key.slice(0);
54313            decKey = [];
54314            this._key = [encKey, decKey]; // schedule encryption keys
54315  
54316            for (i = keyLen; i < 4 * keyLen + 28; i++) {
54317              tmp = encKey[i - 1]; // apply sbox
54318  
54319              if (i % keyLen === 0 || keyLen === 8 && i % keyLen === 4) {
54320                tmp = sbox[tmp >>> 24] << 24 ^ sbox[tmp >> 16 & 255] << 16 ^ sbox[tmp >> 8 & 255] << 8 ^ sbox[tmp & 255]; // shift rows and add rcon
54321  
54322                if (i % keyLen === 0) {
54323                  tmp = tmp << 8 ^ tmp >>> 24 ^ rcon << 24;
54324                  rcon = rcon << 1 ^ (rcon >> 7) * 283;
54325                }
54326              }
54327  
54328              encKey[i] = encKey[i - keyLen] ^ tmp;
54329            } // schedule decryption keys
54330  
54331  
54332            for (j = 0; i; j++, i--) {
54333              tmp = encKey[j & 3 ? i : i - 4];
54334  
54335              if (i <= 4 || j < 4) {
54336                decKey[j] = tmp;
54337              } else {
54338                decKey[j] = decTable[0][sbox[tmp >>> 24]] ^ decTable[1][sbox[tmp >> 16 & 255]] ^ decTable[2][sbox[tmp >> 8 & 255]] ^ decTable[3][sbox[tmp & 255]];
54339              }
54340            }
54341          }
54342          /**
54343           * Decrypt 16 bytes, specified as four 32-bit words.
54344           *
54345           * @param {Number} encrypted0 the first word to decrypt
54346           * @param {Number} encrypted1 the second word to decrypt
54347           * @param {Number} encrypted2 the third word to decrypt
54348           * @param {Number} encrypted3 the fourth word to decrypt
54349           * @param {Int32Array} out the array to write the decrypted words
54350           * into
54351           * @param {Number} offset the offset into the output array to start
54352           * writing results
54353           * @return {Array} The plaintext.
54354           */
54355  
54356  
54357          AES.prototype.decrypt = function decrypt$$1(encrypted0, encrypted1, encrypted2, encrypted3, out, offset) {
54358            var key = this._key[1]; // state variables a,b,c,d are loaded with pre-whitened data
54359  
54360            var a = encrypted0 ^ key[0];
54361            var b = encrypted3 ^ key[1];
54362            var c = encrypted2 ^ key[2];
54363            var d = encrypted1 ^ key[3];
54364            var a2 = void 0;
54365            var b2 = void 0;
54366            var c2 = void 0; // key.length === 2 ?
54367  
54368            var nInnerRounds = key.length / 4 - 2;
54369            var i = void 0;
54370            var kIndex = 4;
54371            var table = this._tables[1]; // load up the tables
54372  
54373            var table0 = table[0];
54374            var table1 = table[1];
54375            var table2 = table[2];
54376            var table3 = table[3];
54377            var sbox = table[4]; // Inner rounds. Cribbed from OpenSSL.
54378  
54379            for (i = 0; i < nInnerRounds; i++) {
54380              a2 = table0[a >>> 24] ^ table1[b >> 16 & 255] ^ table2[c >> 8 & 255] ^ table3[d & 255] ^ key[kIndex];
54381              b2 = table0[b >>> 24] ^ table1[c >> 16 & 255] ^ table2[d >> 8 & 255] ^ table3[a & 255] ^ key[kIndex + 1];
54382              c2 = table0[c >>> 24] ^ table1[d >> 16 & 255] ^ table2[a >> 8 & 255] ^ table3[b & 255] ^ key[kIndex + 2];
54383              d = table0[d >>> 24] ^ table1[a >> 16 & 255] ^ table2[b >> 8 & 255] ^ table3[c & 255] ^ key[kIndex + 3];
54384              kIndex += 4;
54385              a = a2;
54386              b = b2;
54387              c = c2;
54388            } // Last round.
54389  
54390  
54391            for (i = 0; i < 4; i++) {
54392              out[(3 & -i) + offset] = sbox[a >>> 24] << 24 ^ sbox[b >> 16 & 255] << 16 ^ sbox[c >> 8 & 255] << 8 ^ sbox[d & 255] ^ key[kIndex++];
54393              a2 = a;
54394              a = b;
54395              b = c;
54396              c = d;
54397              d = a2;
54398            }
54399          };
54400  
54401          return AES;
54402        }();
54403        /**
54404         * @file stream.js
54405         */
54406  
54407        /**
54408         * A lightweight readable stream implemention that handles event dispatching.
54409         *
54410         * @class Stream
54411         */
54412  
54413  
54414        var Stream = function () {
54415          function Stream() {
54416            classCallCheck(this, Stream);
54417            this.listeners = {};
54418          }
54419          /**
54420           * Add a listener for a specified event type.
54421           *
54422           * @param {String} type the event name
54423           * @param {Function} listener the callback to be invoked when an event of
54424           * the specified type occurs
54425           */
54426  
54427  
54428          Stream.prototype.on = function on(type, listener) {
54429            if (!this.listeners[type]) {
54430              this.listeners[type] = [];
54431            }
54432  
54433            this.listeners[type].push(listener);
54434          };
54435          /**
54436           * Remove a listener for a specified event type.
54437           *
54438           * @param {String} type the event name
54439           * @param {Function} listener  a function previously registered for this
54440           * type of event through `on`
54441           * @return {Boolean} if we could turn it off or not
54442           */
54443  
54444  
54445          Stream.prototype.off = function off(type, listener) {
54446            if (!this.listeners[type]) {
54447              return false;
54448            }
54449  
54450            var index = this.listeners[type].indexOf(listener);
54451            this.listeners[type].splice(index, 1);
54452            return index > -1;
54453          };
54454          /**
54455           * Trigger an event of the specified type on this stream. Any additional
54456           * arguments to this function are passed as parameters to event listeners.
54457           *
54458           * @param {String} type the event name
54459           */
54460  
54461  
54462          Stream.prototype.trigger = function trigger(type) {
54463            var callbacks = this.listeners[type];
54464  
54465            if (!callbacks) {
54466              return;
54467            } // Slicing the arguments on every invocation of this method
54468            // can add a significant amount of overhead. Avoid the
54469            // intermediate object creation for the common case of a
54470            // single callback argument
54471  
54472  
54473            if (arguments.length === 2) {
54474              var length = callbacks.length;
54475  
54476              for (var i = 0; i < length; ++i) {
54477                callbacks[i].call(this, arguments[1]);
54478              }
54479            } else {
54480              var args = Array.prototype.slice.call(arguments, 1);
54481              var _length = callbacks.length;
54482  
54483              for (var _i = 0; _i < _length; ++_i) {
54484                callbacks[_i].apply(this, args);
54485              }
54486            }
54487          };
54488          /**
54489           * Destroys the stream and cleans up.
54490           */
54491  
54492  
54493          Stream.prototype.dispose = function dispose() {
54494            this.listeners = {};
54495          };
54496          /**
54497           * Forwards all `data` events on this stream to the destination stream. The
54498           * destination stream should provide a method `push` to receive the data
54499           * events as they arrive.
54500           *
54501           * @param {Stream} destination the stream that will receive all `data` events
54502           * @see http://nodejs.org/api/stream.html#stream_readable_pipe_destination_options
54503           */
54504  
54505  
54506          Stream.prototype.pipe = function pipe(destination) {
54507            this.on('data', function (data) {
54508              destination.push(data);
54509            });
54510          };
54511  
54512          return Stream;
54513        }();
54514        /**
54515         * @file async-stream.js
54516         */
54517  
54518        /**
54519         * A wrapper around the Stream class to use setTiemout
54520         * and run stream "jobs" Asynchronously
54521         *
54522         * @class AsyncStream
54523         * @extends Stream
54524         */
54525  
54526  
54527        var AsyncStream$$1 = function (_Stream) {
54528          inherits(AsyncStream$$1, _Stream);
54529  
54530          function AsyncStream$$1() {
54531            classCallCheck(this, AsyncStream$$1);
54532  
54533            var _this = possibleConstructorReturn(this, _Stream.call(this, Stream));
54534  
54535            _this.jobs = [];
54536            _this.delay = 1;
54537            _this.timeout_ = null;
54538            return _this;
54539          }
54540          /**
54541           * process an async job
54542           *
54543           * @private
54544           */
54545  
54546  
54547          AsyncStream$$1.prototype.processJob_ = function processJob_() {
54548            this.jobs.shift()();
54549  
54550            if (this.jobs.length) {
54551              this.timeout_ = setTimeout(this.processJob_.bind(this), this.delay);
54552            } else {
54553              this.timeout_ = null;
54554            }
54555          };
54556          /**
54557           * push a job into the stream
54558           *
54559           * @param {Function} job the job to push into the stream
54560           */
54561  
54562  
54563          AsyncStream$$1.prototype.push = function push(job) {
54564            this.jobs.push(job);
54565  
54566            if (!this.timeout_) {
54567              this.timeout_ = setTimeout(this.processJob_.bind(this), this.delay);
54568            }
54569          };
54570  
54571          return AsyncStream$$1;
54572        }(Stream);
54573        /**
54574         * @file decrypter.js
54575         *
54576         * An asynchronous implementation of AES-128 CBC decryption with
54577         * PKCS#7 padding.
54578         */
54579  
54580        /**
54581         * Convert network-order (big-endian) bytes into their little-endian
54582         * representation.
54583         */
54584  
54585  
54586        var ntoh = function ntoh(word) {
54587          return word << 24 | (word & 0xff00) << 8 | (word & 0xff0000) >> 8 | word >>> 24;
54588        };
54589        /**
54590         * Decrypt bytes using AES-128 with CBC and PKCS#7 padding.
54591         *
54592         * @param {Uint8Array} encrypted the encrypted bytes
54593         * @param {Uint32Array} key the bytes of the decryption key
54594         * @param {Uint32Array} initVector the initialization vector (IV) to
54595         * use for the first round of CBC.
54596         * @return {Uint8Array} the decrypted bytes
54597         *
54598         * @see http://en.wikipedia.org/wiki/Advanced_Encryption_Standard
54599         * @see http://en.wikipedia.org/wiki/Block_cipher_mode_of_operation#Cipher_Block_Chaining_.28CBC.29
54600         * @see https://tools.ietf.org/html/rfc2315
54601         */
54602  
54603  
54604        var decrypt$$1 = function decrypt$$1(encrypted, key, initVector) {
54605          // word-level access to the encrypted bytes
54606          var encrypted32 = new Int32Array(encrypted.buffer, encrypted.byteOffset, encrypted.byteLength >> 2);
54607          var decipher = new AES(Array.prototype.slice.call(key)); // byte and word-level access for the decrypted output
54608  
54609          var decrypted = new Uint8Array(encrypted.byteLength);
54610          var decrypted32 = new Int32Array(decrypted.buffer); // temporary variables for working with the IV, encrypted, and
54611          // decrypted data
54612  
54613          var init0 = void 0;
54614          var init1 = void 0;
54615          var init2 = void 0;
54616          var init3 = void 0;
54617          var encrypted0 = void 0;
54618          var encrypted1 = void 0;
54619          var encrypted2 = void 0;
54620          var encrypted3 = void 0; // iteration variable
54621  
54622          var wordIx = void 0; // pull out the words of the IV to ensure we don't modify the
54623          // passed-in reference and easier access
54624  
54625          init0 = initVector[0];
54626          init1 = initVector[1];
54627          init2 = initVector[2];
54628          init3 = initVector[3]; // decrypt four word sequences, applying cipher-block chaining (CBC)
54629          // to each decrypted block
54630  
54631          for (wordIx = 0; wordIx < encrypted32.length; wordIx += 4) {
54632            // convert big-endian (network order) words into little-endian
54633            // (javascript order)
54634            encrypted0 = ntoh(encrypted32[wordIx]);
54635            encrypted1 = ntoh(encrypted32[wordIx + 1]);
54636            encrypted2 = ntoh(encrypted32[wordIx + 2]);
54637            encrypted3 = ntoh(encrypted32[wordIx + 3]); // decrypt the block
54638  
54639            decipher.decrypt(encrypted0, encrypted1, encrypted2, encrypted3, decrypted32, wordIx); // XOR with the IV, and restore network byte-order to obtain the
54640            // plaintext
54641  
54642            decrypted32[wordIx] = ntoh(decrypted32[wordIx] ^ init0);
54643            decrypted32[wordIx + 1] = ntoh(decrypted32[wordIx + 1] ^ init1);
54644            decrypted32[wordIx + 2] = ntoh(decrypted32[wordIx + 2] ^ init2);
54645            decrypted32[wordIx + 3] = ntoh(decrypted32[wordIx + 3] ^ init3); // setup the IV for the next round
54646  
54647            init0 = encrypted0;
54648            init1 = encrypted1;
54649            init2 = encrypted2;
54650            init3 = encrypted3;
54651          }
54652  
54653          return decrypted;
54654        };
54655        /**
54656         * The `Decrypter` class that manages decryption of AES
54657         * data through `AsyncStream` objects and the `decrypt`
54658         * function
54659         *
54660         * @param {Uint8Array} encrypted the encrypted bytes
54661         * @param {Uint32Array} key the bytes of the decryption key
54662         * @param {Uint32Array} initVector the initialization vector (IV) to
54663         * @param {Function} done the function to run when done
54664         * @class Decrypter
54665         */
54666  
54667  
54668        var Decrypter$$1 = function () {
54669          function Decrypter$$1(encrypted, key, initVector, done) {
54670            classCallCheck(this, Decrypter$$1);
54671            var step = Decrypter$$1.STEP;
54672            var encrypted32 = new Int32Array(encrypted.buffer);
54673            var decrypted = new Uint8Array(encrypted.byteLength);
54674            var i = 0;
54675            this.asyncStream_ = new AsyncStream$$1(); // split up the encryption job and do the individual chunks asynchronously
54676  
54677            this.asyncStream_.push(this.decryptChunk_(encrypted32.subarray(i, i + step), key, initVector, decrypted));
54678  
54679            for (i = step; i < encrypted32.length; i += step) {
54680              initVector = new Uint32Array([ntoh(encrypted32[i - 4]), ntoh(encrypted32[i - 3]), ntoh(encrypted32[i - 2]), ntoh(encrypted32[i - 1])]);
54681              this.asyncStream_.push(this.decryptChunk_(encrypted32.subarray(i, i + step), key, initVector, decrypted));
54682            } // invoke the done() callback when everything is finished
54683  
54684  
54685            this.asyncStream_.push(function () {
54686              // remove pkcs#7 padding from the decrypted bytes
54687              done(null, unpad(decrypted));
54688            });
54689          }
54690          /**
54691           * a getter for step the maximum number of bytes to process at one time
54692           *
54693           * @return {Number} the value of step 32000
54694           */
54695  
54696          /**
54697           * @private
54698           */
54699  
54700  
54701          Decrypter$$1.prototype.decryptChunk_ = function decryptChunk_(encrypted, key, initVector, decrypted) {
54702            return function () {
54703              var bytes = decrypt$$1(encrypted, key, initVector);
54704              decrypted.set(bytes, encrypted.byteOffset);
54705            };
54706          };
54707  
54708          createClass(Decrypter$$1, null, [{
54709            key: 'STEP',
54710            get: function get$$1() {
54711              // 4 * 8000;
54712              return 32000;
54713            }
54714          }]);
54715          return Decrypter$$1;
54716        }();
54717        /**
54718         * @file bin-utils.js
54719         */
54720  
54721        /**
54722         * Creates an object for sending to a web worker modifying properties that are TypedArrays
54723         * into a new object with seperated properties for the buffer, byteOffset, and byteLength.
54724         *
54725         * @param {Object} message
54726         *        Object of properties and values to send to the web worker
54727         * @return {Object}
54728         *         Modified message with TypedArray values expanded
54729         * @function createTransferableMessage
54730         */
54731  
54732  
54733        var createTransferableMessage = function createTransferableMessage(message) {
54734          var transferable = {};
54735          Object.keys(message).forEach(function (key) {
54736            var value = message[key];
54737  
54738            if (ArrayBuffer.isView(value)) {
54739              transferable[key] = {
54740                bytes: value.buffer,
54741                byteOffset: value.byteOffset,
54742                byteLength: value.byteLength
54743              };
54744            } else {
54745              transferable[key] = value;
54746            }
54747          });
54748          return transferable;
54749        };
54750        /**
54751         * Our web worker interface so that things can talk to aes-decrypter
54752         * that will be running in a web worker. the scope is passed to this by
54753         * webworkify.
54754         *
54755         * @param {Object} self
54756         *        the scope for the web worker
54757         */
54758  
54759  
54760        var DecrypterWorker = function DecrypterWorker(self) {
54761          self.onmessage = function (event) {
54762            var data = event.data;
54763            var encrypted = new Uint8Array(data.encrypted.bytes, data.encrypted.byteOffset, data.encrypted.byteLength);
54764            var key = new Uint32Array(data.key.bytes, data.key.byteOffset, data.key.byteLength / 4);
54765            var iv = new Uint32Array(data.iv.bytes, data.iv.byteOffset, data.iv.byteLength / 4);
54766            /* eslint-disable no-new, handle-callback-err */
54767  
54768            new Decrypter$$1(encrypted, key, iv, function (err, bytes) {
54769              self.postMessage(createTransferableMessage({
54770                source: data.source,
54771                decrypted: bytes
54772              }), [bytes.buffer]);
54773            });
54774            /* eslint-enable */
54775          };
54776        };
54777  
54778        var decrypterWorker = new DecrypterWorker(self);
54779        return decrypterWorker;
54780      }();
54781    });
54782    /**
54783     * Convert the properties of an HLS track into an audioTrackKind.
54784     *
54785     * @private
54786     */
54787  
54788    var audioTrackKind_ = function audioTrackKind_(properties) {
54789      var kind = properties["default"] ? 'main' : 'alternative';
54790  
54791      if (properties.characteristics && properties.characteristics.indexOf('public.accessibility.describes-video') >= 0) {
54792        kind = 'main-desc';
54793      }
54794  
54795      return kind;
54796    };
54797    /**
54798     * Pause provided segment loader and playlist loader if active
54799     *
54800     * @param {SegmentLoader} segmentLoader
54801     *        SegmentLoader to pause
54802     * @param {Object} mediaType
54803     *        Active media type
54804     * @function stopLoaders
54805     */
54806  
54807  
54808    var stopLoaders = function stopLoaders(segmentLoader, mediaType) {
54809      segmentLoader.abort();
54810      segmentLoader.pause();
54811  
54812      if (mediaType && mediaType.activePlaylistLoader) {
54813        mediaType.activePlaylistLoader.pause();
54814        mediaType.activePlaylistLoader = null;
54815      }
54816    };
54817    /**
54818     * Start loading provided segment loader and playlist loader
54819     *
54820     * @param {PlaylistLoader} playlistLoader
54821     *        PlaylistLoader to start loading
54822     * @param {Object} mediaType
54823     *        Active media type
54824     * @function startLoaders
54825     */
54826  
54827  
54828    var startLoaders = function startLoaders(playlistLoader, mediaType) {
54829      // Segment loader will be started after `loadedmetadata` or `loadedplaylist` from the
54830      // playlist loader
54831      mediaType.activePlaylistLoader = playlistLoader;
54832      playlistLoader.load();
54833    };
54834    /**
54835     * Returns a function to be called when the media group changes. It performs a
54836     * non-destructive (preserve the buffer) resync of the SegmentLoader. This is because a
54837     * change of group is merely a rendition switch of the same content at another encoding,
54838     * rather than a change of content, such as switching audio from English to Spanish.
54839     *
54840     * @param {String} type
54841     *        MediaGroup type
54842     * @param {Object} settings
54843     *        Object containing required information for media groups
54844     * @return {Function}
54845     *         Handler for a non-destructive resync of SegmentLoader when the active media
54846     *         group changes.
54847     * @function onGroupChanged
54848     */
54849  
54850  
54851    var onGroupChanged = function onGroupChanged(type, settings) {
54852      return function () {
54853        var _settings$segmentLoad = settings.segmentLoaders,
54854            segmentLoader = _settings$segmentLoad[type],
54855            mainSegmentLoader = _settings$segmentLoad.main,
54856            mediaType = settings.mediaTypes[type];
54857        var activeTrack = mediaType.activeTrack();
54858        var activeGroup = mediaType.activeGroup(activeTrack);
54859        var previousActiveLoader = mediaType.activePlaylistLoader;
54860        stopLoaders(segmentLoader, mediaType);
54861  
54862        if (!activeGroup) {
54863          // there is no group active
54864          return;
54865        }
54866  
54867        if (!activeGroup.playlistLoader) {
54868          if (previousActiveLoader) {
54869            // The previous group had a playlist loader but the new active group does not
54870            // this means we are switching from demuxed to muxed audio. In this case we want to
54871            // do a destructive reset of the main segment loader and not restart the audio
54872            // loaders.
54873            mainSegmentLoader.resetEverything();
54874          }
54875  
54876          return;
54877        } // Non-destructive resync
54878  
54879  
54880        segmentLoader.resyncLoader();
54881        startLoaders(activeGroup.playlistLoader, mediaType);
54882      };
54883    };
54884    /**
54885     * Returns a function to be called when the media track changes. It performs a
54886     * destructive reset of the SegmentLoader to ensure we start loading as close to
54887     * currentTime as possible.
54888     *
54889     * @param {String} type
54890     *        MediaGroup type
54891     * @param {Object} settings
54892     *        Object containing required information for media groups
54893     * @return {Function}
54894     *         Handler for a destructive reset of SegmentLoader when the active media
54895     *         track changes.
54896     * @function onTrackChanged
54897     */
54898  
54899  
54900    var onTrackChanged = function onTrackChanged(type, settings) {
54901      return function () {
54902        var _settings$segmentLoad2 = settings.segmentLoaders,
54903            segmentLoader = _settings$segmentLoad2[type],
54904            mainSegmentLoader = _settings$segmentLoad2.main,
54905            mediaType = settings.mediaTypes[type];
54906        var activeTrack = mediaType.activeTrack();
54907        var activeGroup = mediaType.activeGroup(activeTrack);
54908        var previousActiveLoader = mediaType.activePlaylistLoader;
54909        stopLoaders(segmentLoader, mediaType);
54910  
54911        if (!activeGroup) {
54912          // there is no group active so we do not want to restart loaders
54913          return;
54914        }
54915  
54916        if (!activeGroup.playlistLoader) {
54917          // when switching from demuxed audio/video to muxed audio/video (noted by no playlist
54918          // loader for the audio group), we want to do a destructive reset of the main segment
54919          // loader and not restart the audio loaders
54920          mainSegmentLoader.resetEverything();
54921          return;
54922        }
54923  
54924        if (previousActiveLoader === activeGroup.playlistLoader) {
54925          // Nothing has actually changed. This can happen because track change events can fire
54926          // multiple times for a "single" change. One for enabling the new active track, and
54927          // one for disabling the track that was active
54928          startLoaders(activeGroup.playlistLoader, mediaType);
54929          return;
54930        }
54931  
54932        if (segmentLoader.track) {
54933          // For WebVTT, set the new text track in the segmentloader
54934          segmentLoader.track(activeTrack);
54935        } // destructive reset
54936  
54937  
54938        segmentLoader.resetEverything();
54939        startLoaders(activeGroup.playlistLoader, mediaType);
54940      };
54941    };
54942  
54943    var onError = {
54944      /**
54945       * Returns a function to be called when a SegmentLoader or PlaylistLoader encounters
54946       * an error.
54947       *
54948       * @param {String} type
54949       *        MediaGroup type
54950       * @param {Object} settings
54951       *        Object containing required information for media groups
54952       * @return {Function}
54953       *         Error handler. Logs warning (or error if the playlist is blacklisted) to
54954       *         console and switches back to default audio track.
54955       * @function onError.AUDIO
54956       */
54957      AUDIO: function AUDIO(type, settings) {
54958        return function () {
54959          var segmentLoader = settings.segmentLoaders[type],
54960              mediaType = settings.mediaTypes[type],
54961              blacklistCurrentPlaylist = settings.blacklistCurrentPlaylist;
54962          stopLoaders(segmentLoader, mediaType); // switch back to default audio track
54963  
54964          var activeTrack = mediaType.activeTrack();
54965          var activeGroup = mediaType.activeGroup();
54966          var id = (activeGroup.filter(function (group) {
54967            return group["default"];
54968          })[0] || activeGroup[0]).id;
54969          var defaultTrack = mediaType.tracks[id];
54970  
54971          if (activeTrack === defaultTrack) {
54972            // Default track encountered an error. All we can do now is blacklist the current
54973            // rendition and hope another will switch audio groups
54974            blacklistCurrentPlaylist({
54975              message: 'Problem encountered loading the default audio track.'
54976            });
54977            return;
54978          }
54979  
54980          videojs$1.log.warn('Problem encountered loading the alternate audio track.' + 'Switching back to default.');
54981  
54982          for (var trackId in mediaType.tracks) {
54983            mediaType.tracks[trackId].enabled = mediaType.tracks[trackId] === defaultTrack;
54984          }
54985  
54986          mediaType.onTrackChanged();
54987        };
54988      },
54989  
54990      /**
54991       * Returns a function to be called when a SegmentLoader or PlaylistLoader encounters
54992       * an error.
54993       *
54994       * @param {String} type
54995       *        MediaGroup type
54996       * @param {Object} settings
54997       *        Object containing required information for media groups
54998       * @return {Function}
54999       *         Error handler. Logs warning to console and disables the active subtitle track
55000       * @function onError.SUBTITLES
55001       */
55002      SUBTITLES: function SUBTITLES(type, settings) {
55003        return function () {
55004          var segmentLoader = settings.segmentLoaders[type],
55005              mediaType = settings.mediaTypes[type];
55006          videojs$1.log.warn('Problem encountered loading the subtitle track.' + 'Disabling subtitle track.');
55007          stopLoaders(segmentLoader, mediaType);
55008          var track = mediaType.activeTrack();
55009  
55010          if (track) {
55011            track.mode = 'disabled';
55012          }
55013  
55014          mediaType.onTrackChanged();
55015        };
55016      }
55017    };
55018    var setupListeners = {
55019      /**
55020       * Setup event listeners for audio playlist loader
55021       *
55022       * @param {String} type
55023       *        MediaGroup type
55024       * @param {PlaylistLoader|null} playlistLoader
55025       *        PlaylistLoader to register listeners on
55026       * @param {Object} settings
55027       *        Object containing required information for media groups
55028       * @function setupListeners.AUDIO
55029       */
55030      AUDIO: function AUDIO(type, playlistLoader, settings) {
55031        if (!playlistLoader) {
55032          // no playlist loader means audio will be muxed with the video
55033          return;
55034        }
55035  
55036        var tech = settings.tech,
55037            requestOptions = settings.requestOptions,
55038            segmentLoader = settings.segmentLoaders[type];
55039        playlistLoader.on('loadedmetadata', function () {
55040          var media = playlistLoader.media();
55041          segmentLoader.playlist(media, requestOptions); // if the video is already playing, or if this isn't a live video and preload
55042          // permits, start downloading segments
55043  
55044          if (!tech.paused() || media.endList && tech.preload() !== 'none') {
55045            segmentLoader.load();
55046          }
55047        });
55048        playlistLoader.on('loadedplaylist', function () {
55049          segmentLoader.playlist(playlistLoader.media(), requestOptions); // If the player isn't paused, ensure that the segment loader is running
55050  
55051          if (!tech.paused()) {
55052            segmentLoader.load();
55053          }
55054        });
55055        playlistLoader.on('error', onError[type](type, settings));
55056      },
55057  
55058      /**
55059       * Setup event listeners for subtitle playlist loader
55060       *
55061       * @param {String} type
55062       *        MediaGroup type
55063       * @param {PlaylistLoader|null} playlistLoader
55064       *        PlaylistLoader to register listeners on
55065       * @param {Object} settings
55066       *        Object containing required information for media groups
55067       * @function setupListeners.SUBTITLES
55068       */
55069      SUBTITLES: function SUBTITLES(type, playlistLoader, settings) {
55070        var tech = settings.tech,
55071            requestOptions = settings.requestOptions,
55072            segmentLoader = settings.segmentLoaders[type],
55073            mediaType = settings.mediaTypes[type];
55074        playlistLoader.on('loadedmetadata', function () {
55075          var media = playlistLoader.media();
55076          segmentLoader.playlist(media, requestOptions);
55077          segmentLoader.track(mediaType.activeTrack()); // if the video is already playing, or if this isn't a live video and preload
55078          // permits, start downloading segments
55079  
55080          if (!tech.paused() || media.endList && tech.preload() !== 'none') {
55081            segmentLoader.load();
55082          }
55083        });
55084        playlistLoader.on('loadedplaylist', function () {
55085          segmentLoader.playlist(playlistLoader.media(), requestOptions); // If the player isn't paused, ensure that the segment loader is running
55086  
55087          if (!tech.paused()) {
55088            segmentLoader.load();
55089          }
55090        });
55091        playlistLoader.on('error', onError[type](type, settings));
55092      }
55093    };
55094    var initialize = {
55095      /**
55096       * Setup PlaylistLoaders and AudioTracks for the audio groups
55097       *
55098       * @param {String} type
55099       *        MediaGroup type
55100       * @param {Object} settings
55101       *        Object containing required information for media groups
55102       * @function initialize.AUDIO
55103       */
55104      'AUDIO': function AUDIO(type, settings) {
55105        var hls = settings.hls,
55106            sourceType = settings.sourceType,
55107            segmentLoader = settings.segmentLoaders[type],
55108            requestOptions = settings.requestOptions,
55109            mediaGroups = settings.master.mediaGroups,
55110            _settings$mediaTypes$ = settings.mediaTypes[type],
55111            groups = _settings$mediaTypes$.groups,
55112            tracks = _settings$mediaTypes$.tracks,
55113            masterPlaylistLoader = settings.masterPlaylistLoader; // force a default if we have none
55114  
55115        if (!mediaGroups[type] || Object.keys(mediaGroups[type]).length === 0) {
55116          mediaGroups[type] = {
55117            main: {
55118              "default": {
55119                "default": true
55120              }
55121            }
55122          };
55123        }
55124  
55125        for (var groupId in mediaGroups[type]) {
55126          if (!groups[groupId]) {
55127            groups[groupId] = [];
55128          } // List of playlists that have an AUDIO attribute value matching the current
55129          // group ID
55130  
55131  
55132          for (var variantLabel in mediaGroups[type][groupId]) {
55133            var properties = mediaGroups[type][groupId][variantLabel];
55134            var playlistLoader = void 0;
55135  
55136            if (properties.resolvedUri) {
55137              playlistLoader = new PlaylistLoader(properties.resolvedUri, hls, requestOptions);
55138            } else if (properties.playlists && sourceType === 'dash') {
55139              playlistLoader = new DashPlaylistLoader(properties.playlists[0], hls, requestOptions, masterPlaylistLoader);
55140            } else {
55141              // no resolvedUri means the audio is muxed with the video when using this
55142              // audio track
55143              playlistLoader = null;
55144            }
55145  
55146            properties = videojs$1.mergeOptions({
55147              id: variantLabel,
55148              playlistLoader: playlistLoader
55149            }, properties);
55150            setupListeners[type](type, properties.playlistLoader, settings);
55151            groups[groupId].push(properties);
55152  
55153            if (typeof tracks[variantLabel] === 'undefined') {
55154              var track = new videojs$1.AudioTrack({
55155                id: variantLabel,
55156                kind: audioTrackKind_(properties),
55157                enabled: false,
55158                language: properties.language,
55159                "default": properties["default"],
55160                label: variantLabel
55161              });
55162              tracks[variantLabel] = track;
55163            }
55164          }
55165        } // setup single error event handler for the segment loader
55166  
55167  
55168        segmentLoader.on('error', onError[type](type, settings));
55169      },
55170  
55171      /**
55172       * Setup PlaylistLoaders and TextTracks for the subtitle groups
55173       *
55174       * @param {String} type
55175       *        MediaGroup type
55176       * @param {Object} settings
55177       *        Object containing required information for media groups
55178       * @function initialize.SUBTITLES
55179       */
55180      'SUBTITLES': function SUBTITLES(type, settings) {
55181        var tech = settings.tech,
55182            hls = settings.hls,
55183            sourceType = settings.sourceType,
55184            segmentLoader = settings.segmentLoaders[type],
55185            requestOptions = settings.requestOptions,
55186            mediaGroups = settings.master.mediaGroups,
55187            _settings$mediaTypes$2 = settings.mediaTypes[type],
55188            groups = _settings$mediaTypes$2.groups,
55189            tracks = _settings$mediaTypes$2.tracks,
55190            masterPlaylistLoader = settings.masterPlaylistLoader;
55191  
55192        for (var groupId in mediaGroups[type]) {
55193          if (!groups[groupId]) {
55194            groups[groupId] = [];
55195          }
55196  
55197          for (var variantLabel in mediaGroups[type][groupId]) {
55198            if (mediaGroups[type][groupId][variantLabel].forced) {
55199              // Subtitle playlists with the forced attribute are not selectable in Safari.
55200              // According to Apple's HLS Authoring Specification:
55201              //   If content has forced subtitles and regular subtitles in a given language,
55202              //   the regular subtitles track in that language MUST contain both the forced
55203              //   subtitles and the regular subtitles for that language.
55204              // Because of this requirement and that Safari does not add forced subtitles,
55205              // forced subtitles are skipped here to maintain consistent experience across
55206              // all platforms
55207              continue;
55208            }
55209  
55210            var properties = mediaGroups[type][groupId][variantLabel];
55211            var playlistLoader = void 0;
55212  
55213            if (sourceType === 'hls') {
55214              playlistLoader = new PlaylistLoader(properties.resolvedUri, hls, requestOptions);
55215            } else if (sourceType === 'dash') {
55216              playlistLoader = new DashPlaylistLoader(properties.playlists[0], hls, requestOptions, masterPlaylistLoader);
55217            }
55218  
55219            properties = videojs$1.mergeOptions({
55220              id: variantLabel,
55221              playlistLoader: playlistLoader
55222            }, properties);
55223            setupListeners[type](type, properties.playlistLoader, settings);
55224            groups[groupId].push(properties);
55225  
55226            if (typeof tracks[variantLabel] === 'undefined') {
55227              var track = tech.addRemoteTextTrack({
55228                id: variantLabel,
55229                kind: 'subtitles',
55230                "default": properties["default"] && properties.autoselect,
55231                language: properties.language,
55232                label: variantLabel
55233              }, false).track;
55234              tracks[variantLabel] = track;
55235            }
55236          }
55237        } // setup single error event handler for the segment loader
55238  
55239  
55240        segmentLoader.on('error', onError[type](type, settings));
55241      },
55242  
55243      /**
55244       * Setup TextTracks for the closed-caption groups
55245       *
55246       * @param {String} type
55247       *        MediaGroup type
55248       * @param {Object} settings
55249       *        Object containing required information for media groups
55250       * @function initialize['CLOSED-CAPTIONS']
55251       */
55252      'CLOSED-CAPTIONS': function CLOSEDCAPTIONS(type, settings) {
55253        var tech = settings.tech,
55254            mediaGroups = settings.master.mediaGroups,
55255            _settings$mediaTypes$3 = settings.mediaTypes[type],
55256            groups = _settings$mediaTypes$3.groups,
55257            tracks = _settings$mediaTypes$3.tracks;
55258  
55259        for (var groupId in mediaGroups[type]) {
55260          if (!groups[groupId]) {
55261            groups[groupId] = [];
55262          }
55263  
55264          for (var variantLabel in mediaGroups[type][groupId]) {
55265            var properties = mediaGroups[type][groupId][variantLabel]; // We only support CEA608 captions for now, so ignore anything that
55266            // doesn't use a CCx INSTREAM-ID
55267  
55268            if (!properties.instreamId.match(/CC\d/)) {
55269              continue;
55270            } // No PlaylistLoader is required for Closed-Captions because the captions are
55271            // embedded within the video stream
55272  
55273  
55274            groups[groupId].push(videojs$1.mergeOptions({
55275              id: variantLabel
55276            }, properties));
55277  
55278            if (typeof tracks[variantLabel] === 'undefined') {
55279              var track = tech.addRemoteTextTrack({
55280                id: properties.instreamId,
55281                kind: 'captions',
55282                "default": properties["default"] && properties.autoselect,
55283                language: properties.language,
55284                label: variantLabel
55285              }, false).track;
55286              tracks[variantLabel] = track;
55287            }
55288          }
55289        }
55290      }
55291    };
55292    /**
55293     * Returns a function used to get the active group of the provided type
55294     *
55295     * @param {String} type
55296     *        MediaGroup type
55297     * @param {Object} settings
55298     *        Object containing required information for media groups
55299     * @return {Function}
55300     *         Function that returns the active media group for the provided type. Takes an
55301     *         optional parameter {TextTrack} track. If no track is provided, a list of all
55302     *         variants in the group, otherwise the variant corresponding to the provided
55303     *         track is returned.
55304     * @function activeGroup
55305     */
55306  
55307    var activeGroup = function activeGroup(type, settings) {
55308      return function (track) {
55309        var masterPlaylistLoader = settings.masterPlaylistLoader,
55310            groups = settings.mediaTypes[type].groups;
55311        var media = masterPlaylistLoader.media();
55312  
55313        if (!media) {
55314          return null;
55315        }
55316  
55317        var variants = null;
55318  
55319        if (media.attributes[type]) {
55320          variants = groups[media.attributes[type]];
55321        }
55322  
55323        variants = variants || groups.main;
55324  
55325        if (typeof track === 'undefined') {
55326          return variants;
55327        }
55328  
55329        if (track === null) {
55330          // An active track was specified so a corresponding group is expected. track === null
55331          // means no track is currently active so there is no corresponding group
55332          return null;
55333        }
55334  
55335        return variants.filter(function (props) {
55336          return props.id === track.id;
55337        })[0] || null;
55338      };
55339    };
55340  
55341    var activeTrack = {
55342      /**
55343       * Returns a function used to get the active track of type provided
55344       *
55345       * @param {String} type
55346       *        MediaGroup type
55347       * @param {Object} settings
55348       *        Object containing required information for media groups
55349       * @return {Function}
55350       *         Function that returns the active media track for the provided type. Returns
55351       *         null if no track is active
55352       * @function activeTrack.AUDIO
55353       */
55354      AUDIO: function AUDIO(type, settings) {
55355        return function () {
55356          var tracks = settings.mediaTypes[type].tracks;
55357  
55358          for (var id in tracks) {
55359            if (tracks[id].enabled) {
55360              return tracks[id];
55361            }
55362          }
55363  
55364          return null;
55365        };
55366      },
55367  
55368      /**
55369       * Returns a function used to get the active track of type provided
55370       *
55371       * @param {String} type
55372       *        MediaGroup type
55373       * @param {Object} settings
55374       *        Object containing required information for media groups
55375       * @return {Function}
55376       *         Function that returns the active media track for the provided type. Returns
55377       *         null if no track is active
55378       * @function activeTrack.SUBTITLES
55379       */
55380      SUBTITLES: function SUBTITLES(type, settings) {
55381        return function () {
55382          var tracks = settings.mediaTypes[type].tracks;
55383  
55384          for (var id in tracks) {
55385            if (tracks[id].mode === 'showing' || tracks[id].mode === 'hidden') {
55386              return tracks[id];
55387            }
55388          }
55389  
55390          return null;
55391        };
55392      }
55393    };
55394    /**
55395     * Setup PlaylistLoaders and Tracks for media groups (Audio, Subtitles,
55396     * Closed-Captions) specified in the master manifest.
55397     *
55398     * @param {Object} settings
55399     *        Object containing required information for setting up the media groups
55400     * @param {SegmentLoader} settings.segmentLoaders.AUDIO
55401     *        Audio segment loader
55402     * @param {SegmentLoader} settings.segmentLoaders.SUBTITLES
55403     *        Subtitle segment loader
55404     * @param {SegmentLoader} settings.segmentLoaders.main
55405     *        Main segment loader
55406     * @param {Tech} settings.tech
55407     *        The tech of the player
55408     * @param {Object} settings.requestOptions
55409     *        XHR request options used by the segment loaders
55410     * @param {PlaylistLoader} settings.masterPlaylistLoader
55411     *        PlaylistLoader for the master source
55412     * @param {HlsHandler} settings.hls
55413     *        HLS SourceHandler
55414     * @param {Object} settings.master
55415     *        The parsed master manifest
55416     * @param {Object} settings.mediaTypes
55417     *        Object to store the loaders, tracks, and utility methods for each media type
55418     * @param {Function} settings.blacklistCurrentPlaylist
55419     *        Blacklists the current rendition and forces a rendition switch.
55420     * @function setupMediaGroups
55421     */
55422  
55423    var setupMediaGroups = function setupMediaGroups(settings) {
55424      ['AUDIO', 'SUBTITLES', 'CLOSED-CAPTIONS'].forEach(function (type) {
55425        initialize[type](type, settings);
55426      });
55427      var mediaTypes = settings.mediaTypes,
55428          masterPlaylistLoader = settings.masterPlaylistLoader,
55429          tech = settings.tech,
55430          hls = settings.hls; // setup active group and track getters and change event handlers
55431  
55432      ['AUDIO', 'SUBTITLES'].forEach(function (type) {
55433        mediaTypes[type].activeGroup = activeGroup(type, settings);
55434        mediaTypes[type].activeTrack = activeTrack[type](type, settings);
55435        mediaTypes[type].onGroupChanged = onGroupChanged(type, settings);
55436        mediaTypes[type].onTrackChanged = onTrackChanged(type, settings);
55437      }); // DO NOT enable the default subtitle or caption track.
55438      // DO enable the default audio track
55439  
55440      var audioGroup = mediaTypes.AUDIO.activeGroup();
55441      var groupId = (audioGroup.filter(function (group) {
55442        return group["default"];
55443      })[0] || audioGroup[0]).id;
55444      mediaTypes.AUDIO.tracks[groupId].enabled = true;
55445      mediaTypes.AUDIO.onTrackChanged();
55446      masterPlaylistLoader.on('mediachange', function () {
55447        ['AUDIO', 'SUBTITLES'].forEach(function (type) {
55448          return mediaTypes[type].onGroupChanged();
55449        });
55450      }); // custom audio track change event handler for usage event
55451  
55452      var onAudioTrackChanged = function onAudioTrackChanged() {
55453        mediaTypes.AUDIO.onTrackChanged();
55454        tech.trigger({
55455          type: 'usage',
55456          name: 'hls-audio-change'
55457        });
55458      };
55459  
55460      tech.audioTracks().addEventListener('change', onAudioTrackChanged);
55461      tech.remoteTextTracks().addEventListener('change', mediaTypes.SUBTITLES.onTrackChanged);
55462      hls.on('dispose', function () {
55463        tech.audioTracks().removeEventListener('change', onAudioTrackChanged);
55464        tech.remoteTextTracks().removeEventListener('change', mediaTypes.SUBTITLES.onTrackChanged);
55465      }); // clear existing audio tracks and add the ones we just created
55466  
55467      tech.clearTracks('audio');
55468  
55469      for (var id in mediaTypes.AUDIO.tracks) {
55470        tech.audioTracks().addTrack(mediaTypes.AUDIO.tracks[id]);
55471      }
55472    };
55473    /**
55474     * Creates skeleton object used to store the loaders, tracks, and utility methods for each
55475     * media type
55476     *
55477     * @return {Object}
55478     *         Object to store the loaders, tracks, and utility methods for each media type
55479     * @function createMediaTypes
55480     */
55481  
55482  
55483    var createMediaTypes = function createMediaTypes() {
55484      var mediaTypes = {};
55485      ['AUDIO', 'SUBTITLES', 'CLOSED-CAPTIONS'].forEach(function (type) {
55486        mediaTypes[type] = {
55487          groups: {},
55488          tracks: {},
55489          activePlaylistLoader: null,
55490          activeGroup: noop$1,
55491          activeTrack: noop$1,
55492          onGroupChanged: noop$1,
55493          onTrackChanged: noop$1
55494        };
55495      });
55496      return mediaTypes;
55497    };
55498    /**
55499     * @file master-playlist-controller.js
55500     */
55501  
55502  
55503    var ABORT_EARLY_BLACKLIST_SECONDS = 60 * 2;
55504    var Hls = void 0; // SegmentLoader stats that need to have each loader's
55505    // values summed to calculate the final value
55506  
55507    var loaderStats = ['mediaRequests', 'mediaRequestsAborted', 'mediaRequestsTimedout', 'mediaRequestsErrored', 'mediaTransferDuration', 'mediaBytesTransferred'];
55508  
55509    var sumLoaderStat = function sumLoaderStat(stat) {
55510      return this.audioSegmentLoader_[stat] + this.mainSegmentLoader_[stat];
55511    };
55512  
55513    var shouldSwitchToMedia = function shouldSwitchToMedia(_ref) {
55514      var currentPlaylist = _ref.currentPlaylist,
55515          nextPlaylist = _ref.nextPlaylist,
55516          forwardBuffer = _ref.forwardBuffer,
55517          bufferLowWaterLine = _ref.bufferLowWaterLine,
55518          duration$$1 = _ref.duration,
55519          log = _ref.log; // we have no other playlist to switch to
55520  
55521      if (!nextPlaylist) {
55522        videojs$1.log.warn('We received no playlist to switch to. Please check your stream.');
55523        return false;
55524      } // If the playlist is live, then we want to not take low water line into account.
55525      // This is because in LIVE, the player plays 3 segments from the end of the
55526      // playlist, and if `BUFFER_LOW_WATER_LINE` is greater than the duration availble
55527      // in those segments, a viewer will never experience a rendition upswitch.
55528  
55529  
55530      if (!currentPlaylist.endList) {
55531        return true;
55532      } // For the same reason as LIVE, we ignore the low water line when the VOD
55533      // duration is below the max potential low water line
55534  
55535  
55536      if (duration$$1 < Config.MAX_BUFFER_LOW_WATER_LINE) {
55537        return true;
55538      } // we want to switch down to lower resolutions quickly to continue playback, but
55539  
55540  
55541      if (nextPlaylist.attributes.BANDWIDTH < currentPlaylist.attributes.BANDWIDTH) {
55542        return true;
55543      } // ensure we have some buffer before we switch up to prevent us running out of
55544      // buffer while loading a higher rendition.
55545  
55546  
55547      if (forwardBuffer >= bufferLowWaterLine) {
55548        return true;
55549      }
55550  
55551      return false;
55552    };
55553    /**
55554     * the master playlist controller controller all interactons
55555     * between playlists and segmentloaders. At this time this mainly
55556     * involves a master playlist and a series of audio playlists
55557     * if they are available
55558     *
55559     * @class MasterPlaylistController
55560     * @extends videojs.EventTarget
55561     */
55562  
55563  
55564    var MasterPlaylistController = function (_videojs$EventTarget) {
55565      inherits$2(MasterPlaylistController, _videojs$EventTarget);
55566  
55567      function MasterPlaylistController(options) {
55568        classCallCheck$1(this, MasterPlaylistController);
55569  
55570        var _this = possibleConstructorReturn$1(this, (MasterPlaylistController.__proto__ || Object.getPrototypeOf(MasterPlaylistController)).call(this));
55571  
55572        var url = options.url,
55573            handleManifestRedirects = options.handleManifestRedirects,
55574            withCredentials = options.withCredentials,
55575            tech = options.tech,
55576            bandwidth = options.bandwidth,
55577            externHls = options.externHls,
55578            useCueTags = options.useCueTags,
55579            blacklistDuration = options.blacklistDuration,
55580            enableLowInitialPlaylist = options.enableLowInitialPlaylist,
55581            cacheEncryptionKeys = options.cacheEncryptionKeys,
55582            sourceType = options.sourceType;
55583  
55584        if (!url) {
55585          throw new Error('A non-empty playlist URL is required');
55586        }
55587  
55588        Hls = externHls;
55589        _this.withCredentials = withCredentials;
55590        _this.tech_ = tech;
55591        _this.hls_ = tech.hls;
55592        _this.sourceType_ = sourceType;
55593        _this.useCueTags_ = useCueTags;
vendor: 18,094 bytes, lines 55594-56071
55594        _this.blacklistDuration = blacklistDuration;
55595        _this.enableLowInitialPlaylist = enableLowInitialPlaylist;
55596  
55597        if (_this.useCueTags_) {
55598          _this.cueTagsTrack_ = _this.tech_.addTextTrack('metadata', 'ad-cues');
55599          _this.cueTagsTrack_.inBandMetadataTrackDispatchType = '';
55600        }
55601  
55602        _this.requestOptions_ = {
55603          withCredentials: withCredentials,
55604          handleManifestRedirects: handleManifestRedirects,
55605          timeout: null
55606        };
55607        _this.mediaTypes_ = createMediaTypes();
55608        _this.mediaSource = new videojs$1.MediaSource(); // load the media source into the player
55609  
55610        _this.mediaSource.addEventListener('sourceopen', _this.handleSourceOpen_.bind(_this));
55611  
55612        _this.seekable_ = videojs$1.createTimeRanges();
55613        _this.hasPlayed_ = false;
55614        _this.syncController_ = new SyncController(options);
55615        _this.segmentMetadataTrack_ = tech.addRemoteTextTrack({
55616          kind: 'metadata',
55617          label: 'segment-metadata'
55618        }, false).track;
55619        _this.decrypter_ = new Decrypter$1();
55620        _this.inbandTextTracks_ = {};
55621        var segmentLoaderSettings = {
55622          hls: _this.hls_,
55623          mediaSource: _this.mediaSource,
55624          currentTime: _this.tech_.currentTime.bind(_this.tech_),
55625          seekable: function seekable$$1() {
55626            return _this.seekable();
55627          },
55628          seeking: function seeking() {
55629            return _this.tech_.seeking();
55630          },
55631          duration: function duration$$1() {
55632            return _this.mediaSource.duration;
55633          },
55634          hasPlayed: function hasPlayed() {
55635            return _this.hasPlayed_;
55636          },
55637          goalBufferLength: function goalBufferLength() {
55638            return _this.goalBufferLength();
55639          },
55640          bandwidth: bandwidth,
55641          syncController: _this.syncController_,
55642          decrypter: _this.decrypter_,
55643          sourceType: _this.sourceType_,
55644          inbandTextTracks: _this.inbandTextTracks_,
55645          cacheEncryptionKeys: cacheEncryptionKeys
55646        };
55647        _this.masterPlaylistLoader_ = _this.sourceType_ === 'dash' ? new DashPlaylistLoader(url, _this.hls_, _this.requestOptions_) : new PlaylistLoader(url, _this.hls_, _this.requestOptions_);
55648  
55649        _this.setupMasterPlaylistLoaderListeners_(); // setup segment loaders
55650        // combined audio/video or just video when alternate audio track is selected
55651  
55652  
55653        _this.mainSegmentLoader_ = new SegmentLoader(videojs$1.mergeOptions(segmentLoaderSettings, {
55654          segmentMetadataTrack: _this.segmentMetadataTrack_,
55655          loaderType: 'main'
55656        }), options); // alternate audio track
55657  
55658        _this.audioSegmentLoader_ = new SegmentLoader(videojs$1.mergeOptions(segmentLoaderSettings, {
55659          loaderType: 'audio'
55660        }), options);
55661        _this.subtitleSegmentLoader_ = new VTTSegmentLoader(videojs$1.mergeOptions(segmentLoaderSettings, {
55662          loaderType: 'vtt',
55663          featuresNativeTextTracks: _this.tech_.featuresNativeTextTracks
55664        }), options);
55665  
55666        _this.setupSegmentLoaderListeners_(); // Create SegmentLoader stat-getters
55667  
55668  
55669        loaderStats.forEach(function (stat) {
55670          _this[stat + '_'] = sumLoaderStat.bind(_this, stat);
55671        });
55672        _this.logger_ = logger('MPC');
55673  
55674        _this.masterPlaylistLoader_.load();
55675  
55676        return _this;
55677      }
55678      /**
55679       * Register event handlers on the master playlist loader. A helper
55680       * function for construction time.
55681       *
55682       * @private
55683       */
55684  
55685  
55686      createClass$1(MasterPlaylistController, [{
55687        key: 'setupMasterPlaylistLoaderListeners_',
55688        value: function setupMasterPlaylistLoaderListeners_() {
55689          var _this2 = this;
55690  
55691          this.masterPlaylistLoader_.on('loadedmetadata', function () {
55692            var media = _this2.masterPlaylistLoader_.media();
55693  
55694            var requestTimeout = media.targetDuration * 1.5 * 1000; // If we don't have any more available playlists, we don't want to
55695            // timeout the request.
55696  
55697            if (isLowestEnabledRendition(_this2.masterPlaylistLoader_.master, _this2.masterPlaylistLoader_.media())) {
55698              _this2.requestOptions_.timeout = 0;
55699            } else {
55700              _this2.requestOptions_.timeout = requestTimeout;
55701            } // if this isn't a live video and preload permits, start
55702            // downloading segments
55703  
55704  
55705            if (media.endList && _this2.tech_.preload() !== 'none') {
55706              _this2.mainSegmentLoader_.playlist(media, _this2.requestOptions_);
55707  
55708              _this2.mainSegmentLoader_.load();
55709            }
55710  
55711            setupMediaGroups({
55712              sourceType: _this2.sourceType_,
55713              segmentLoaders: {
55714                AUDIO: _this2.audioSegmentLoader_,
55715                SUBTITLES: _this2.subtitleSegmentLoader_,
55716                main: _this2.mainSegmentLoader_
55717              },
55718              tech: _this2.tech_,
55719              requestOptions: _this2.requestOptions_,
55720              masterPlaylistLoader: _this2.masterPlaylistLoader_,
55721              hls: _this2.hls_,
55722              master: _this2.master(),
55723              mediaTypes: _this2.mediaTypes_,
55724              blacklistCurrentPlaylist: _this2.blacklistCurrentPlaylist.bind(_this2)
55725            });
55726  
55727            _this2.triggerPresenceUsage_(_this2.master(), media);
55728  
55729            try {
55730              _this2.setupSourceBuffers_();
55731            } catch (e) {
55732              videojs$1.log.warn('Failed to create SourceBuffers', e);
55733              return _this2.mediaSource.endOfStream('decode');
55734            }
55735  
55736            _this2.setupFirstPlay();
55737  
55738            if (!_this2.mediaTypes_.AUDIO.activePlaylistLoader || _this2.mediaTypes_.AUDIO.activePlaylistLoader.media()) {
55739              _this2.trigger('selectedinitialmedia');
55740            } else {
55741              // We must wait for the active audio playlist loader to
55742              // finish setting up before triggering this event so the
55743              // representations API and EME setup is correct
55744              _this2.mediaTypes_.AUDIO.activePlaylistLoader.one('loadedmetadata', function () {
55745                _this2.trigger('selectedinitialmedia');
55746              });
55747            }
55748          });
55749          this.masterPlaylistLoader_.on('loadedplaylist', function () {
55750            var updatedPlaylist = _this2.masterPlaylistLoader_.media();
55751  
55752            if (!updatedPlaylist) {
55753              // blacklist any variants that are not supported by the browser before selecting
55754              // an initial media as the playlist selectors do not consider browser support
55755              _this2.excludeUnsupportedVariants_();
55756  
55757              var selectedMedia = void 0;
55758  
55759              if (_this2.enableLowInitialPlaylist) {
55760                selectedMedia = _this2.selectInitialPlaylist();
55761              }
55762  
55763              if (!selectedMedia) {
55764                selectedMedia = _this2.selectPlaylist();
55765              }
55766  
55767              _this2.initialMedia_ = selectedMedia;
55768  
55769              _this2.masterPlaylistLoader_.media(_this2.initialMedia_);
55770  
55771              return;
55772            }
55773  
55774            if (_this2.useCueTags_) {
55775              _this2.updateAdCues_(updatedPlaylist);
55776            } // TODO: Create a new event on the PlaylistLoader that signals
55777            // that the segments have changed in some way and use that to
55778            // update the SegmentLoader instead of doing it twice here and
55779            // on `mediachange`
55780  
55781  
55782            _this2.mainSegmentLoader_.playlist(updatedPlaylist, _this2.requestOptions_);
55783  
55784            _this2.updateDuration(); // If the player isn't paused, ensure that the segment loader is running,
55785            // as it is possible that it was temporarily stopped while waiting for
55786            // a playlist (e.g., in case the playlist errored and we re-requested it).
55787  
55788  
55789            if (!_this2.tech_.paused()) {
55790              _this2.mainSegmentLoader_.load();
55791  
55792              if (_this2.audioSegmentLoader_) {
55793                _this2.audioSegmentLoader_.load();
55794              }
55795            }
55796  
55797            if (!updatedPlaylist.endList) {
55798              var addSeekableRange = function addSeekableRange() {
55799                var seekable$$1 = _this2.seekable();
55800  
55801                if (seekable$$1.length !== 0) {
55802                  _this2.mediaSource.addSeekableRange_(seekable$$1.start(0), seekable$$1.end(0));
55803                }
55804              };
55805  
55806              if (_this2.duration() !== Infinity) {
55807                var onDurationchange = function onDurationchange() {
55808                  if (_this2.duration() === Infinity) {
55809                    addSeekableRange();
55810                  } else {
55811                    _this2.tech_.one('durationchange', onDurationchange);
55812                  }
55813                };
55814  
55815                _this2.tech_.one('durationchange', onDurationchange);
55816              } else {
55817                addSeekableRange();
55818              }
55819            }
55820          });
55821          this.masterPlaylistLoader_.on('error', function () {
55822            _this2.blacklistCurrentPlaylist(_this2.masterPlaylistLoader_.error);
55823          });
55824          this.masterPlaylistLoader_.on('mediachanging', function () {
55825            _this2.mainSegmentLoader_.abort();
55826  
55827            _this2.mainSegmentLoader_.pause();
55828          });
55829          this.masterPlaylistLoader_.on('mediachange', function () {
55830            var media = _this2.masterPlaylistLoader_.media();
55831  
55832            var requestTimeout = media.targetDuration * 1.5 * 1000; // If we don't have any more available playlists, we don't want to
55833            // timeout the request.
55834  
55835            if (isLowestEnabledRendition(_this2.masterPlaylistLoader_.master, _this2.masterPlaylistLoader_.media())) {
55836              _this2.requestOptions_.timeout = 0;
55837            } else {
55838              _this2.requestOptions_.timeout = requestTimeout;
55839            } // TODO: Create a new event on the PlaylistLoader that signals
55840            // that the segments have changed in some way and use that to
55841            // update the SegmentLoader instead of doing it twice here and
55842            // on `loadedplaylist`
55843  
55844  
55845            _this2.mainSegmentLoader_.playlist(media, _this2.requestOptions_);
55846  
55847            _this2.mainSegmentLoader_.load();
55848  
55849            _this2.tech_.trigger({
55850              type: 'mediachange',
55851              bubbles: true
55852            });
55853          });
55854          this.masterPlaylistLoader_.on('playlistunchanged', function () {
55855            var updatedPlaylist = _this2.masterPlaylistLoader_.media();
55856  
55857            var playlistOutdated = _this2.stuckAtPlaylistEnd_(updatedPlaylist);
55858  
55859            if (playlistOutdated) {
55860              // Playlist has stopped updating and we're stuck at its end. Try to
55861              // blacklist it and switch to another playlist in the hope that that
55862              // one is updating (and give the player a chance to re-adjust to the
55863              // safe live point).
55864              _this2.blacklistCurrentPlaylist({
55865                message: 'Playlist no longer updating.'
55866              }); // useful for monitoring QoS
55867  
55868  
55869              _this2.tech_.trigger('playliststuck');
55870            }
55871          });
55872          this.masterPlaylistLoader_.on('renditiondisabled', function () {
55873            _this2.tech_.trigger({
55874              type: 'usage',
55875              name: 'hls-rendition-disabled'
55876            });
55877          });
55878          this.masterPlaylistLoader_.on('renditionenabled', function () {
55879            _this2.tech_.trigger({
55880              type: 'usage',
55881              name: 'hls-rendition-enabled'
55882            });
55883          });
55884        }
55885        /**
55886         * A helper function for triggerring presence usage events once per source
55887         *
55888         * @private
55889         */
55890  
55891      }, {
55892        key: 'triggerPresenceUsage_',
55893        value: function triggerPresenceUsage_(master, media) {
55894          var mediaGroups = master.mediaGroups || {};
55895          var defaultDemuxed = true;
55896          var audioGroupKeys = Object.keys(mediaGroups.AUDIO);
55897  
55898          for (var mediaGroup in mediaGroups.AUDIO) {
55899            for (var label in mediaGroups.AUDIO[mediaGroup]) {
55900              var properties = mediaGroups.AUDIO[mediaGroup][label];
55901  
55902              if (!properties.uri) {
55903                defaultDemuxed = false;
55904              }
55905            }
55906          }
55907  
55908          if (defaultDemuxed) {
55909            this.tech_.trigger({
55910              type: 'usage',
55911              name: 'hls-demuxed'
55912            });
55913          }
55914  
55915          if (Object.keys(mediaGroups.SUBTITLES).length) {
55916            this.tech_.trigger({
55917              type: 'usage',
55918              name: 'hls-webvtt'
55919            });
55920          }
55921  
55922          if (Hls.Playlist.isAes(media)) {
55923            this.tech_.trigger({
55924              type: 'usage',
55925              name: 'hls-aes'
55926            });
55927          }
55928  
55929          if (Hls.Playlist.isFmp4(media)) {
55930            this.tech_.trigger({
55931              type: 'usage',
55932              name: 'hls-fmp4'
55933            });
55934          }
55935  
55936          if (audioGroupKeys.length && Object.keys(mediaGroups.AUDIO[audioGroupKeys[0]]).length > 1) {
55937            this.tech_.trigger({
55938              type: 'usage',
55939              name: 'hls-alternate-audio'
55940            });
55941          }
55942  
55943          if (this.useCueTags_) {
55944            this.tech_.trigger({
55945              type: 'usage',
55946              name: 'hls-playlist-cue-tags'
55947            });
55948          }
55949        }
55950        /**
55951         * Register event handlers on the segment loaders. A helper function
55952         * for construction time.
55953         *
55954         * @private
55955         */
55956  
55957      }, {
55958        key: 'setupSegmentLoaderListeners_',
55959        value: function setupSegmentLoaderListeners_() {
55960          var _this3 = this;
55961  
55962          this.mainSegmentLoader_.on('bandwidthupdate', function () {
55963            var nextPlaylist = _this3.selectPlaylist();
55964  
55965            var currentPlaylist = _this3.masterPlaylistLoader_.media();
55966  
55967            var buffered = _this3.tech_.buffered();
55968  
55969            var forwardBuffer = buffered.length ? buffered.end(buffered.length - 1) - _this3.tech_.currentTime() : 0;
55970  
55971            var bufferLowWaterLine = _this3.bufferLowWaterLine();
55972  
55973            if (shouldSwitchToMedia({
55974              currentPlaylist: currentPlaylist,
55975              nextPlaylist: nextPlaylist,
55976              forwardBuffer: forwardBuffer,
55977              bufferLowWaterLine: bufferLowWaterLine,
55978              duration: _this3.duration(),
55979              log: _this3.logger_
55980            })) {
55981              _this3.masterPlaylistLoader_.media(nextPlaylist);
55982            }
55983  
55984            _this3.tech_.trigger('bandwidthupdate');
55985          });
55986          this.mainSegmentLoader_.on('progress', function () {
55987            _this3.trigger('progress');
55988          });
55989          this.mainSegmentLoader_.on('error', function () {
55990            _this3.blacklistCurrentPlaylist(_this3.mainSegmentLoader_.error());
55991          });
55992          this.mainSegmentLoader_.on('syncinfoupdate', function () {
55993            _this3.onSyncInfoUpdate_();
55994          });
55995          this.mainSegmentLoader_.on('timestampoffset', function () {
55996            _this3.tech_.trigger({
55997              type: 'usage',
55998              name: 'hls-timestamp-offset'
55999            });
56000          });
56001          this.audioSegmentLoader_.on('syncinfoupdate', function () {
56002            _this3.onSyncInfoUpdate_();
56003          });
56004          this.mainSegmentLoader_.on('ended', function () {
56005            _this3.onEndOfStream();
56006          });
56007          this.mainSegmentLoader_.on('earlyabort', function () {
56008            _this3.blacklistCurrentPlaylist({
56009              message: 'Aborted early because there isn\'t enough bandwidth to complete the ' + 'request without rebuffering.'
56010            }, ABORT_EARLY_BLACKLIST_SECONDS);
56011          });
56012          this.mainSegmentLoader_.on('reseteverything', function () {
56013            // If playing an MTS stream, a videojs.MediaSource is listening for
56014            // hls-reset to reset caption parsing state in the transmuxer
56015            _this3.tech_.trigger('hls-reset');
56016          });
56017          this.mainSegmentLoader_.on('segmenttimemapping', function (event) {
56018            // If playing an MTS stream in html, a videojs.MediaSource is listening for
56019            // hls-segment-time-mapping update its internal mapping of stream to display time
56020            _this3.tech_.trigger({
56021              type: 'hls-segment-time-mapping',
56022              mapping: event.mapping
56023            });
56024          });
56025          this.audioSegmentLoader_.on('ended', function () {
56026            _this3.onEndOfStream();
56027          });
56028        }
56029      }, {
56030        key: 'mediaSecondsLoaded_',
56031        value: function mediaSecondsLoaded_() {
56032          return Math.max(this.audioSegmentLoader_.mediaSecondsLoaded + this.mainSegmentLoader_.mediaSecondsLoaded);
56033        }
56034        /**
56035         * Call load on our SegmentLoaders
56036         */
56037  
56038      }, {
56039        key: 'load',
56040        value: function load() {
56041          this.mainSegmentLoader_.load();
56042  
56043          if (this.mediaTypes_.AUDIO.activePlaylistLoader) {
56044            this.audioSegmentLoader_.load();
56045          }
56046  
56047          if (this.mediaTypes_.SUBTITLES.activePlaylistLoader) {
56048            this.subtitleSegmentLoader_.load();
56049          }
56050        }
56051        /**
56052         * Re-tune playback quality level for the current player
56053         * conditions without performing destructive actions, like
56054         * removing already buffered content
56055         *
56056         * @private
56057         */
56058  
56059      }, {
56060        key: 'smoothQualityChange_',
56061        value: function smoothQualityChange_() {
56062          var media = this.selectPlaylist();
56063  
56064          if (media !== this.masterPlaylistLoader_.media()) {
56065            this.masterPlaylistLoader_.media(media);
56066            this.mainSegmentLoader_.resetLoader(); // don't need to reset audio as it is reset when media changes
56067          }
56068        }
56069        /**
56070         * Re-tune playback quality level for the current player
56071         * conditions. This method will perform destructive actions like remov
56071ing
56072         * already buffered content in order to readjust the currently active
56073         * playlist quickly. This is good for manual quality changes
56074         *
56075         * @private
56076         */
56077  
56078      }, {
56079        key: 'fastQualityChange_',
56080        value: function fastQualityChange_() {
56081          var _this4 = this;
56082  
56083          var media = this.selectPlaylist();
56084  
56085          if (media === this.masterPlaylistLoader_.media()) {
56086            return;
56087          }
56088  
56089          this.masterPlaylistLoader_.media(media); // Delete all buffered data to allow an immediate quality switch, then seek to give
56090          // the browser a kick to remove any cached frames from the previous rendtion (.04 seconds
56091          // ahead is roughly the minimum that will accomplish this across a variety of content
56092          // in IE and Edge, but seeking in place is sufficient on all other browsers)
56093          // Edge/IE bug: https://developer.microsoft.com/en-us/microsoft-edge/platform/issues/14600375/
56094          // Chrome bug: https://bugs.chromium.org/p/chromium/issues/detail?id=651904
56095  
56096          this.mainSegmentLoader_.resetEverything(function () {
56097            // Since this is not a typical seek, we avoid the seekTo method which can cause segments
56098            // from the previously enabled rendition to load before the new playlist has finished loading
56099            if (videojs$1.browser.IE_VERSION || videojs$1.browser.IS_EDGE) {
56100              _this4.tech_.setCurrentTime(_this4.tech_.currentTime() + 0.04);
56101            } else {
56102              _this4.tech_.setCurrentTime(_this4.tech_.currentTime());
56103            }
56104          }); // don't need to reset audio as it is reset when media changes
56105        }
56106        /**
56107         * Begin playback.
56108         */
56109  
56110      }, {
56111        key: 'play',
56112        value: function play() {
56113          if (this.setupFirstPlay()) {
56114            return;
56115          }
56116  
56117          if (this.tech_.ended()) {
56118            this.tech_.setCurrentTime(0);
56119          }
56120  
56121          if (this.hasPlayed_) {
56122            this.load();
56123          }
56124  
56125          var seekable$$1 = this.tech_.seekable(); // if the viewer has paused and we fell out of the live window,
56126          // seek forward to the live point
56127  
56128          if (this.tech_.duration() === Infinity) {
56129            if (this.tech_.currentTime() < seekable$$1.start(0)) {
56130              return this.tech_.setCurrentTime(seekable$$1.end(seekable$$1.length - 1));
56131            }
56132          }
56133        }
56134        /**
56135         * Seek to the latest media position if this is a live video and the
56136         * player and video are loaded and initialized.
56137         */
56138  
56139      }, {
56140        key: 'setupFirstPlay',
56141        value: function setupFirstPlay() {
56142          var _this5 = this;
56143  
56144          var media = this.masterPlaylistLoader_.media(); // Check that everything is ready to begin buffering for the first call to play
56145          //  If 1) there is no active media
56146          //     2) the player is paused
56147          //     3) the first play has already been setup
56148          // then exit early
56149  
56150          if (!media || this.tech_.paused() || this.hasPlayed_) {
56151            return false;
56152          } // when the video is a live stream
56153  
56154  
56155          if (!media.endList) {
56156            var seekable$$1 = this.seekable();
56157  
56158            if (!seekable$$1.length) {
56159              // without a seekable range, the player cannot seek to begin buffering at the live
56160              // point
56161              return false;
56162            }
56163  
56164            if (videojs$1.browser.IE_VERSION && this.tech_.readyState() === 0) {
56165              // IE11 throws an InvalidStateError if you try to set currentTime while the
56166              // readyState is 0, so it must be delayed until the tech fires loadedmetadata.
56167              this.tech_.one('loadedmetadata', function () {
56168                _this5.trigger('firstplay');
56169  
56170                _this5.tech_.setCurrentTime(seekable$$1.end(0));
56171  
56172                _this5.hasPlayed_ = true;
56173              });
56174              return false;
56175            } // trigger firstplay to inform the source handler to ignore the next seek event
56176  
56177  
56178            this.trigger('firstplay'); // seek to the live point
56179  
56180            this.tech_.setCurrentTime(seekable$$1.end(0));
56181          }
56182  
56183          this.hasPlayed_ = true; // we can begin loading now that everything is ready
56184  
56185          this.load();
56186          return true;
56187        }
56188        /**
56189         * handle the sourceopen event on the MediaSource
56190         *
56191         * @private
56192         */
56193  
56194      }, {
56195        key: 'handleSourceOpen_',
56196        value: function handleSourceOpen_() {
56197          // Only attempt to create the source buffer if none already exist.
56198          // handleSourceOpen is also called when we are "re-opening" a source buffer
56199          // after `endOfStream` has been called (in response to a seek for instance)
56200          try {
56201            this.setupSourceBuffers_();
56202          } catch (e) {
56203            videojs$1.log.warn('Failed to create Source Buffers', e);
56204            return this.mediaSource.endOfStream('decode');
56205          } // if autoplay is enabled, begin playback. This is duplicative of
56206          // code in video.js but is required because play() must be invoked
56207          // *after* the media source has opened.
56208  
56209  
56210          if (this.tech_.autoplay()) {
vendor: 4,870 bytes, lines 56211-56322
56211            var playPromise = this.tech_.play(); // Catch/silence error when a pause interrupts a play request
56212            // on browsers which return a promise
56213  
56214            if (typeof playPromise !== 'undefined' && typeof playPromise.then === 'function') {
56215              playPromise.then(null, function (e) {});
56216            }
56217          }
56218  
56219          this.trigger('sourceopen');
56220        }
56221        /**
56222         * Calls endOfStream on the media source when all active stream types have called
56223         * endOfStream
56224         *
56225         * @param {string} streamType
56226         *        Stream type of the segment loader that called endOfStream
56227         * @private
56228         */
56229  
56230      }, {
56231        key: 'onEndOfStream',
56232        value: function onEndOfStream() {
56233          var isEndOfStream = this.mainSegmentLoader_.ended_;
56234  
56235          if (this.mediaTypes_.AUDIO.activePlaylistLoader) {
56236            // if the audio playlist loader exists, then alternate audio is active
56237            if (!this.mainSegmentLoader_.startingMedia_ || this.mainSegmentLoader_.startingMedia_.containsVideo) {
56238              // if we do not know if the main segment loader contains video yet or if we
56239              // definitively know the main segment loader contains video, then we need to wait
56240              // for both main and audio segment loaders to call endOfStream
56241              isEndOfStream = isEndOfStream && this.audioSegmentLoader_.ended_;
56242            } else {
56243              // otherwise just rely on the audio loader
56244              isEndOfStream = this.audioSegmentLoader_.ended_;
56245            }
56246          }
56247  
56248          if (!isEndOfStream) {
56249            return;
56250          }
56251  
56252          this.logger_('calling mediaSource.endOfStream()'); // on chrome calling endOfStream can sometimes cause an exception,
56253          // even when the media source is in a valid state.
56254  
56255          try {
56256            this.mediaSource.endOfStream();
56257          } catch (e) {
56258            videojs$1.log.warn('Failed to call media source endOfStream', e);
56259          }
56260        }
56261        /**
56262         * Check if a playlist has stopped being updated
56263         * @param {Object} playlist the media playlist object
56264         * @return {boolean} whether the playlist has stopped being updated or not
56265         */
56266  
56267      }, {
56268        key: 'stuckAtPlaylistEnd_',
56269        value: function stuckAtPlaylistEnd_(playlist) {
56270          var seekable$$1 = this.seekable();
56271  
56272          if (!seekable$$1.length) {
56273            // playlist doesn't have enough information to determine whether we are stuck
56274            return false;
56275          }
56276  
56277          var expired = this.syncController_.getExpiredTime(playlist, this.mediaSource.duration);
56278  
56279          if (expired === null) {
56280            return false;
56281          } // does not use the safe live end to calculate playlist end, since we
56282          // don't want to say we are stuck while there is still content
56283  
56284  
56285          var absolutePlaylistEnd = Hls.Playlist.playlistEnd(playlist, expired);
56286          var currentTime = this.tech_.currentTime();
56287          var buffered = this.tech_.buffered();
56288  
56289          if (!buffered.length) {
56290            // return true if the playhead reached the absolute end of the playlist
56291            return absolutePlaylistEnd - currentTime <= SAFE_TIME_DELTA;
56292          }
56293  
56294          var bufferedEnd = buffered.end(buffered.length - 1); // return true if there is too little buffer left and buffer has reached absolute
56295          // end of playlist
56296  
56297          return bufferedEnd - currentTime <= SAFE_TIME_DELTA && absolutePlaylistEnd - bufferedEnd <= SAFE_TIME_DELTA;
56298        }
56299        /**
56300         * Blacklists a playlist when an error occurs for a set amount of time
56301         * making it unavailable for selection by the rendition selection algorithm
56302         * and then forces a new playlist (rendition) selection.
56303         *
56304         * @param {Object=} error an optional error that may include the playlist
56305         * to blacklist
56306         * @param {Number=} blacklistDuration an optional number of seconds to blacklist the
56307         * playlist
56308         */
56309  
56310      }, {
56311        key: 'blacklistCurrentPlaylist',
56312        value: function blacklistCurrentPlaylist() {
56313          var error = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
56314          var blacklistDuration = arguments[1];
56315          var currentPlaylist = void 0;
56316          var nextPlaylist = void 0; // If the `error` was generated by the playlist loader, it will contain
56317          // the playlist we were trying to load (but failed) and that should be
56318          // blacklisted instead of the currently selected playlist which is likely
56319          // out-of-date in this scenario
56320  
56321          currentPlaylist = error.playlist || this.masterPlaylistLoader_.media();
56322          blacklistDuration = blacklistDuration || error.blacklistDuration || this.blackl
56322istDuration; // If there is no current playlist, then an error occurred while we were
56323          // trying to load the master OR while we were disposing of the tech
56324  
56325          if (!currentPlaylist) {
56326            this.error = error;
56327  
56328            try {
56329              return this.mediaSource.endOfStream('network');
56330            } catch (e) {
56331              return this.trigger('error');
56332            }
56333          }
56334  
56335          var isFinalRendition = this.masterPlaylistLoader_.master.playlists.filter(isEnabled).length === 1;
56336          var playlists = this.masterPlaylistLoader_.master.playlists;
56337  
56338          if (playlists.length === 1) {
56339            // Never blacklisting this playlist because it's the only playlist
56340            videojs$1.log.warn('Problem encountered with the current ' + 'HLS playlist. Trying again since it is the only playlist.');
56341            this.tech_.trigger('retryplaylist');
56342            return this.masterPlaylistLoader_.load(isFinalRendition);
56343          }
56344  
56345          if (isFinalRendition) {
56346            // Since we're on the final non-blacklisted playlist, and we're about to blacklist
56347            // it, instead of erring the player or retrying this playlist, clear out the current
56348            // blacklist. This allows other playlists to be attempted in case any have been
56349            // fixed.
56350            videojs$1.log.warn('Removing all playlists from the blacklist because the last ' + 'rendition is about to be blacklisted.');
vendor: 12,527 bytes, lines 56351-56703
56351            playlists.forEach(function (playlist) {
56352              if (playlist.excludeUntil !== Infinity) {
56353                delete playlist.excludeUntil;
56354              }
56355            }); // Technically we are retrying a playlist, in that we are simply retrying a previous
56356            // playlist. This is needed for users relying on the retryplaylist event to catch a
56357            // case where the player might be stuck and looping through "dead" playlists.
56358  
56359            this.tech_.trigger('retryplaylist');
56360          } // Blacklist this playlist
56361  
56362  
56363          currentPlaylist.excludeUntil = Date.now() + blacklistDuration * 1000;
56364          this.tech_.trigger('blacklistplaylist');
56365          this.tech_.trigger({
56366            type: 'usage',
56367            name: 'hls-rendition-blacklisted'
56368          }); // Select a new playlist
56369  
56370          nextPlaylist = this.selectPlaylist();
56371          videojs$1.log.warn('Problem encountered with the current HLS playlist.' + (error.message ? ' ' + error.message : '') + ' Switching to another playlist.');
56372          return this.masterPlaylistLoader_.media(nextPlaylist, isFinalRendition);
56373        }
56374        /**
56375         * Pause all segment loaders
56376         */
56377  
56378      }, {
56379        key: 'pauseLoading',
56380        value: function pauseLoading() {
56381          this.mainSegmentLoader_.pause();
56382  
56383          if (this.mediaTypes_.AUDIO.activePlaylistLoader) {
56384            this.audioSegmentLoader_.pause();
56385          }
56386  
56387          if (this.mediaTypes_.SUBTITLES.activePlaylistLoader) {
56388            this.subtitleSegmentLoader_.pause();
56389          }
56390        }
56391        /**
56392         * set the current time on all segment loaders
56393         *
56394         * @param {TimeRange} currentTime the current time to set
56395         * @return {TimeRange} the current time
56396         */
56397  
56398      }, {
56399        key: 'setCurrentTime',
56400        value: function setCurrentTime(currentTime) {
56401          var buffered = findRange(this.tech_.buffered(), currentTime);
56402  
56403          if (!(this.masterPlaylistLoader_ && this.masterPlaylistLoader_.media())) {
56404            // return immediately if the metadata is not ready yet
56405            return 0;
56406          } // it's clearly an edge-case but don't thrown an error if asked to
56407          // seek within an empty playlist
56408  
56409  
56410          if (!this.masterPlaylistLoader_.media().segments) {
56411            return 0;
56412          } // In flash playback, the segment loaders should be reset on every seek, even
56413          // in buffer seeks. If the seek location is already buffered, continue buffering as
56414          // usual
56415          // TODO: redo this comment
56416  
56417  
56418          if (buffered && buffered.length) {
56419            return currentTime;
56420          } // cancel outstanding requests so we begin buffering at the new
56421          // location
56422  
56423  
56424          this.mainSegmentLoader_.resetEverything();
56425          this.mainSegmentLoader_.abort();
56426  
56427          if (this.mediaTypes_.AUDIO.activePlaylistLoader) {
56428            this.audioSegmentLoader_.resetEverything();
56429            this.audioSegmentLoader_.abort();
56430          }
56431  
56432          if (this.mediaTypes_.SUBTITLES.activePlaylistLoader) {
56433            this.subtitleSegmentLoader_.resetEverything();
56434            this.subtitleSegmentLoader_.abort();
56435          } // start segment loader loading in case they are paused
56436  
56437  
56438          this.load();
56439        }
56440        /**
56441         * get the current duration
56442         *
56443         * @return {TimeRange} the duration
56444         */
56445  
56446      }, {
56447        key: 'duration',
56448        value: function duration$$1() {
56449          if (!this.masterPlaylistLoader_) {
56450            return 0;
56451          }
56452  
56453          if (this.mediaSource) {
56454            return this.mediaSource.duration;
56455          }
56456  
56457          return Hls.Playlist.duration(this.masterPlaylistLoader_.media());
56458        }
56459        /**
56460         * check the seekable range
56461         *
56462         * @return {TimeRange} the seekable range
56463         */
56464  
56465      }, {
56466        key: 'seekable',
56467        value: function seekable$$1() {
56468          return this.seekable_;
56469        }
56470      }, {
56471        key: 'onSyncInfoUpdate_',
56472        value: function onSyncInfoUpdate_() {
56473          var audioSeekable = void 0;
56474  
56475          if (!this.masterPlaylistLoader_) {
56476            return;
56477          }
56478  
56479          var media = this.masterPlaylistLoader_.media();
56480  
56481          if (!media) {
56482            return;
56483          }
56484  
56485          var expired = this.syncController_.getExpiredTime(media, this.mediaSource.duration);
56486  
56487          if (expired === null) {
56488            // not enough information to update seekable
56489            return;
56490          }
56491  
56492          var suggestedPresentationDelay = this.masterPlaylistLoader_.master.suggestedPresentationDelay;
56493          var mainSeekable = Hls.Playlist.seekable(media, expired, suggestedPresentationDelay);
56494  
56495          if (mainSeekable.length === 0) {
56496            return;
56497          }
56498  
56499          if (this.mediaTypes_.AUDIO.activePlaylistLoader) {
56500            media = this.mediaTypes_.AUDIO.activePlaylistLoader.media();
56501            expired = this.syncController_.getExpiredTime(media, this.mediaSource.duration);
56502  
56503            if (expired === null) {
56504              return;
56505            }
56506  
56507            audioSeekable = Hls.Playlist.seekable(media, expired, suggestedPresentationDelay);
56508  
56509            if (audioSeekable.length === 0) {
56510              return;
56511            }
56512          }
56513  
56514          var oldEnd = void 0;
56515          var oldStart = void 0;
56516  
56517          if (this.seekable_ && this.seekable_.length) {
56518            oldEnd = this.seekable_.end(0);
56519            oldStart = this.seekable_.start(0);
56520          }
56521  
56522          if (!audioSeekable) {
56523            // seekable has been calculated based on buffering video data so it
56524            // can be returned directly
56525            this.seekable_ = mainSeekable;
56526          } else if (audioSeekable.start(0) > mainSeekable.end(0) || mainSeekable.start(0) > audioSeekable.end(0)) {
56527            // seekables are pretty far off, rely on main
56528            this.seekable_ = mainSeekable;
56529          } else {
56530            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)]]);
56531          } // seekable is the same as last time
56532  
56533  
56534          if (this.seekable_ && this.seekable_.length) {
56535            if (this.seekable_.end(0) === oldEnd && this.seekable_.start(0) === oldStart) {
56536              return;
56537            }
56538          }
56539  
56540          this.logger_('seekable updated [' + printableRange(this.seekable_) + ']');
56541          this.tech_.trigger('seekablechanged');
56542        }
56543        /**
56544         * Update the player duration
56545         */
56546  
56547      }, {
56548        key: 'updateDuration',
56549        value: function updateDuration() {
56550          var _this6 = this;
56551  
56552          var oldDuration = this.mediaSource.duration;
56553          var newDuration = Hls.Playlist.duration(this.masterPlaylistLoader_.media());
56554          var buffered = this.tech_.buffered();
56555  
56556          var setDuration = function setDuration() {
56557            // on firefox setting the duration may sometimes cause an exception
56558            // even if the media source is open and source buffers are not
56559            // updating, something about the media source being in an invalid state.
56560            _this6.logger_('Setting duration from ' + _this6.mediaSource.duration + ' => ' + newDuration);
56561  
56562            try {
56563              _this6.mediaSource.duration = newDuration;
56564            } catch (e) {
56565              videojs$1.log.warn('Failed to set media source duration', e);
56566            }
56567  
56568            _this6.tech_.trigger('durationchange');
56569  
56570            _this6.mediaSource.removeEventListener('sourceopen', setDuration);
56571          };
56572  
56573          if (buffered.length > 0) {
56574            newDuration = Math.max(newDuration, buffered.end(buffered.length - 1));
56575          } // if the duration has changed, invalidate the cached value
56576  
56577  
56578          if (oldDuration !== newDuration) {
56579            // update the duration
56580            if (this.mediaSource.readyState !== 'open') {
56581              this.mediaSource.addEventListener('sourceopen', setDuration);
56582            } else {
56583              setDuration();
56584            }
56585          }
56586        }
56587        /**
56588         * dispose of the MasterPlaylistController and everything
56589         * that it controls
56590         */
56591  
56592      }, {
56593        key: 'dispose',
56594        value: function dispose() {
56595          var _this7 = this;
56596  
56597          this.trigger('dispose');
56598  
56599          if (this.decrypter_) {
56600            this.decrypter_.terminate();
56601          }
56602  
56603          this.masterPlaylistLoader_.dispose();
56604          this.mainSegmentLoader_.dispose();
56605          ['AUDIO', 'SUBTITLES'].forEach(function (type) {
56606            var groups = _this7.mediaTypes_[type].groups;
56607  
56608            for (var id in groups) {
56609              groups[id].forEach(function (group) {
56610                if (group.playlistLoader) {
56611                  group.playlistLoader.dispose();
56612                }
56613              });
56614            }
56615          });
56616          this.audioSegmentLoader_.dispose();
56617          this.subtitleSegmentLoader_.dispose();
56618          this.off();
56619  
56620          if (this.mediaSource.dispose) {
56621            this.mediaSource.dispose();
56622          }
56623        }
56624        /**
56625         * return the master playlist object if we have one
56626         *
56627         * @return {Object} the master playlist object that we parsed
56628         */
56629  
56630      }, {
56631        key: 'master',
56632        value: function master() {
56633          return this.masterPlaylistLoader_.master;
56634        }
56635        /**
56636         * return the currently selected playlist
56637         *
56638         * @return {Object} the currently selected playlist object that we parsed
56639         */
56640  
56641      }, {
56642        key: 'media',
56643        value: function media() {
56644          // playlist loader will not return media if it has not been fully loaded
56645          return this.masterPlaylistLoader_.media() || this.initialMedia_;
56646        }
56647        /**
56648         * setup our internal source buffers on our segment Loaders
56649         *
56650         * @private
56651         */
56652  
56653      }, {
56654        key: 'setupSourceBuffers_',
56655        value: function setupSourceBuffers_() {
56656          var media = this.masterPlaylistLoader_.media();
56657          var mimeTypes = void 0; // wait until a media playlist is available and the Media Source is
56658          // attached
56659  
56660          if (!media || this.mediaSource.readyState !== 'open') {
56661            return;
56662          }
56663  
56664          mimeTypes = mimeTypesForPlaylist(this.masterPlaylistLoader_.master, media);
56665  
56666          if (mimeTypes.length < 1) {
56667            this.error = 'No compatible SourceBuffer configuration for the variant stream:' + media.resolvedUri;
56668            return this.mediaSource.endOfStream('decode');
56669          }
56670  
56671          this.configureLoaderMimeTypes_(mimeTypes); // exclude any incompatible variant streams from future playlist
56672          // selection
56673  
56674          this.excludeIncompatibleVariants_(media);
56675        }
56676      }, {
56677        key: 'configureLoaderMimeTypes_',
56678        value: function configureLoaderMimeTypes_(mimeTypes) {
56679          // If the content is demuxed, we can't start appending segments to a source buffer
56680          // until both source buffers are set up, or else the browser may not let us add the
56681          // second source buffer (it will assume we are playing either audio only or video
56682          // only).
56683          var sourceBufferEmitter = // If there is more than one mime type
56684          mimeTypes.length > 1 && // and the first mime type does not have muxed video and audio
56685          mimeTypes[0].indexOf(',') === -1 && // and the two mime types are different (they can be the same in the case of audio
56686          // only with alternate audio)
56687          mimeTypes[0] !== mimeTypes[1] ? // then we want to wait on the second source buffer
56688          new videojs$1.EventTarget() : // otherwise there is no need to wait as the content is either audio only,
56689          // video only, or muxed content.
56690          null;
56691          this.mainSegmentLoader_.mimeType(mimeTypes[0], sourceBufferEmitter);
56692  
56693          if (mimeTypes[1]) {
56694            this.audioSegmentLoader_.mimeType(mimeTypes[1], sourceBufferEmitter);
56695          }
56696        }
56697        /**
56698         * Blacklists playlists with codecs that are unsupported by the browser.
56699         */
56700  
56701      }, {
56702        key: 'excludeUnsupportedVariants_',
56703        value: function excludeUnsupportedVariants_() {
56704          this.master().playlists.forEach(function (variant) {
56705            if (variant.attributes.CODECS && window$3.MediaSource && window$3.MediaSource.isTypeSupported && !window$3.MediaSource.isTypeSupported('video/mp4; codecs="' + mapLegacyAvcCodecs(variant.attributes.CODECS) + '"')) {
56706              variant.excludeUntil = Infinity;
56707            }
56708          });
56709        }
56710        /**
56711         * Blacklist playlists that are known to be codec or
56712         * stream-incompatible with the SourceBuffer configuration. For
56713         * instance, Media Source Extensions would cause the video element to
56714         * stall waiting for video data if you switched from a variant with
56715         * video and audio to an audio-only one.
56716         *
56717         * @param {Object} media a media playlist compatible with the current
56718         * set of SourceBuffers. Variants in the current master playlist that
56719         * do not appear to have compatible codec or stream configurations
56720         * will be excluded from the default playlist selection algorithm
56721         * indefinitely.
56722         * @private
56723         */
56724  
56725      }, {
56726        key: 'excludeIncompatibleVariants_',
56727        value: function excludeIncompatibleVariants_(media) {
56728          var codecCount = 2;
56729          var videoCodec = null;
56730          var codecs = void 0;
56731  
56732          if (media.attributes.CODECS) {
56733            codecs = parseCodecs(media.attributes.CODECS);
56734            videoCodec = codecs.videoCodec;
56735            codecCount = codecs.codecCount;
56736          }
56737  
vendor: 29,861 bytes, lines 56738-57554
56738          this.master().playlists.forEach(function (variant) {
56739            var variantCodecs = {
56740              codecCount: 2,
56741              videoCodec: null
56742            };
56743  
56744            if (variant.attributes.CODECS) {
56745              variantCodecs = parseCodecs(variant.attributes.CODECS);
56746            } // if the streams differ in the presence or absence of audio or
56747            // video, they are incompatible
56748  
56749  
56750            if (variantCodecs.codecCount !== codecCount) {
56751              variant.excludeUntil = Infinity;
56752            } // if h.264 is specified on the current playlist, some flavor of
56753            // it must be specified on all compatible variants
56754  
56755  
56756            if (variantCodecs.videoCodec !== videoCodec) {
56757              variant.excludeUntil = Infinity;
56758            }
56759          });
56760        }
56761      }, {
56762        key: 'updateAdCues_',
56763        value: function updateAdCues_(media) {
56764          var offset = 0;
56765          var seekable$$1 = this.seekable();
56766  
56767          if (seekable$$1.length) {
56768            offset = seekable$$1.start(0);
56769          }
56770  
56771          updateAdCues(media, this.cueTagsTrack_, offset);
56772        }
56773        /**
56774         * Calculates the desired forward buffer length based on current time
56775         *
56776         * @return {Number} Desired forward buffer length in seconds
56777         */
56778  
56779      }, {
56780        key: 'goalBufferLength',
56781        value: function goalBufferLength() {
56782          var currentTime = this.tech_.currentTime();
56783          var initial = Config.GOAL_BUFFER_LENGTH;
56784          var rate = Config.GOAL_BUFFER_LENGTH_RATE;
56785          var max = Math.max(initial, Config.MAX_GOAL_BUFFER_LENGTH);
56786          return Math.min(initial + currentTime * rate, max);
56787        }
56788        /**
56789         * Calculates the desired buffer low water line based on current time
56790         *
56791         * @return {Number} Desired buffer low water line in seconds
56792         */
56793  
56794      }, {
56795        key: 'bufferLowWaterLine',
56796        value: function bufferLowWaterLine() {
56797          var currentTime = this.tech_.currentTime();
56798          var initial = Config.BUFFER_LOW_WATER_LINE;
56799          var rate = Config.BUFFER_LOW_WATER_LINE_RATE;
56800          var max = Math.max(initial, Config.MAX_BUFFER_LOW_WATER_LINE);
56801          return Math.min(initial + currentTime * rate, max);
56802        }
56803      }]);
56804      return MasterPlaylistController;
56805    }(videojs$1.EventTarget);
56806    /**
56807     * Returns a function that acts as the Enable/disable playlist function.
56808     *
56809     * @param {PlaylistLoader} loader - The master playlist loader
56810  
56811     * @param {string} playlistID - id of the playlist
56812     * @param {Function} changePlaylistFn - A function to be called after a
56813     * playlist's enabled-state has been changed. Will NOT be called if a
56814     * playlist's enabled-state is unchanged
56815     * @param {Boolean=} enable - Value to set the playlist enabled-state to
56816     * or if undefined returns the current enabled-state for the playlist
56817     * @return {Function} Function for setting/getting enabled
56818     */
56819  
56820  
56821    var enableFunction = function enableFunction(loader, playlistID, changePlaylistFn) {
56822      return function (enable) {
56823        var playlist = loader.master.playlists[playlistID];
56824        var incompatible = isIncompatible(playlist);
56825        var currentlyEnabled = isEnabled(playlist);
56826  
56827        if (typeof enable === 'undefined') {
56828          return currentlyEnabled;
56829        }
56830  
56831        if (enable) {
56832          delete playlist.disabled;
56833        } else {
56834          playlist.disabled = true;
56835        }
56836  
56837        if (enable !== currentlyEnabled && !incompatible) {
56838          // Ensure the outside world knows about our changes
56839          changePlaylistFn();
56840  
56841          if (enable) {
56842            loader.trigger('renditionenabled');
56843          } else {
56844            loader.trigger('renditiondisabled');
56845          }
56846        }
56847  
56848        return enable;
56849      };
56850    };
56851    /**
56852     * The representation object encapsulates the publicly visible information
56853     * in a media playlist along with a setter/getter-type function (enabled)
56854     * for changing the enabled-state of a particular playlist entry
56855     *
56856     * @class Representation
56857     */
56858  
56859  
56860    var Representation = function Representation(hlsHandler, playlist, id) {
56861      classCallCheck$1(this, Representation);
56862      var mpc = hlsHandler.masterPlaylistController_,
56863          smoothQualityChange = hlsHandler.options_.smoothQualityChange; // Get a reference to a bound version of the quality change function
56864  
56865      var changeType = smoothQualityChange ? 'smooth' : 'fast';
56866      var qualityChangeFunction = mpc[changeType + 'QualityChange_'].bind(mpc); // some playlist attributes are optional
56867  
56868      if (playlist.attributes.RESOLUTION) {
56869        var resolution = playlist.attributes.RESOLUTION;
56870        this.width = resolution.width;
56871        this.height = resolution.height;
56872      }
56873  
56874      this.bandwidth = playlist.attributes.BANDWIDTH; // The id is simply the ordinality of the media playlist
56875      // within the master playlist
56876  
56877      this.id = id; // Partially-apply the enableFunction to create a playlist-
56878      // specific variant
56879  
56880      this.enabled = enableFunction(hlsHandler.playlists, playlist.id, qualityChangeFunction);
56881    };
56882    /**
56883     * A mixin function that adds the `representations` api to an instance
56884     * of the HlsHandler class
56885     * @param {HlsHandler} hlsHandler - An instance of HlsHandler to add the
56886     * representation API into
56887     */
56888  
56889  
56890    var renditionSelectionMixin = function renditionSelectionMixin(hlsHandler) {
56891      var playlists = hlsHandler.playlists; // Add a single API-specific function to the HlsHandler instance
56892  
56893      hlsHandler.representations = function () {
56894        if (!playlists || !playlists.master || !playlists.master.playlists) {
56895          return [];
56896        }
56897  
56898        return playlists.master.playlists.filter(function (media) {
56899          return !isIncompatible(media);
56900        }).map(function (e, i) {
56901          return new Representation(hlsHandler, e, e.id);
56902        });
56903      };
56904    };
56905    /**
56906     * @file playback-watcher.js
56907     *
56908     * Playback starts, and now my watch begins. It shall not end until my death. I shall
56909     * take no wait, hold no uncleared timeouts, father no bad seeks. I shall wear no crowns
56910     * and win no glory. I shall live and die at my post. I am the corrector of the underflow.
56911     * I am the watcher of gaps. I am the shield that guards the realms of seekable. I pledge
56912     * my life and honor to the Playback Watch, for this Player and all the Players to come.
56913     */
56914    // Set of events that reset the playback-watcher time check logic and clear the timeout
56915  
56916  
56917    var timerCancelEvents = ['seeking', 'seeked', 'pause', 'playing', 'error'];
56918    /**
56919     * @class PlaybackWatcher
56920     */
56921  
56922    var PlaybackWatcher = function () {
56923      /**
56924       * Represents an PlaybackWatcher object.
56925       * @constructor
56926       * @param {object} options an object that includes the tech and settings
56927       */
56928      function PlaybackWatcher(options) {
56929        var _this = this;
56930  
56931        classCallCheck$1(this, PlaybackWatcher);
56932        this.tech_ = options.tech;
56933        this.seekable = options.seekable;
56934        this.allowSeeksWithinUnsafeLiveWindow = options.allowSeeksWithinUnsafeLiveWindow;
56935        this.media = options.media;
56936        this.consecutiveUpdates = 0;
56937        this.lastRecordedTime = null;
56938        this.timer_ = null;
56939        this.checkCurrentTimeTimeout_ = null;
56940        this.logger_ = logger('PlaybackWatcher');
56941        this.logger_('initialize');
56942  
56943        var canPlayHandler = function canPlayHandler() {
56944          return _this.monitorCurrentTime_();
56945        };
56946  
56947        var waitingHandler = function waitingHandler() {
56948          return _this.techWaiting_();
56949        };
56950  
56951        var cancelTimerHandler = function cancelTimerHandler() {
56952          return _this.cancelTimer_();
56953        };
56954  
56955        var fixesBadSeeksHandler = function fixesBadSeeksHandler() {
56956          return _this.fixesBadSeeks_();
56957        };
56958  
56959        this.tech_.on('seekablechanged', fixesBadSeeksHandler);
56960        this.tech_.on('waiting', waitingHandler);
56961        this.tech_.on(timerCancelEvents, cancelTimerHandler);
56962        this.tech_.on('canplay', canPlayHandler); // Define the dispose function to clean up our events
56963  
56964        this.dispose = function () {
56965          _this.logger_('dispose');
56966  
56967          _this.tech_.off('seekablechanged', fixesBadSeeksHandler);
56968  
56969          _this.tech_.off('waiting', waitingHandler);
56970  
56971          _this.tech_.off(timerCancelEvents, cancelTimerHandler);
56972  
56973          _this.tech_.off('canplay', canPlayHandler);
56974  
56975          if (_this.checkCurrentTimeTimeout_) {
56976            window$3.clearTimeout(_this.checkCurrentTimeTimeout_);
56977          }
56978  
56979          _this.cancelTimer_();
56980        };
56981      }
56982      /**
56983       * Periodically check current time to see if playback stopped
56984       *
56985       * @private
56986       */
56987  
56988  
56989      createClass$1(PlaybackWatcher, [{
56990        key: 'monitorCurrentTime_',
56991        value: function monitorCurrentTime_() {
56992          this.checkCurrentTime_();
56993  
56994          if (this.checkCurrentTimeTimeout_) {
56995            window$3.clearTimeout(this.checkCurrentTimeTimeout_);
56996          } // 42 = 24 fps // 250 is what Webkit uses // FF uses 15
56997  
56998  
56999          this.checkCurrentTimeTimeout_ = window$3.setTimeout(this.monitorCurrentTime_.bind(this), 250);
57000        }
57001        /**
57002         * The purpose of this function is to emulate the "waiting" event on
57003         * browsers that do not emit it when they are waiting for more
57004         * data to continue playback
57005         *
57006         * @private
57007         */
57008  
57009      }, {
57010        key: 'checkCurrentTime_',
57011        value: function checkCurrentTime_() {
57012          if (this.tech_.seeking() && this.fixesBadSeeks_()) {
57013            this.consecutiveUpdates = 0;
57014            this.lastRecordedTime = this.tech_.currentTime();
57015            return;
57016          }
57017  
57018          if (this.tech_.paused() || this.tech_.seeking()) {
57019            return;
57020          }
57021  
57022          var currentTime = this.tech_.currentTime();
57023          var buffered = this.tech_.buffered();
57024  
57025          if (this.lastRecordedTime === currentTime && (!buffered.length || currentTime + SAFE_TIME_DELTA >= buffered.end(buffered.length - 1))) {
57026            // If current time is at the end of the final buffered region, then any playback
57027            // stall is most likely caused by buffering in a low bandwidth environment. The tech
57028            // should fire a `waiting` event in this scenario, but due to browser and tech
57029            // inconsistencies. Calling `techWaiting_` here allows us to simulate
57030            // responding to a native `waiting` event when the tech fails to emit one.
57031            return this.techWaiting_();
57032          }
57033  
57034          if (this.consecutiveUpdates >= 5 && currentTime === this.lastRecordedTime) {
57035            this.consecutiveUpdates++;
57036            this.waiting_();
57037          } else if (currentTime === this.lastRecordedTime) {
57038            this.consecutiveUpdates++;
57039          } else {
57040            this.consecutiveUpdates = 0;
57041            this.lastRecordedTime = currentTime;
57042          }
57043        }
57044        /**
57045         * Cancels any pending timers and resets the 'timeupdate' mechanism
57046         * designed to detect that we are stalled
57047         *
57048         * @private
57049         */
57050  
57051      }, {
57052        key: 'cancelTimer_',
57053        value: function cancelTimer_() {
57054          this.consecutiveUpdates = 0;
57055  
57056          if (this.timer_) {
57057            this.logger_('cancelTimer_');
57058            clearTimeout(this.timer_);
57059          }
57060  
57061          this.timer_ = null;
57062        }
57063        /**
57064         * Fixes situations where there's a bad seek
57065         *
57066         * @return {Boolean} whether an action was taken to fix the seek
57067         * @private
57068         */
57069  
57070      }, {
57071        key: 'fixesBadSeeks_',
57072        value: function fixesBadSeeks_() {
57073          var seeking = this.tech_.seeking();
57074  
57075          if (!seeking) {
57076            return false;
57077          }
57078  
57079          var seekable = this.seekable();
57080          var currentTime = this.tech_.currentTime();
57081          var isAfterSeekableRange = this.afterSeekableWindow_(seekable, currentTime, this.media(), this.allowSeeksWithinUnsafeLiveWindow);
57082          var seekTo = void 0;
57083  
57084          if (isAfterSeekableRange) {
57085            var seekableEnd = seekable.end(seekable.length - 1); // sync to live point (if VOD, our seekable was updated and we're simply adjusting)
57086  
57087            seekTo = seekableEnd;
57088          }
57089  
57090          if (this.beforeSeekableWindow_(seekable, currentTime)) {
57091            var seekableStart = seekable.start(0); // sync to the beginning of the live window
57092            // provide a buffer of .1 seconds to handle rounding/imprecise numbers
57093  
57094            seekTo = seekableStart + SAFE_TIME_DELTA;
57095          }
57096  
57097          if (typeof seekTo !== 'undefined') {
57098            this.logger_('Trying to seek outside of seekable at time ' + currentTime + ' with ' + ('seekable range ' + printableRange(seekable) + '. Seeking to ') + (seekTo + '.'));
57099            this.tech_.setCurrentTime(seekTo);
57100            return true;
57101          }
57102  
57103          return false;
57104        }
57105        /**
57106         * Handler for situations when we determine the player is waiting.
57107         *
57108         * @private
57109         */
57110  
57111      }, {
57112        key: 'waiting_',
57113        value: function waiting_() {
57114          if (this.techWaiting_()) {
57115            return;
57116          } // All tech waiting checks failed. Use last resort correction
57117  
57118  
57119          var currentTime = this.tech_.currentTime();
57120          var buffered = this.tech_.buffered();
57121          var currentRange = findRange(buffered, currentTime); // Sometimes the player can stall for unknown reasons within a contiguous buffered
57122          // region with no indication that anything is amiss (seen in Firefox). Seeking to
57123          // currentTime is usually enough to kickstart the player. This checks that the player
57124          // is currently within a buffered region before attempting a corrective seek.
57125          // Chrome does not appear to continue `timeupdate` events after a `waiting` event
57126          // until there is ~ 3 seconds of forward buffer available. PlaybackWatcher should also
57127          // make sure there is ~3 seconds of forward buffer before taking any corrective action
57128          // to avoid triggering an `unknownwaiting` event when the network is slow.
57129  
57130          if (currentRange.length && currentTime + 3 <= currentRange.end(0)) {
57131            this.cancelTimer_();
57132            this.tech_.setCurrentTime(currentTime);
57133            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
57134  
57135            this.tech_.trigger({
57136              type: 'usage',
57137              name: 'hls-unknown-waiting'
57138            });
57139            return;
57140          }
57141        }
57142        /**
57143         * Handler for situations when the tech fires a `waiting` event
57144         *
57145         * @return {Boolean}
57146         *         True if an action (or none) was needed to correct the waiting. False if no
57147         *         checks passed
57148         * @private
57149         */
57150  
57151      }, {
57152        key: 'techWaiting_',
57153        value: function techWaiting_() {
57154          var seekable = this.seekable();
57155          var currentTime = this.tech_.currentTime();
57156  
57157          if (this.tech_.seeking() && this.fixesBadSeeks_()) {
57158            // Tech is seeking or bad seek fixed, no action needed
57159            return true;
57160          }
57161  
57162          if (this.tech_.seeking() || this.timer_ !== null) {
57163            // Tech is seeking or already waiting on another action, no action needed
57164            return true;
57165          }
57166  
57167          if (this.beforeSeekableWindow_(seekable, currentTime)) {
57168            var livePoint = seekable.end(seekable.length - 1);
57169            this.logger_('Fell out of live window at time ' + currentTime + '. Seeking to ' + ('live point (seekable end) ' + livePoint));
57170            this.cancelTimer_();
57171            this.tech_.setCurrentTime(livePoint); // live window resyncs may be useful for monitoring QoS
57172  
57173            this.tech_.trigger({
57174              type: 'usage',
57175              name: 'hls-live-resync'
57176            });
57177            return true;
57178          }
57179  
57180          var buffered = this.tech_.buffered();
57181          var nextRange = findNextRange(buffered, currentTime);
57182  
57183          if (this.videoUnderflow_(nextRange, buffered, currentTime)) {
57184            // Even though the video underflowed and was stuck in a gap, the audio overplayed
57185            // the gap, leading currentTime into a buffered range. Seeking to currentTime
57186            // allows the video to catch up to the audio position without losing any audio
57187            // (only suffering ~3 seconds of frozen video and a pause in audio playback).
57188            this.cancelTimer_();
57189            this.tech_.setCurrentTime(currentTime); // video underflow may be useful for monitoring QoS
57190  
57191            this.tech_.trigger({
57192              type: 'usage',
57193              name: 'hls-video-underflow'
57194            });
57195            return true;
57196          } // check for gap
57197  
57198  
57199          if (nextRange.length > 0) {
57200            var difference = nextRange.start(0) - currentTime;
57201            this.logger_('Stopped at ' + currentTime + ', setting timer for ' + difference + ', seeking ' + ('to ' + nextRange.start(0)));
57202            this.timer_ = setTimeout(this.skipTheGap_.bind(this), difference * 1000, currentTime);
57203            return true;
57204          } // All checks failed. Returning false to indicate failure to correct waiting
57205  
57206  
57207          return false;
57208        }
57209      }, {
57210        key: 'afterSeekableWindow_',
57211        value: function afterSeekableWindow_(seekable, currentTime, playlist) {
57212          var allowSeeksWithinUnsafeLiveWindow = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : false;
57213  
57214          if (!seekable.length) {
57215            // we can't make a solid case if there's no seekable, default to false
57216            return false;
57217          }
57218  
57219          var allowedEnd = seekable.end(seekable.length - 1) + SAFE_TIME_DELTA;
57220          var isLive = !playlist.endList;
57221  
57222          if (isLive && allowSeeksWithinUnsafeLiveWindow) {
57223            allowedEnd = seekable.end(seekable.length - 1) + playlist.targetDuration * 3;
57224          }
57225  
57226          if (currentTime > allowedEnd) {
57227            return true;
57228          }
57229  
57230          return false;
57231        }
57232      }, {
57233        key: 'beforeSeekableWindow_',
57234        value: function beforeSeekableWindow_(seekable, currentTime) {
57235          if (seekable.length && // can't fall before 0 and 0 seekable start identifies VOD stream
57236          seekable.start(0) > 0 && currentTime < seekable.start(0) - SAFE_TIME_DELTA) {
57237            return true;
57238          }
57239  
57240          return false;
57241        }
57242      }, {
57243        key: 'videoUnderflow_',
57244        value: function videoUnderflow_(nextRange, buffered, currentTime) {
57245          if (nextRange.length === 0) {
57246            // Even if there is no available next range, there is still a possibility we are
57247            // stuck in a gap due to video underflow.
57248            var gap = this.gapFromVideoUnderflow_(buffered, currentTime);
57249  
57250            if (gap) {
57251              this.logger_('Encountered a gap in video from ' + gap.start + ' to ' + gap.end + '. ' + ('Seeking to current time ' + currentTime));
57252              return true;
57253            }
57254          }
57255  
57256          return false;
57257        }
57258        /**
57259         * Timer callback. If playback still has not proceeded, then we seek
57260         * to the start of the next buffered region.
57261         *
57262         * @private
57263         */
57264  
57265      }, {
57266        key: 'skipTheGap_',
57267        value: function skipTheGap_(scheduledCurrentTime) {
57268          var buffered = this.tech_.buffered();
57269          var currentTime = this.tech_.currentTime();
57270          var nextRange = findNextRange(buffered, currentTime);
57271          this.cancelTimer_();
57272  
57273          if (nextRange.length === 0 || currentTime !== scheduledCurrentTime) {
57274            return;
57275          }
57276  
57277          this.logger_('skipTheGap_:', 'currentTime:', currentTime, 'scheduled currentTime:', scheduledCurrentTime, 'nextRange start:', nextRange.start(0)); // only seek if we still have not played
57278  
57279          this.tech_.setCurrentTime(nextRange.start(0) + TIME_FUDGE_FACTOR);
57280          this.tech_.trigger({
57281            type: 'usage',
57282            name: 'hls-gap-skip'
57283          });
57284        }
57285      }, {
57286        key: 'gapFromVideoUnderflow_',
57287        value: function gapFromVideoUnderflow_(buffered, currentTime) {
57288          // At least in Chrome, if there is a gap in the video buffer, the audio will continue
57289          // playing for ~3 seconds after the video gap starts. This is done to account for
57290          // video buffer underflow/underrun (note that this is not done when there is audio
57291          // buffer underflow/underrun -- in that case the video will stop as soon as it
57292          // encounters the gap, as audio stalls are more noticeable/jarring to a user than
57293          // video stalls). The player's time will reflect the playthrough of audio, so the
57294          // time will appear as if we are in a buffered region, even if we are stuck in a
57295          // "gap."
57296          //
57297          // Example:
57298          // video buffer:   0 => 10.1, 10.2 => 20
57299          // audio buffer:   0 => 20
57300          // overall buffer: 0 => 10.1, 10.2 => 20
57301          // current time: 13
57302          //
57303          // Chrome's video froze at 10 seconds, where the video buffer encountered the gap,
57304          // however, the audio continued playing until it reached ~3 seconds past the gap
57305          // (13 seconds), at which point it stops as well. Since current time is past the
57306          // gap, findNextRange will return no ranges.
57307          //
57308          // To check for this issue, we see if there is a gap that starts somewhere within
57309          // a 3 second range (3 seconds +/- 1 second) back from our current time.
57310          var gaps = findGaps(buffered);
57311  
57312          for (var i = 0; i < gaps.length; i++) {
57313            var start = gaps.start(i);
57314            var end = gaps.end(i); // gap is starts no more than 4 seconds back
57315  
57316            if (currentTime - start < 4 && currentTime - start > 2) {
57317              return {
57318                start: start,
57319                end: end
57320              };
57321            }
57322          }
57323  
57324          return null;
57325        }
57326      }]);
57327      return PlaybackWatcher;
57328    }();
57329  
57330    var defaultOptions = {
57331      errorInterval: 30,
57332      getSource: function getSource(next) {
57333        var tech = this.tech({
57334          IWillNotUseThisInPlugins: true
57335        });
57336        var sourceObj = tech.currentSource_;
57337        return next(sourceObj);
57338      }
57339    };
57340    /**
57341     * Main entry point for the plugin
57342     *
57343     * @param {Player} player a reference to a videojs Player instance
57344     * @param {Object} [options] an object with plugin options
57345     * @private
57346     */
57347  
57348    var initPlugin = function initPlugin(player, options) {
57349      var lastCalled = 0;
57350      var seekTo = 0;
57351      var localOptions = videojs$1.mergeOptions(defaultOptions, options);
57352      player.ready(function () {
57353        player.trigger({
57354          type: 'usage',
57355          name: 'hls-error-reload-initialized'
57356        });
57357      });
57358      /**
57359       * Player modifications to perform that must wait until `loadedmetadata`
57360       * has been triggered
57361       *
57362       * @private
57363       */
57364  
57365      var loadedMetadataHandler = function loadedMetadataHandler() {
57366        if (seekTo) {
57367          player.currentTime(seekTo);
57368        }
57369      };
57370      /**
57371       * Set the source on the player element, play, and seek if necessary
57372       *
57373       * @param {Object} sourceObj An object specifying the source url and mime-type to play
57374       * @private
57375       */
57376  
57377  
57378      var setSource = function setSource(sourceObj) {
57379        if (sourceObj === null || sourceObj === undefined) {
57380          return;
57381        }
57382  
57383        seekTo = player.duration() !== Infinity && player.currentTime() || 0;
57384        player.one('loadedmetadata', loadedMetadataHandler);
57385        player.src(sourceObj);
57386        player.trigger({
57387          type: 'usage',
57388          name: 'hls-error-reload'
57389        });
57390        player.play();
57391      };
57392      /**
57393       * Attempt to get a source from either the built-in getSource function
57394       * or a custom function provided via the options
57395       *
57396       * @private
57397       */
57398  
57399  
57400      var errorHandler = function errorHandler() {
57401        // Do not attempt to reload the source if a source-reload occurred before
57402        // 'errorInterval' time has elapsed since the last source-reload
57403        if (Date.now() - lastCalled < localOptions.errorInterval * 1000) {
57404          player.trigger({
57405            type: 'usage',
57406            name: 'hls-error-reload-canceled'
57407          });
57408          return;
57409        }
57410  
57411        if (!localOptions.getSource || typeof localOptions.getSource !== 'function') {
57412          videojs$1.log.error('ERROR: reloadSourceOnError - The option getSource must be a function!');
57413          return;
57414        }
57415  
57416        lastCalled = Date.now();
57417        return localOptions.getSource.call(player, setSource);
57418      };
57419      /**
57420       * Unbind any event handlers that were bound by the plugin
57421       *
57422       * @private
57423       */
57424  
57425  
57426      var cleanupEvents = function cleanupEvents() {
57427        player.off('loadedmetadata', loadedMetadataHandler);
57428        player.off('error', errorHandler);
57429        player.off('dispose', cleanupEvents);
57430      };
57431      /**
57432       * Cleanup before re-initializing the plugin
57433       *
57434       * @param {Object} [newOptions] an object with plugin options
57435       * @private
57436       */
57437  
57438  
57439      var reinitPlugin = function reinitPlugin(newOptions) {
57440        cleanupEvents();
57441        initPlugin(player, newOptions);
57442      };
57443  
57444      player.on('error', errorHandler);
57445      player.on('dispose', cleanupEvents); // Overwrite the plugin function so that we can correctly cleanup before
57446      // initializing the plugin
57447  
57448      player.reloadSourceOnError = reinitPlugin;
57449    };
57450    /**
57451     * Reload the source when an error is detected as long as there
57452     * wasn't an error previously within the last 30 seconds
57453     *
57454     * @param {Object} [options] an object with plugin options
57455     */
57456  
57457  
57458    var reloadSourceOnError = function reloadSourceOnError(options) {
57459      initPlugin(this, options);
57460    };
57461  
57462    var version$1 = "1.13.2";
57463    /**
57464     * @file videojs-http-streaming.js
57465     *
57466     * The main file for the HLS project.
57467     * License: https://github.com/videojs/videojs-http-streaming/blob/master/LICENSE
57468     */
57469  
57470    var Hls$1 = {
57471      PlaylistLoader: PlaylistLoader,
57472      Playlist: Playlist,
57473      Decrypter: Decrypter,
57474      AsyncStream: AsyncStream,
57475      decrypt: decrypt,
57476      utils: utils$1,
57477      STANDARD_PLAYLIST_SELECTOR: lastBandwidthSelector,
57478      INITIAL_PLAYLIST_SELECTOR: lowestBitrateCompatibleVariantSelector,
57479      comparePlaylistBandwidth: comparePlaylistBandwidth,
57480      comparePlaylistResolution: comparePlaylistResolution,
57481      xhr: xhrFactory()
57482    }; // Define getter/setters for config properites
57483  
57484    ['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) {
57485      Object.defineProperty(Hls$1, prop, {
57486        get: function get$$1() {
57487          videojs$1.log.warn('using Hls.' + prop + ' is UNSAFE be sure you know what you are doing');
57488          return Config[prop];
57489        },
57490        set: function set$$1(value) {
57491          videojs$1.log.warn('using Hls.' + prop + ' is UNSAFE be sure you know what you are doing');
57492  
57493          if (typeof value !== 'number' || value < 0) {
57494            videojs$1.log.warn('value of Hls.' + prop + ' must be greater than or equal to 0');
57495            return;
57496          }
57497  
57498          Config[prop] = value;
57499        }
57500      });
57501    });
57502    var LOCAL_STORAGE_KEY$1 = 'videojs-vhs';
57503  
57504    var simpleTypeFromSourceType = function simpleTypeFromSourceType(type) {
57505      var mpegurlRE = /^(audio|video|application)\/(x-|vnd\.apple\.)?mpegurl/i;
57506  
57507      if (mpegurlRE.test(type)) {
57508        return 'hls';
57509      }
57510  
57511      var dashRE = /^application\/dash\+xml/i;
57512  
57513      if (dashRE.test(type)) {
57514        return 'dash';
57515      }
57516  
57517      return null;
57518    };
57519    /**
57520     * Updates the selectedIndex of the QualityLevelList when a mediachange happens in hls.
57521     *
57522     * @param {QualityLevelList} qualityLevels The QualityLevelList to update.
57523     * @param {PlaylistLoader} playlistLoader PlaylistLoader containing the new media info.
57524     * @function handleHlsMediaChange
57525     */
57526  
57527  
57528    var handleHlsMediaChange = function handleHlsMediaChange(qualityLevels, playlistLoader) {
57529      var newPlaylist = playlistLoader.media();
57530      var selectedIndex = -1;
57531  
57532      for (var i = 0; i < qualityLevels.length; i++) {
57533        if (qualityLevels[i].id === newPlaylist.id) {
57534          selectedIndex = i;
57535          break;
57536        }
57537      }
57538  
57539      qualityLevels.selectedIndex_ = selectedIndex;
57540      qualityLevels.trigger({
57541        selectedIndex: selectedIndex,
57542        type: 'change'
57543      });
57544    };
57545    /**
57546     * Adds quality levels to list once playlist metadata is available
57547     *
57548     * @param {QualityLevelList} qualityLevels The QualityLevelList to attach events to.
57549     * @param {Object} hls Hls object to listen to for media events.
57550     * @function handleHlsLoadedMetadata
57551     */
57552  
57553  
57554    var handleHlsLoadedMetadata = function handleHlsLoadedMetadata(qualityLevels, hls) {
57555      hls.representations().forEach(function (rep) {
57556        qualityLevels.addQualityLevel(rep);
57557      });
57558      handleHlsMediaChange(qualityLevels, hls.playlists);
57559    }; // HLS is a source handler, not a tech. Make sure attempts to use it
57560    // as one do not cause exceptions.
57561  
57562  
57563    Hls$1.canPlaySource = function () {
57564      return videojs$1.log.warn('HLS is no longer a tech. Please remove it from ' + 'your player\'s techOrder.');
57565    };
57566  
57567    var emeKeySystems = function emeKeySystems(keySystemOptions, mainSegmentLoader, audioSegmentLoader) {
57568      if (!keySystemOptions) {
57569        return keySystemOptions;
57570      }
57571  
57572      var videoMimeType = void 0;
57573      var audioMimeType = void 0; // if there is a mimeType associated with the audioSegmentLoader, then the audio
57574      // and video mimeType and codec strings are already in the format we need to
57575      // pass with the other key systems
57576  
57577      if (audioSegmentLoader.mimeType_) {
57578        videoMimeType = mainSegmentLoader.mimeType_;
57579        audioMimeType = audioSegmentLoader.mimeType_; // if there is no audioSegmentLoader mimeType, then we have to create the
57580        // the audio and video mimeType/codec strings from information extrapolated
57581        // from the mainSegmentLoader mimeType (ex. 'video/mp4; codecs="mp4, avc1"' -->
57582        // 'video/mp4; codecs="avc1"' and 'audio/mp4; codecs="mp4"')
57583      } else {
57584        var parsedMimeType = parseContentType(mainSegmentLoader.mimeType_);
57585        var codecs = parsedMimeType.parameters.codecs.split(',');
57586        var audioCodec = void 0;
57587        var videoCodec = void 0;
57588        codecs.forEach(function (codec) {
57589          codec = codec.trim();
57590  
57591          if (isAudioCodec(codec)) {
57592            audioCodec = codec;
57593          } else if (isVideoCodec(codec)) {
57594            videoCodec = codec;
57595          }
57596        });
57597        videoMimeType = parsedMimeType.type + '; codecs="' + videoCodec + '"';
57598        audioMimeType = parsedMimeType.type.replace('video', 'audio') + '; codecs="' + audioCodec + '"';
57599      } // upsert the content types based on the selected playlist
57600  
57601  
57602      var keySystemContentTypes = {};
57603      var videoPlaylist = mainSegmentLoader.playlist_;
57604  
57605      for (var keySystem in keySystemOptions) {
57606        keySystemContentTypes[keySystem] = {
57607          audioContentType: audioMimeType,
57608          videoContentType: videoMimeType
57609        };
57610  
57611        if (videoPlaylist.contentProtection && videoPlaylist.contentProtection[keySystem] && videoPlaylist.contentProtection[keySystem].pssh) {
57612          keySystemContentTypes[keySystem].pssh = videoPlaylist.contentProtection[keySystem].pssh;
57613        } // videojs-contrib-eme accepts the option of specifying: 'com.some.cdm': 'url'
57614        // so we need to prevent overwriting the URL entirely
57615  
57616  
57617        if (typeof keySystemOptions[keySystem] === 'string') {
57618          keySystemContentTypes[keySystem].url = keySystemOptions[keySystem];
57619        }
57620      }
57621  
57622      return videojs$1.mergeOptions(keySystemOptions, keySystemContentTypes);
57623    };
57624  
57625    var setupEmeOptions = function setupEmeOptions(hlsHandler) {
57626      var mainSegmentLoader = hlsHandler.masterPlaylistController_.mainSegmentLoader_;
57627      var audioSegmentLoader = hlsHandler.masterPlaylistController_.audioSegmentLoader_;
57628      var player = videojs$1.players[hlsHandler.tech_.options_.playerId];
57629  
57630      if (player.eme) {
57631        var sourceOptions = emeKeySystems(hlsHandler.source_.keySystems, mainSegmentLoader, audioSegmentLoader);
57632  
57633        if (sourceOptions) {
57634          player.currentSource().keySystems = sourceOptions; // Works around https://bugs.chromium.org/p/chromium/issues/detail?id=895449
57635          // in non-IE11 browsers. In IE11 this is too early to initialize media keys
57636  
57637          if (!(videojs$1.browser.IE_VERSION === 11) && player.eme.initializeMediaKeys) {
57638            player.eme.initializeMediaKeys();
57639          }
57640        }
57641      }
57642    };
57643  
57644    var getVhsLocalStorage = function getVhsLocalStorage() {
57645      if (!window.localStorage) {
57646        return null;
57647      }
57648  
57649      var storedObject = window.localStorage.getItem(LOCAL_STORAGE_KEY$1);
57650  
57651      if (!storedObject) {
57652        return null;
57653      }
57654  
57655      try {
57656        return JSON.parse(storedObject);
57657      } catch (e) {
57658        // someone may have tampered with the value
57659        return null;
57660      }
57661    };
57662  
57663    var updateVhsLocalStorage = function updateVhsLocalStorage(options) {
57664      if (!window.localStorage) {
57665        return false;
57666      }
57667  
57668      var objectToStore = getVhsLocalStorage();
57669      objectToStore = objectToStore ? videojs$1.mergeOptions(objectToStore, options) : options;
57670  
57671      try {
57672        window.localStorage.setItem(LOCAL_STORAGE_KEY$1, JSON.stringify(objectToStore));
57673      } catch (e) {
57674        // Throws if storage is full (e.g., always on iOS 5+ Safari private mode, where
57675        // storage is set to 0).
57676        // https://developer.mozilla.org/en-US/docs/Web/API/Storage/setItem#Exceptions
57677        // No need to perform any operation.
57678        return false;
57679      }
57680  
57681      return objectToStore;
57682    };
57683    /**
57684     * Whether the browser has built-in HLS support.
57685     */
57686  
57687  
57688    Hls$1.supportsNativeHls = function () {
57689      var video = document.createElement('video'); // native HLS is definitely not supported if HTML5 video isn't
57690  
57691      if (!videojs$1.getTech('Html5').isSupported()) {
57692        return false;
57693      } // HLS manifests can go by many mime-types
57694  
57695  
57696      var canPlay = [// Apple santioned
57697      'application/vnd.apple.mpegurl', // Apple sanctioned for backwards compatibility
57698      'audio/mpegurl', // Very common
57699      'audio/x-mpegurl', // Very common
vendor: 20,913 bytes, lines 57700-58234
57700      'application/x-mpegurl', // Included for completeness
57701      'video/x-mpegurl', 'video/mpegurl', 'application/mpegurl'];
57702      return canPlay.some(function (canItPlay) {
57703        return /maybe|probably/i.test(video.canPlayType(canItPlay));
57704      });
57705    }();
57706  
57707    Hls$1.supportsNativeDash = function () {
57708      if (!videojs$1.getTech('Html5').isSupported()) {
57709        return false;
57710      }
57711  
57712      return /maybe|probably/i.test(document.createElement('video').canPlayType('application/dash+xml'));
57713    }();
57714  
57715    Hls$1.supportsTypeNatively = function (type) {
57716      if (type === 'hls') {
57717        return Hls$1.supportsNativeHls;
57718      }
57719  
57720      if (type === 'dash') {
57721        return Hls$1.supportsNativeDash;
57722      }
57723  
57724      return false;
57725    };
57726    /**
57727     * HLS is a source handler, not a tech. Make sure attempts to use it
57728     * as one do not cause exceptions.
57729     */
57730  
57731  
57732    Hls$1.isSupported = function () {
57733      return videojs$1.log.warn('HLS is no longer a tech. Please remove it from ' + 'your player\'s techOrder.');
57734    };
57735  
57736    var Component$1 = videojs$1.getComponent('Component');
57737    /**
57738     * The Hls Handler object, where we orchestrate all of the parts
57739     * of HLS to interact with video.js
57740     *
57741     * @class HlsHandler
57742     * @extends videojs.Component
57743     * @param {Object} source the soruce object
57744     * @param {Tech} tech the parent tech object
57745     * @param {Object} options optional and required options
57746     */
57747  
57748    var HlsHandler = function (_Component) {
57749      inherits$2(HlsHandler, _Component);
57750  
57751      function HlsHandler(source, tech, options) {
57752        classCallCheck$1(this, HlsHandler); // tech.player() is deprecated but setup a reference to HLS for
57753        // backwards-compatibility
57754  
57755        var _this = possibleConstructorReturn$1(this, (HlsHandler.__proto__ || Object.getPrototypeOf(HlsHandler)).call(this, tech, options.hls));
57756  
57757        if (tech.options_ && tech.options_.playerId) {
57758          var _player = videojs$1(tech.options_.playerId);
57759  
57760          if (!_player.hasOwnProperty('hls')) {
57761            Object.defineProperty(_player, 'hls', {
57762              get: function get$$1() {
57763                videojs$1.log.warn('player.hls is deprecated. Use player.tech().hls instead.');
57764                tech.trigger({
57765                  type: 'usage',
57766                  name: 'hls-player-access'
57767                });
57768                return _this;
57769              },
57770              configurable: true
57771            });
57772          } // Set up a reference to the HlsHandler from player.vhs. This allows users to start
57773          // migrating from player.tech_.hls... to player.vhs... for API access. Although this
57774          // isn't the most appropriate form of reference for video.js (since all APIs should
57775          // be provided through core video.js), it is a common pattern for plugins, and vhs
57776          // will act accordingly.
57777  
57778  
57779          _player.vhs = _this; // deprecated, for backwards compatibility
57780  
57781          _player.dash = _this;
57782          _this.player_ = _player;
57783        }
57784  
57785        _this.tech_ = tech;
57786        _this.source_ = source;
57787        _this.stats = {};
57788        _this.ignoreNextSeekingEvent_ = false;
57789  
57790        _this.setOptions_();
57791  
57792        if (_this.options_.overrideNative && tech.overrideNativeAudioTracks && tech.overrideNativeVideoTracks) {
57793          tech.overrideNativeAudioTracks(true);
57794          tech.overrideNativeVideoTracks(true);
57795        } else if (_this.options_.overrideNative && (tech.featuresNativeVideoTracks || tech.featuresNativeAudioTracks)) {
57796          // overriding native HLS only works if audio tracks have been emulated
57797          // error early if we're misconfigured
57798          throw new Error('Overriding native HLS requires emulated tracks. ' + 'See https://git.io/vMpjB');
57799        } // listen for fullscreenchange events for this player so that we
57800        // can adjust our quality selection quickly
57801  
57802  
57803        _this.on(document, ['fullscreenchange', 'webkitfullscreenchange', 'mozfullscreenchange', 'MSFullscreenChange'], function (event) {
57804          var fullscreenElement = document.fullscreenElement || document.webkitFullscreenElement || document.mozFullScreenElement || document.msFullscreenElement;
57805  
57806          if (fullscreenElement && fullscreenElement.contains(_this.tech_.el())) {
57807            _this.masterPlaylistController_.smoothQualityChange_();
57808          }
57809        });
57810  
57811        _this.on(_this.tech_, 'seeking', function () {
57812          if (this.ignoreNextSeekingEvent_) {
57813            this.ignoreNextSeekingEvent_ = false;
57814            return;
57815          }
57816  
57817          this.setCurrentTime(this.tech_.currentTime());
57818        });
57819  
57820        _this.on(_this.tech_, 'error', function () {
57821          if (this.masterPlaylistController_) {
57822            this.masterPlaylistController_.pauseLoading();
57823          }
57824        });
57825  
57826        _this.on(_this.tech_, 'play', _this.play);
57827  
57828        return _this;
57829      }
57830  
57831      createClass$1(HlsHandler, [{
57832        key: 'setOptions_',
57833        value: function setOptions_() {
57834          var _this2 = this; // defaults
57835  
57836  
57837          this.options_.withCredentials = this.options_.withCredentials || false;
57838          this.options_.handleManifestRedirects = this.options_.handleManifestRedirects || false;
57839          this.options_.limitRenditionByPlayerDimensions = this.options_.limitRenditionByPlayerDimensions === false ? false : true;
57840          this.options_.useDevicePixelRatio = this.options_.useDevicePixelRatio || false;
57841          this.options_.smoothQualityChange = this.options_.smoothQualityChange || false;
57842          this.options_.useBandwidthFromLocalStorage = typeof this.source_.useBandwidthFromLocalStorage !== 'undefined' ? this.source_.useBandwidthFromLocalStorage : this.options_.useBandwidthFromLocalStorage || false;
57843          this.options_.customTagParsers = this.options_.customTagParsers || [];
57844          this.options_.customTagMappers = this.options_.customTagMappers || [];
57845          this.options_.cacheEncryptionKeys = this.options_.cacheEncryptionKeys || false;
57846  
57847          if (typeof this.options_.blacklistDuration !== 'number') {
57848            this.options_.blacklistDuration = 5 * 60;
57849          }
57850  
57851          if (typeof this.options_.bandwidth !== 'number') {
57852            if (this.options_.useBandwidthFromLocalStorage) {
57853              var storedObject = getVhsLocalStorage();
57854  
57855              if (storedObject && storedObject.bandwidth) {
57856                this.options_.bandwidth = storedObject.bandwidth;
57857                this.tech_.trigger({
57858                  type: 'usage',
57859                  name: 'hls-bandwidth-from-local-storage'
57860                });
57861              }
57862  
57863              if (storedObject && storedObject.throughput) {
57864                this.options_.throughput = storedObject.throughput;
57865                this.tech_.trigger({
57866                  type: 'usage',
57867                  name: 'hls-throughput-from-local-storage'
57868                });
57869              }
57870            }
57871          } // if bandwidth was not set by options or pulled from local storage, start playlist
57872          // selection at a reasonable bandwidth
57873  
57874  
57875          if (typeof this.options_.bandwidth !== 'number') {
57876            this.options_.bandwidth = Config.INITIAL_BANDWIDTH;
57877          } // If the bandwidth number is unchanged from the initial setting
57878          // then this takes precedence over the enableLowInitialPlaylist option
57879  
57880  
57881          this.options_.enableLowInitialPlaylist = this.options_.enableLowInitialPlaylist && this.options_.bandwidth === Config.INITIAL_BANDWIDTH; // grab options passed to player.src
57882  
57883          ['withCredentials', 'useDevicePixelRatio', 'limitRenditionByPlayerDimensions', 'bandwidth', 'smoothQualityChange', 'customTagParsers', 'customTagMappers', 'handleManifestRedirects', 'cacheEncryptionKeys'].forEach(function (option) {
57884            if (typeof _this2.source_[option] !== 'undefined') {
57885              _this2.options_[option] = _this2.source_[option];
57886            }
57887          });
57888          this.limitRenditionByPlayerDimensions = this.options_.limitRenditionByPlayerDimensions;
57889          this.useDevicePixelRatio = this.options_.useDevicePixelRatio;
57890        }
57891        /**
57892         * called when player.src gets called, handle a new source
57893         *
57894         * @param {Object} src the source object to handle
57895         */
57896  
57897      }, {
57898        key: 'src',
57899        value: function src(_src, type) {
57900          var _this3 = this; // do nothing if the src is falsey
57901  
57902  
57903          if (!_src) {
57904            return;
57905          }
57906  
57907          this.setOptions_(); // add master playlist controller options
57908  
57909          this.options_.url = this.source_.src;
57910          this.options_.tech = this.tech_;
57911          this.options_.externHls = Hls$1;
57912          this.options_.sourceType = simpleTypeFromSourceType(type); // Whenever we seek internally, we should update the tech
57913  
57914          this.options_.seekTo = function (time) {
57915            _this3.tech_.setCurrentTime(time);
57916          };
57917  
57918          this.masterPlaylistController_ = new MasterPlaylistController(this.options_);
57919          this.playbackWatcher_ = new PlaybackWatcher(videojs$1.mergeOptions(this.options_, {
57920            seekable: function seekable$$1() {
57921              return _this3.seekable();
57922            },
57923            media: function media() {
57924              return _this3.masterPlaylistController_.media();
57925            }
57926          }));
57927          this.masterPlaylistController_.on('error', function () {
57928            var player = videojs$1.players[_this3.tech_.options_.playerId];
57929            player.error(_this3.masterPlaylistController_.error);
57930          }); // `this` in selectPlaylist should be the HlsHandler for backwards
57931          // compatibility with < v2
57932  
57933          this.masterPlaylistController_.selectPlaylist = this.selectPlaylist ? this.selectPlaylist.bind(this) : Hls$1.STANDARD_PLAYLIST_SELECTOR.bind(this);
57934          this.masterPlaylistController_.selectInitialPlaylist = Hls$1.INITIAL_PLAYLIST_SELECTOR.bind(this); // re-expose some internal objects for backwards compatibility with < v2
57935  
57936          this.playlists = this.masterPlaylistController_.masterPlaylistLoader_;
57937          this.mediaSource = this.masterPlaylistController_.mediaSource; // Proxy assignment of some properties to the master playlist
57938          // controller. Using a custom property for backwards compatibility
57939          // with < v2
57940  
57941          Object.defineProperties(this, {
57942            selectPlaylist: {
57943              get: function get$$1() {
57944                return this.masterPlaylistController_.selectPlaylist;
57945              },
57946              set: function set$$1(selectPlaylist) {
57947                this.masterPlaylistController_.selectPlaylist = selectPlaylist.bind(this);
57948              }
57949            },
57950            throughput: {
57951              get: function get$$1() {
57952                return this.masterPlaylistController_.mainSegmentLoader_.throughput.rate;
57953              },
57954              set: function set$$1(throughput) {
57955                this.masterPlaylistController_.mainSegmentLoader_.throughput.rate = throughput; // By setting `count` to 1 the throughput value becomes the starting value
57956                // for the cumulative average
57957  
57958                this.masterPlaylistController_.mainSegmentLoader_.throughput.count = 1;
57959              }
57960            },
57961            bandwidth: {
57962              get: function get$$1() {
57963                return this.masterPlaylistController_.mainSegmentLoader_.bandwidth;
57964              },
57965              set: function set$$1(bandwidth) {
57966                this.masterPlaylistController_.mainSegmentLoader_.bandwidth = bandwidth; // setting the bandwidth manually resets the throughput counter
57967                // `count` is set to zero that current value of `rate` isn't included
57968                // in the cumulative average
57969  
57970                this.masterPlaylistController_.mainSegmentLoader_.throughput = {
57971                  rate: 0,
57972                  count: 0
57973                };
57974              }
57975            },
57976  
57977            /**
57978             * `systemBandwidth` is a combination of two serial processes bit-rates. The first
57979             * is the network bitrate provided by `bandwidth` and the second is the bitrate of
57980             * the entire process after that - decryption, transmuxing, and appending - provided
57981             * by `throughput`.
57982             *
57983             * Since the two process are serial, the overall system bandwidth is given by:
57984             *   sysBandwidth = 1 / (1 / bandwidth + 1 / throughput)
57985             */
57986            systemBandwidth: {
57987              get: function get$$1() {
57988                var invBandwidth = 1 / (this.bandwidth || 1);
57989                var invThroughput = void 0;
57990  
57991                if (this.throughput > 0) {
57992                  invThroughput = 1 / this.throughput;
57993                } else {
57994                  invThroughput = 0;
57995                }
57996  
57997                var systemBitrate = Math.floor(1 / (invBandwidth + invThroughput));
57998                return systemBitrate;
57999              },
58000              set: function set$$1() {
58001                videojs$1.log.error('The "systemBandwidth" property is read-only');
58002              }
58003            }
58004          });
58005  
58006          if (this.options_.bandwidth) {
58007            this.bandwidth = this.options_.bandwidth;
58008          }
58009  
58010          if (this.options_.throughput) {
58011            this.throughput = this.options_.throughput;
58012          }
58013  
58014          Object.defineProperties(this.stats, {
58015            bandwidth: {
58016              get: function get$$1() {
58017                return _this3.bandwidth || 0;
58018              },
58019              enumerable: true
58020            },
58021            mediaRequests: {
58022              get: function get$$1() {
58023                return _this3.masterPlaylistController_.mediaRequests_() || 0;
58024              },
58025              enumerable: true
58026            },
58027            mediaRequestsAborted: {
58028              get: function get$$1() {
58029                return _this3.masterPlaylistController_.mediaRequestsAborted_() || 0;
58030              },
58031              enumerable: true
58032            },
58033            mediaRequestsTimedout: {
58034              get: function get$$1() {
58035                return _this3.masterPlaylistController_.mediaRequestsTimedout_() || 0;
58036              },
58037              enumerable: true
58038            },
58039            mediaRequestsErrored: {
58040              get: function get$$1() {
58041                return _this3.masterPlaylistController_.mediaRequestsErrored_() || 0;
58042              },
58043              enumerable: true
58044            },
58045            mediaTransferDuration: {
58046              get: function get$$1() {
58047                return _this3.masterPlaylistController_.mediaTransferDuration_() || 0;
58048              },
58049              enumerable: true
58050            },
58051            mediaBytesTransferred: {
58052              get: function get$$1() {
58053                return _this3.masterPlaylistController_.mediaBytesTransferred_() || 0;
58054              },
58055              enumerable: true
58056            },
58057            mediaSecondsLoaded: {
58058              get: function get$$1() {
58059                return _this3.masterPlaylistController_.mediaSecondsLoaded_() || 0;
58060              },
58061              enumerable: true
58062            },
58063            buffered: {
58064              get: function get$$1() {
58065                return timeRangesToArray(_this3.tech_.buffered());
58066              },
58067              enumerable: true
58068            },
58069            currentTime: {
58070              get: function get$$1() {
58071                return _this3.tech_.currentTime();
58072              },
58073              enumerable: true
58074            },
58075            currentSource: {
58076              get: function get$$1() {
58077                return _this3.tech_.currentSource_;
58078              },
58079              enumerable: true
58080            },
58081            currentTech: {
58082              get: function get$$1() {
58083                return _this3.tech_.name_;
58084              },
58085              enumerable: true
58086            },
58087            duration: {
58088              get: function get$$1() {
58089                return _this3.tech_.duration();
58090              },
58091              enumerable: true
58092            },
58093            master: {
58094              get: function get$$1() {
58095                return _this3.playlists.master;
58096              },
58097              enumerable: true
58098            },
58099            playerDimensions: {
58100              get: function get$$1() {
58101                return _this3.tech_.currentDimensions();
58102              },
58103              enumerable: true
58104            },
58105            seekable: {
58106              get: function get$$1() {
58107                return timeRangesToArray(_this3.tech_.seekable());
58108              },
58109              enumerable: true
58110            },
58111            timestamp: {
58112              get: function get$$1() {
58113                return Date.now();
58114              },
58115              enumerable: true
58116            },
58117            videoPlaybackQuality: {
58118              get: function get$$1() {
58119                return _this3.tech_.getVideoPlaybackQuality();
58120              },
58121              enumerable: true
58122            }
58123          });
58124          this.tech_.one('canplay', this.masterPlaylistController_.setupFirstPlay.bind(this.masterPlaylistController_));
58125          this.tech_.on('bandwidthupdate', function () {
58126            if (_this3.options_.useBandwidthFromLocalStorage) {
58127              updateVhsLocalStorage({
58128                bandwidth: _this3.bandwidth,
58129                throughput: Math.round(_this3.throughput)
58130              });
58131            }
58132          });
58133          this.masterPlaylistController_.on('selectedinitialmedia', function () {
58134            // Add the manual rendition mix-in to HlsHandler
58135            renditionSelectionMixin(_this3);
58136            setupEmeOptions(_this3);
58137          }); // the bandwidth of the primary segment loader is our best
58138          // estimate of overall bandwidth
58139  
58140          this.on(this.masterPlaylistController_, 'progress', function () {
58141            this.tech_.trigger('progress');
58142          }); // In the live case, we need to ignore the very first `seeking` event since
58143          // that will be the result of the seek-to-live behavior
58144  
58145          this.on(this.masterPlaylistController_, 'firstplay', function () {
58146            this.ignoreNextSeekingEvent_ = true;
58147          });
58148          this.setupQualityLevels_(); // do nothing if the tech has been disposed already
58149          // this can occur if someone sets the src in player.ready(), for instance
58150  
58151          if (!this.tech_.el()) {
58152            return;
58153          }
58154  
58155          this.tech_.src(videojs$1.URL.createObjectURL(this.masterPlaylistController_.mediaSource));
58156        }
58157        /**
58158         * Initializes the quality levels and sets listeners to update them.
58159         *
58160         * @method setupQualityLevels_
58161         * @private
58162         */
58163  
58164      }, {
58165        key: 'setupQualityLevels_',
58166        value: function setupQualityLevels_() {
58167          var _this4 = this;
58168  
58169          var player = videojs$1.players[this.tech_.options_.playerId]; // if there isn't a player or there isn't a qualityLevels plugin
58170          // or qualityLevels_ listeners have already been setup, do nothing.
58171  
58172          if (!player || !player.qualityLevels || this.qualityLevels_) {
58173            return;
58174          }
58175  
58176          this.qualityLevels_ = player.qualityLevels();
58177          this.masterPlaylistController_.on('selectedinitialmedia', function () {
58178            handleHlsLoadedMetadata(_this4.qualityLevels_, _this4);
58179          });
58180          this.playlists.on('mediachange', function () {
58181            handleHlsMediaChange(_this4.qualityLevels_, _this4.playlists);
58182          });
58183        }
58184        /**
58185         * Begin playing the video.
58186         */
58187  
58188      }, {
58189        key: 'play',
58190        value: function play() {
58191          this.masterPlaylistController_.play();
58192        }
58193        /**
58194         * a wrapper around the function in MasterPlaylistController
58195         */
58196  
58197      }, {
58198        key: 'setCurrentTime',
58199        value: function setCurrentTime(currentTime) {
58200          this.masterPlaylistController_.setCurrentTime(currentTime);
58201        }
58202        /**
58203         * a wrapper around the function in MasterPlaylistController
58204         */
58205  
58206      }, {
58207        key: 'duration',
58208        value: function duration$$1() {
58209          return this.masterPlaylistController_.duration();
58210        }
58211        /**
58212         * a wrapper around the function in MasterPlaylistController
58213         */
58214  
58215      }, {
58216        key: 'seekable',
58217        value: function seekable$$1() {
58218          return this.masterPlaylistController_.seekable();
58219        }
58220        /**
58221         * Abort all outstanding work and cleanup.
58222         */
58223  
58224      }, {
58225        key: 'dispose',
58226        value: function dispose() {
58227          if (this.playbackWatcher_) {
58228            this.playbackWatcher_.dispose();
58229          }
58230  
58231          if (this.masterPlaylistController_) {
58232            this.masterPlaylistController_.dispose();
58233          }
58234  
58235          if (this.qualityLevels_) {
58236            this.qualityLevels_.dispose();
58237          }
58238  
58239          if (this.player_) {
58240            delete this.player_.vhs;
58241            delete this.player_.dash;
58242            delete this.player_.hls;
58243          }
58244  
58245          if (this.tech_ && this.tech_.hls) {
58246            delete this.tech_.hls;
58247          }
58248  
58249          get$1(HlsHandler.prototype.__proto__ || Object.getPrototypeOf(HlsHandler.prototype), 'dispose', this).call(this);
58250        }
58251      }, {
58252        key: 'convertToProgramTime',
58253        value: function convertToProgramTime(time, callback) {
58254          return getProgramTime({
58255            playlist: this.masterPlaylistController_.media(),
58256            time: time,
58257            callback: callback
58258          });
58259        } // the player must be playing before calling this
58260  
58261      }, {
58262        key: 'seekToProgramTime',
58263        value: function seekToProgramTime$$1(programTime, callback) {
58264          var pauseAfterSeek = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : true;
58265          var retryCount = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : 2;
58266          return seekToProgramTime({
58267            programTime: programTime,
58268            playlist: this.masterPlaylistController_.media(),
58269            retryCount: retryCount,
58270            pauseAfterSeek: pauseAfterSeek,
58271            seekTo: this.options_.seekTo,
58272            tech: this.options_.tech,
58273            callback: callback
58274          });
58275        }
58276      }]);
58277      return HlsHandler;
58278    }(Component$1);
58279    /**
58280     * The Source Handler object, which informs video.js what additional
58281     * MIME types are supported and sets up playback. It is registered
58282     * automatically to the appropriate tech based on the capabilities of
58283     * the browser it is running in. It is not necessary to use or modify
58284     * this object in normal usage.
58285     */
58286  
58287  
58288    var HlsSourceHandler = {
58289      name: 'videojs-http-streaming',
58290      VERSION: version$1,
58291      canHandleSource: function canHandleSource(srcObj) {
58292        var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
58293        var localOptions = videojs$1.mergeOptions(videojs$1.options, options);
58294        return HlsSourceHandler.canPlayType(srcObj.type, localOptions);
58295      },
58296      handleSource: function handleSource(source, tech) {
58297        var options = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
58298        var localOptions = videojs$1.mergeOptions(videojs$1.options, options);
58299        tech.hls = new HlsHandler(source, tech, localOptions);
58300        tech.hls.xhr = xhrFactory();
58301        tech.hls.src(source.src, source.type);
58302        return tech.hls;
58303      },
58304      canPlayType: function canPlayType(type) {
58305        var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
58306  
58307        var _videojs$mergeOptions = videojs$1.mergeOptions(videojs$1.options, options),
58308            overrideNative = _videojs$mergeOptions.hls.overrideNative;
58309  
58310        var supportedType = simpleTypeFromSourceType(type);
58311        var canUseMsePlayback = supportedType && (!Hls$1.supportsTypeNatively(supportedType) || overrideNative);
58312        return canUseMsePlayback ? 'maybe' : '';
58313      }
58314    };
58315  
58316    if (typeof videojs$1.MediaSource === 'undefined' || typeof videojs$1.URL === 'undefined') {
58317      videojs$1.MediaSource = MediaSource;
58318      videojs$1.URL = URL$1;
58319    } // register source handlers with the appropriate techs
58320  
58321  
58322    if (MediaSource.supportsNativeMediaSources()) {
58323      videojs$1.getTech('Html5').registerSourceHandler(HlsSourceHandler, 0);
58324    }
58325  
58326    videojs$1.HlsHandler = HlsHandler;
58327    videojs$1.HlsSourceHandler = HlsSourceHandler;
58328    videojs$1.Hls = Hls$1;
58329  
58330    if (!videojs$1.use) {
58331      videojs$1.registerComponent('Hls', Hls$1);
58332    }
58333  
58334    videojs$1.options.hls = videojs$1.options.hls || {};
58335  
58336    if (videojs$1.registerPlugin) {
58337      videojs$1.registerPlugin('reloadSourceOnError', reloadSourceOnError);
58338    } else {
58339      videojs$1.plugin('reloadSourceOnError', reloadSourceOnError);
58340    }
58341  
58342    return videojs$1;
58343  
58344  })));
58345  
58346  !function(){!function(a){var b=a&&a.videojs;b&&(b.CDN_VERSION="7.8.4")}(window)}();

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.