PageSourceSearch

https://thesra.co/wp-content/plugins/popupbuilder-video/public/js/Player.js?ver=4.4.3

js thesra.co collected 2026-10-09 17:07:05 UTC 1,854,590 bytes, 56,314 lines download raw bytes

1/**
2 * @license
3 * Video.js 7.6.5 <http://videojs.com/>
4 * Copyright Brightcove, Inc. <https://www.brightcove.com/>
5 * Available under Apache License Version 2.0
6 * <https://github.com/videojs/video.js/blob/master/LICENSE>
7 *
8 * Includes vtt.js <https://github.com/mozilla/vtt.js>
9 * Available under Apache License Version 2.0
10 * <https://github.com/mozilla/vtt.js/blob/master/LICENSE>
11 */
12
13(function (global, factory) {
14  typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory(require('global/window'), require('global/document')) :
15  typeof define === 'function' && define.amd ? define(['global/window', 'global/document'], factory) :
16  (global = global || self, global.videojs = factory(global.window, global.document));
17}(this, function (window$1, document) {
18  window$1 = window$1 && window$1.hasOwnProperty('default') ? window$1['default'] : window$1;
19  document = document && document.hasOwnProperty('default') ? document['default'] : document;
20
21  var version = "7.6.5";
22
23  /**
24   * @file create-logger.js
25   * @module create-logger
26   */
27
28  var history = [];
29  /**
30   * Log messages to the console and history based on the type of message
31   *
32   * @private
33   * @param  {string} type
34   *         The name of the console method to use.
35   *
36   * @param  {Array} args
37   *         The arguments to be passed to the matching console method.
38   */
39
40  var LogByTypeFactory = function LogByTypeFactory(name, log) {
41    return function (type, level, args) {
42      var lvl = log.levels[level];
43      var lvlRegExp = new RegExp("^(" + lvl + ")$");
44
45      if (type !== 'log') {
46        // Add the type to the front of the message when it's not "log".
47        args.unshift(type.toUpperCase() + ':');
48      } // Add console prefix after adding to history.
49
50
51      args.unshift(name + ':'); // Add a clone of the args at this point to history.
52
53      if (history) {
54        history.push([].concat(args));
55      } // If there's no console then don't try to output messages, but they will
56      // still be stored in history.
57
58
59      if (!window$1.console) {
60        return;
61      } // Was setting these once outside of this function, but containing them
62      // in the function makes it easier to test cases where console doesn't exist
63      // when the module is executed.
64
65
66      var fn = window$1.console[type];
67
68      if (!fn && type === 'debug') {
69        // Certain browsers don't have support for console.debug. For those, we
70        // should default to the closest comparable log.
71        fn = window$1.console.info || window$1.console.log;
72      } // Bail out if there's no console or if this type is not allowed by the
73      // current logging level.
74
75
76      if (!fn || !lvl || !lvlRegExp.test(type)) {
77        return;
78      }
79
80      fn[Array.isArray(args) ? 'apply' : 'call'](window$1.console, args);
81    };
82  };
83
84  function createLogger(name) {
85    // This is the private tracking variable for logging level.
86    var level = 'info'; // the curried logByType bound to the specific log and history
87
88    var logByType;
89    /**
90     * Logs plain debug messages. Similar to `console.log`.
91     *
92     * Due to [limitations](https://github.com/jsdoc3/jsdoc/issues/955#issuecomment-313829149)
93     * of our JSDoc template, we cannot properly document this as both a function
94     * and a namespace, so its function signature is documented here.
95     *
96     * #### Arguments
97     * ##### *args
98     * Mixed[]
99     *
100     * Any combination of values that could be passed to `console.log()`.
101     *
102     * #### Return Value
103     *
104     * `undefined`
105     *
106     * @namespace
107     * @param    {Mixed[]} args
108     *           One or more messages or objects that should be logged.
109     */
110
111    var log = function log() {
112      for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) {
113        args[_key] = arguments[_key];
114      }
115
116      logByType('log', level, args);
117    }; // This is the logByType helper that the logging methods below use
118
119
120    logByType = LogByTypeFactory(name, log);
121    /**
122     * Create a new sublogger which chains the old name to the new name.
123     *
124     * For example, doing `videojs.log.createLogger('player')` and then using that logger will log the following:
125     * ```js
126     *  mylogger('foo');
127     *  // > VIDEOJS: player: foo
128     * ```
129     *
130     * @param {string} name
131     *        The name to add call the new logger
132     * @return {Object}
133     */
134
vendor: 23,607 bytes, lines 135-972
135    log.createLogger = function (subname) {
136      return createLogger(name + ': ' + subname);
137    };
138    /**
139     * Enumeration of available logging levels, where the keys are the level names
140     * and the values are `|`-separated strings containing logging methods allowed
141     * in that logging level. These strings are used to create a regular expression
142     * matching the function name being called.
143     *
144     * Levels provided by Video.js are:
145     *
146     * - `off`: Matches no calls. Any value that can be cast to `false` will have
147     *   this effect. The most restrictive.
148     * - `all`: Matches only Video.js-provided functions (`debug`, `log`,
149     *   `log.warn`, and `log.error`).
150     * - `debug`: Matches `log.debug`, `log`, `log.warn`, and `log.error` calls.
151     * - `info` (default): Matches `log`, `log.warn`, and `log.error` calls.
152     * - `warn`: Matches `log.warn` and `log.error` calls.
153     * - `error`: Matches only `log.error` calls.
154     *
155     * @type {Object}
156     */
157
158
159    log.levels = {
160      all: 'debug|log|warn|error',
161      off: '',
162      debug: 'debug|log|warn|error',
163      info: 'log|warn|error',
164      warn: 'warn|error',
165      error: 'error',
166      DEFAULT: level
167    };
168    /**
169     * Get or set the current logging level.
170     *
171     * If a string matching a key from {@link module:log.levels} is provided, acts
172     * as a setter.
173     *
174     * @param  {string} [lvl]
175     *         Pass a valid level to set a new logging level.
176     *
177     * @return {string}
178     *         The current logging level.
179     */
180
181    log.level = function (lvl) {
182      if (typeof lvl === 'string') {
183        if (!log.levels.hasOwnProperty(lvl)) {
184          throw new Error("\"" + lvl + "\" in not a valid log level");
185        }
186
187        level = lvl;
188      }
189
190      return level;
191    };
192    /**
193     * Returns an array containing everything that has been logged to the history.
194     *
195     * This array is a shallow clone of the internal history record. However, its
196     * contents are _not_ cloned; so, mutating objects inside this array will
197     * mutate them in history.
198     *
199     * @return {Array}
200     */
201
202
203    log.history = function () {
204      return history ? [].concat(history) : [];
205    };
206    /**
207     * Allows you to filter the history by the given logger name
208     *
209     * @param {string} fname
210     *        The name to filter by
211     *
212     * @return {Array}
213     *         The filtered list to return
214     */
215
216
217    log.history.filter = function (fname) {
218      return (history || []).filter(function (historyItem) {
219        // if the first item in each historyItem includes `fname`, then it's a match
220        return new RegExp(".*" + fname + ".*").test(historyItem[0]);
221      });
222    };
223    /**
224     * Clears the internal history tracking, but does not prevent further history
225     * tracking.
226     */
227
228
229    log.history.clear = function () {
230      if (history) {
231        history.length = 0;
232      }
233    };
234    /**
235     * Disable history tracking if it is currently enabled.
236     */
237
238
239    log.history.disable = function () {
240      if (history !== null) {
241        history.length = 0;
242        history = null;
243      }
244    };
245    /**
246     * Enable history tracking if it is currently disabled.
247     */
248
249
250    log.history.enable = function () {
251      if (history === null) {
252        history = [];
253      }
254    };
255    /**
256     * Logs error messages. Similar to `console.error`.
257     *
258     * @param {Mixed[]} args
259     *        One or more messages or objects that should be logged as an error
260     */
261
262
263    log.error = function () {
264      for (var _len2 = arguments.length, args = new Array(_len2), _key2 = 0; _key2 < _len2; _key2++) {
265        args[_key2] = arguments[_key2];
266      }
267
268      return logByType('error', level, args);
269    };
270    /**
271     * Logs warning messages. Similar to `console.warn`.
272     *
273     * @param {Mixed[]} args
274     *        One or more messages or objects that should be logged as a warning.
275     */
276
277
278    log.warn = function () {
279      for (var _len3 = arguments.length, args = new Array(_len3), _key3 = 0; _key3 < _len3; _key3++) {
280        args[_key3] = arguments[_key3];
281      }
282
283      return logByType('warn', level, args);
284    };
285    /**
286     * Logs debug messages. Similar to `console.debug`, but may also act as a comparable
287     * log if `console.debug` is not available
288     *
289     * @param {Mixed[]} args
290     *        One or more messages or objects that should be logged as debug.
291     */
292
293
294    log.debug = function () {
295      for (var _len4 = arguments.length, args = new Array(_len4), _key4 = 0; _key4 < _len4; _key4++) {
296        args[_key4] = arguments[_key4];
297      }
298
299      return logByType('debug', level, args);
300    };
301
302    return log;
303  }
304
305  /**
306   * @file log.js
307   * @module log
308   */
309  var log = createLogger('VIDEOJS');
310  var createLogger$1 = log.createLogger;
311
312  /**
313   * @file obj.js
314   * @module obj
315   */
316
317  /**
318   * @callback obj:EachCallback
319   *
320   * @param {Mixed} value
321   *        The current key for the object that is being iterated over.
322   *
323   * @param {string} key
324   *        The current key-value for object that is being iterated over
325   */
326
327  /**
328   * @callback obj:ReduceCallback
329   *
330   * @param {Mixed} accum
331   *        The value that is accumulating over the reduce loop.
332   *
333   * @param {Mixed} value
334   *        The current key for the object that is being iterated over.
335   *
336   * @param {string} key
337   *        The current key-value for object that is being iterated over
338   *
339   * @return {Mixed}
340   *         The new accumulated value.
341   */
342  var toString = Object.prototype.toString;
343  /**
344   * Get the keys of an Object
345   *
346   * @param {Object}
347   *        The Object to get the keys from
348   *
349   * @return {string[]}
350   *         An array of the keys from the object. Returns an empty array if the
351   *         object passed in was invalid or had no keys.
352   *
353   * @private
354   */
355
356  var keys = function keys(object) {
357    return isObject(object) ? Object.keys(object) : [];
358  };
359  /**
360   * Array-like iteration for objects.
361   *
362   * @param {Object} object
363   *        The object to iterate over
364   *
365   * @param {obj:EachCallback} fn
366   *        The callback function which is called for each key in the object.
367   */
368
369
370  function each(object, fn) {
371    keys(object).forEach(function (key) {
372      return fn(object[key], key);
373    });
374  }
375  /**
376   * Array-like reduce for objects.
377   *
378   * @param {Object} object
379   *        The Object that you want to reduce.
380   *
381   * @param {Function} fn
382   *         A callback function which is called for each key in the object. It
383   *         receives the accumulated value and the per-iteration value and key
384   *         as arguments.
385   *
386   * @param {Mixed} [initial = 0]
387   *        Starting value
388   *
389   * @return {Mixed}
390   *         The final accumulated value.
391   */
392
393  function reduce(object, fn, initial) {
394    if (initial === void 0) {
395      initial = 0;
396    }
397
398    return keys(object).reduce(function (accum, key) {
399      return fn(accum, object[key], key);
400    }, initial);
401  }
402  /**
403   * Object.assign-style object shallow merge/extend.
404   *
405   * @param  {Object} target
406   * @param  {Object} ...sources
407   * @return {Object}
408   */
409
410  function assign(target) {
411    for (var _len = arguments.length, sources = new Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {
412      sources[_key - 1] = arguments[_key];
413    }
414
415    if (Object.assign) {
416      return Object.assign.apply(Object, [target].concat(sources));
417    }
418
419    sources.forEach(function (source) {
420      if (!source) {
421        return;
422      }
423
424      each(source, function (value, key) {
425        target[key] = value;
426      });
427    });
428    return target;
429  }
430  /**
431   * Returns whether a value is an object of any kind - including DOM nodes,
432   * arrays, regular expressions, etc. Not functions, though.
433   *
434   * This avoids the gotcha where using `typeof` on a `null` value
435   * results in `'object'`.
436   *
437   * @param  {Object} value
438   * @return {boolean}
439   */
440
441  function isObject(value) {
442    return !!value && typeof value === 'object';
443  }
444  /**
445   * Returns whether an object appears to be a "plain" object - that is, a
446   * direct instance of `Object`.
447   *
448   * @param  {Object} value
449   * @return {boolean}
450   */
451
452  function isPlain(value) {
453    return isObject(value) && toString.call(value) === '[object Object]' && value.constructor === Object;
454  }
455
456  /**
457   * @file computed-style.js
458   * @module computed-style
459   */
460  /**
461   * A safe getComputedStyle.
462   *
463   * This is needed because in Firefox, if the player is loaded in an iframe with
464   * `display:none`, then `getComputedStyle` returns `null`, so, we do a
465   * null-check to make sure that the player doesn't break in these cases.
466   *
467   * @function
468   * @param    {Element} el
469   *           The element you want the computed style of
470   *
471   * @param    {string} prop
472   *           The property name you want
473   *
474   * @see      https://bugzilla.mozilla.org/show_bug.cgi?id=548397
475   */
476
477  function computedStyle(el, prop) {
478    if (!el || !prop) {
479      return '';
480    }
481
482    if (typeof window$1.getComputedStyle === 'function') {
483      var computedStyleValue = window$1.getComputedStyle(el);
484      return computedStyleValue ? computedStyleValue.getPropertyValue(prop) || computedStyleValue[prop] : '';
485    }
486
487    return '';
488  }
489
490  /**
491   * @file dom.js
492   * @module dom
493   */
494  /**
495   * Detect if a value is a string with any non-whitespace characters.
496   *
497   * @private
498   * @param  {string} str
499   *         The string to check
500   *
501   * @return {boolean}
502   *         Will be `true` if the string is non-blank, `false` otherwise.
503   *
504   */
505
506  function isNonBlankString(str) {
507    return typeof str === 'string' && /\S/.test(str);
508  }
509  /**
510   * Throws an error if the passed string has whitespace. This is used by
511   * class methods to be relatively consistent with the classList API.
512   *
513   * @private
514   * @param  {string} str
515   *         The string to check for whitespace.
516   *
517   * @throws {Error}
518   *         Throws an error if there is whitespace in the string.
519   */
520
521
522  function throwIfWhitespace(str) {
523    if (/\s/.test(str)) {
524      throw new Error('class has illegal whitespace characters');
525    }
526  }
527  /**
528   * Produce a regular expression for matching a className within an elements className.
529   *
530   * @private
531   * @param  {string} className
532   *         The className to generate the RegExp for.
533   *
534   * @return {RegExp}
535   *         The RegExp that will check for a specific `className` in an elements
536   *         className.
537   */
538
539
540  function classRegExp(className) {
541    return new RegExp('(^|\\s)' + className + '($|\\s)');
542  }
543  /**
544   * Whether the current DOM interface appears to be real (i.e. not simulated).
545   *
546   * @return {boolean}
547   *         Will be `true` if the DOM appears to be real, `false` otherwise.
548   */
549
550
551  function isReal() {
552    // Both document and window will never be undefined thanks to `global`.
553    return document === window$1.document;
554  }
555  /**
556   * Determines, via duck typing, whether or not a value is a DOM element.
557   *
558   * @param  {Mixed} value
559   *         The value to check.
560   *
561   * @return {boolean}
562   *         Will be `true` if the value is a DOM element, `false` otherwise.
563   */
564
565  function isEl(value) {
566    return isObject(value) && value.nodeType === 1;
567  }
568  /**
569   * Determines if the current DOM is embedded in an iframe.
570   *
571   * @return {boolean}
572   *         Will be `true` if the DOM is embedded in an iframe, `false`
573   *         otherwise.
574   */
575
576  function isInFrame() {
577    // We need a try/catch here because Safari will throw errors when attempting
578    // to get either `parent` or `self`
579    try {
580      return window$1.parent !== window$1.self;
581    } catch (x) {
582      return true;
583    }
584  }
585  /**
586   * Creates functions to query the DOM using a given method.
587   *
588   * @private
589   * @param   {string} method
590   *          The method to create the query with.
591   *
592   * @return  {Function}
593   *          The query method
594   */
595
596  function createQuerier(method) {
597    return function (selector, context) {
598      if (!isNonBlankString(selector)) {
599        return document[method](null);
600      }
601
602      if (isNonBlankString(context)) {
603        context = document.querySelector(context);
604      }
605
606      var ctx = isEl(context) ? context : document;
607      return ctx[method] && ctx[method](selector);
608    };
609  }
610  /**
611   * Creates an element and applies properties, attributes, and inserts content.
612   *
613   * @param  {string} [tagName='div']
614   *         Name of tag to be created.
615   *
616   * @param  {Object} [properties={}]
617   *         Element properties to be applied.
618   *
619   * @param  {Object} [attributes={}]
620   *         Element attributes to be applied.
621   *
622   * @param {module:dom~ContentDescriptor} content
623   *        A content descriptor object.
624   *
625   * @return {Element}
626   *         The element that was created.
627   */
628
629
630  function createEl(tagName, properties, attributes, content) {
631    if (tagName === void 0) {
632      tagName = 'div';
633    }
634
635    if (properties === void 0) {
636      properties = {};
637    }
638
639    if (attributes === void 0) {
640      attributes = {};
641    }
642
643    var el = document.createElement(tagName);
644    Object.getOwnPropertyNames(properties).forEach(function (propName) {
645      var val = properties[propName]; // See #2176
646      // We originally were accepting both properties and attributes in the
647      // same object, but that doesn't work so well.
648
649      if (propName.indexOf('aria-') !== -1 || propName === 'role' || propName === 'type') {
650        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 + "."));
651        el.setAttribute(propName, val); // Handle textContent since it's not supported everywhere and we have a
652        // method for it.
653      } else if (propName === 'textContent') {
654        textContent(el, val);
655      } else {
656        el[propName] = val;
657      }
658    });
659    Object.getOwnPropertyNames(attributes).forEach(function (attrName) {
660      el.setAttribute(attrName, attributes[attrName]);
661    });
662
663    if (content) {
664      appendContent(el, content);
665    }
666
667    return el;
668  }
669  /**
670   * Injects text into an element, replacing any existing contents entirely.
671   *
672   * @param  {Element} el
673   *         The element to add text content into
674   *
675   * @param  {string} text
676   *         The text content to add.
677   *
678   * @return {Element}
679   *         The element with added text content.
680   */
681
682  function textContent(el, text) {
683    if (typeof el.textContent === 'undefined') {
684      el.innerText = text;
685    } else {
686      el.textContent = text;
687    }
688
689    return el;
690  }
691  /**
692   * Insert an element as the first child node of another
693   *
694   * @param {Element} child
695   *        Element to insert
696   *
697   * @param {Element} parent
698   *        Element to insert child into
699   */
700
701  function prependTo(child, parent) {
702    if (parent.firstChild) {
703      parent.insertBefore(child, parent.firstChild);
704    } else {
705      parent.appendChild(child);
706    }
707  }
708  /**
709   * Check if an element has a class name.
710   *
711   * @param  {Element} element
712   *         Element to check
713   *
714   * @param  {string} classToCheck
715   *         Class name to check for
716   *
717   * @return {boolean}
718   *         Will be `true` if the element has a class, `false` otherwise.
719   *
720   * @throws {Error}
721   *         Throws an error if `classToCheck` has white space.
722   */
723
724  function hasClass(element, classToCheck) {
725    throwIfWhitespace(classToCheck);
726
727    if (element.classList) {
728      return element.classList.contains(classToCheck);
729    }
730
731    return classRegExp(classToCheck).test(element.className);
732  }
733  /**
734   * Add a class name to an element.
735   *
736   * @param  {Element} element
737   *         Element to add class name to.
738   *
739   * @param  {string} classToAdd
740   *         Class name to add.
741   *
742   * @return {Element}
743   *         The DOM element with the added class name.
744   */
745
746  function addClass(element, classToAdd) {
747    if (element.classList) {
748      element.classList.add(classToAdd); // Don't need to `throwIfWhitespace` here because `hasElClass` will do it
749      // in the case of classList not being supported.
750    } else if (!hasClass(element, classToAdd)) {
751      element.className = (element.className + ' ' + classToAdd).trim();
752    }
753
754    return element;
755  }
756  /**
757   * Remove a class name from an element.
758   *
759   * @param  {Element} element
760   *         Element to remove a class name from.
761   *
762   * @param  {string} classToRemove
763   *         Class name to remove
764   *
765   * @return {Element}
766   *         The DOM element with class name removed.
767   */
768
769  function removeClass(element, classToRemove) {
770    if (element.classList) {
771      element.classList.remove(classToRemove);
772    } else {
773      throwIfWhitespace(classToRemove);
774      element.className = element.className.split(/\s+/).filter(function (c) {
775        return c !== classToRemove;
776      }).join(' ');
777    }
778
779    return element;
780  }
781  /**
782   * The callback definition for toggleClass.
783   *
784   * @callback module:dom~PredicateCallback
785   * @param    {Element} element
786   *           The DOM element of the Component.
787   *
788   * @param    {string} classToToggle
789   *           The `className` that wants to be toggled
790   *
791   * @return   {boolean|undefined}
792   *           If `true` is returned, the `classToToggle` will be added to the
793   *           `element`. If `false`, the `classToToggle` will be removed from
794   *           the `element`. If `undefined`, the callback will be ignored.
795   */
796
797  /**
798   * Adds or removes a class name to/from an element depending on an optional
799   * condition or the presence/absence of the class name.
800   *
801   * @param  {Element} element
802   *         The element to toggle a class name on.
803   *
804   * @param  {string} classToToggle
805   *         The class that should be toggled.
806   *
807   * @param  {boolean|module:dom~PredicateCallback} [predicate]
808   *         See the return value for {@link module:dom~PredicateCallback}
809   *
810   * @return {Element}
811   *         The element with a class that has been toggled.
812   */
813
814  function toggleClass(element, classToToggle, predicate) {
815    // This CANNOT use `classList` internally because IE11 does not support the
816    // second parameter to the `classList.toggle()` method! Which is fine because
817    // `classList` will be used by the add/remove functions.
818    var has = hasClass(element, classToToggle);
819
820    if (typeof predicate === 'function') {
821      predicate = predicate(element, classToToggle);
822    }
823
824    if (typeof predicate !== 'boolean') {
825      predicate = !has;
826    } // If the necessary class operation matches the current state of the
827    // element, no action is required.
828
829
830    if (predicate === has) {
831      return;
832    }
833
834    if (predicate) {
835      addClass(element, classToToggle);
836    } else {
837      removeClass(element, classToToggle);
838    }
839
840    return element;
841  }
842  /**
843   * Apply attributes to an HTML element.
844   *
845   * @param {Element} el
846   *        Element to add attributes to.
847   *
848   * @param {Object} [attributes]
849   *        Attributes to be applied.
850   */
851
852  function setAttributes(el, attributes) {
853    Object.getOwnPropertyNames(attributes).forEach(function (attrName) {
854      var attrValue = attributes[attrName];
855
856      if (attrValue === null || typeof attrValue === 'undefined' || attrValue === false) {
857        el.removeAttribute(attrName);
858      } else {
859        el.setAttribute(attrName, attrValue === true ? '' : attrValue);
860      }
861    });
862  }
863  /**
864   * Get an element's attribute values, as defined on the HTML tag.
865   *
866   * Attributes are not the same as properties. They're defined on the tag
867   * or with setAttribute.
868   *
869   * @param  {Element} tag
870   *         Element from which to get tag attributes.
871   *
872   * @return {Object}
873   *         All attributes of the element. Boolean attributes will be `true` or
874   *         `false`, others will be strings.
875   */
876
877  function getAttributes(tag) {
878    var obj = {}; // known boolean attributes
879    // we can check for matching boolean properties, but not all browsers
880    // and not all tags know about these attributes, so, we still want to check them manually
881
882    var knownBooleans = ',' + 'autoplay,controls,playsinline,loop,muted,default,defaultMuted' + ',';
883
884    if (tag && tag.attributes && tag.attributes.length > 0) {
885      var attrs = tag.attributes;
886
887      for (var i = attrs.length - 1; i >= 0; i--) {
888        var attrName = attrs[i].name;
889        var attrVal = attrs[i].value; // check for known booleans
890        // the matching element property will return a value for typeof
891
892        if (typeof tag[attrName] === 'boolean' || knownBooleans.indexOf(',' + attrName + ',') !== -1) {
893          // the value of an included boolean attribute is typically an empty
894          // string ('') which would equal false if we just check for a false value.
895          // we also don't want support bad code like autoplay='false'
896          attrVal = attrVal !== null ? true : false;
897        }
898
899        obj[attrName] = attrVal;
900      }
901    }
902
903    return obj;
904  }
905  /**
906   * Get the value of an element's attribute.
907   *
908   * @param {Element} el
909   *        A DOM element.
910   *
911   * @param {string} attribute
912   *        Attribute to get the value of.
913   *
914   * @return {string}
915   *         The value of the attribute.
916   */
917
918  function getAttribute(el, attribute) {
919    return el.getAttribute(attribute);
920  }
921  /**
922   * Set the value of an element's attribute.
923   *
924   * @param {Element} el
925   *        A DOM element.
926   *
927   * @param {string} attribute
928   *        Attribute to set.
929   *
930   * @param {string} value
931   *        Value to set the attribute to.
932   */
933
934  function setAttribute(el, attribute, value) {
935    el.setAttribute(attribute, value);
936  }
937  /**
938   * Remove an element's attribute.
939   *
940   * @param {Element} el
941   *        A DOM element.
942   *
943   * @param {string} attribute
944   *        Attribute to remove.
945   */
946
947  function removeAttribute(el, attribute) {
948    el.removeAttribute(attribute);
949  }
950  /**
951   * Attempt to block the ability to select text.
952   */
953
954  function blockTextSelection() {
955    document.body.focus();
956
957    document.onselectstart = function () {
958      return false;
959    };
960  }
961  /**
962   * Turn off text selection blocking.
963   */
964
965  function unblockTextSelection() {
966    document.onselectstart = function () {
967      return true;
968    };
969  }
970  /**
971   * Identical to the native `getBoundingClientRect` function, but ensures that
972   * the method is supported at all (it is in all browsers we claim to supp
972ort)
973   * and that the element is in the DOM before continuing.
974   *
975   * This wrapper function also shims properties which are not provided by some
976   * older browsers (namely, IE8).
977   *
978   * Additionally, some browsers do not support adding properties to a
979   * `ClientRect`/`DOMRect` object; so, we shallow-copy it with the standard
980   * properties (except `x` and `y` which are not widely supported). This helps
981   * avoid implementations where keys are non-enumerable.
982   *
983   * @param  {Element} el
984   *         Element whose `ClientRect` we want to calculate.
985   *
986   * @return {Object|undefined}
987   *         Always returns a plain object - or `undefined` if it cannot.
988   */
989
990  function getBoundingClientRect(el) {
991    if (el && el.getBoundingClientRect && el.parentNode) {
992      var rect = el.getBoundingClientRect();
993      var result = {};
994      ['bottom', 'height', 'left', 'right', 'top', 'width'].forEach(function (k) {
995        if (rect[k] !== undefined) {
996          result[k] = rect[k];
997        }
998      });
999
1000      if (!result.height) {
1001        result.height = parseFloat(computedStyle(el, 'height'));
1002      }
1003
1004      if (!result.width) {
1005        result.width = parseFloat(computedStyle(el, 'width'));
1006      }
1007
1008      return result;
1009    }
1010  }
1011  /**
1012   * Represents the position of a DOM element on the page.
1013   *
1014   * @typedef  {Object} module:dom~Position
1015   *
1016   * @property {number} left
1017   *           Pixels to the left.
1018   *
1019   * @property {number} top
1020   *           Pixels from the top.
1021   */
1022
1023  /**
1024   * Get the position of an element in the DOM.
1025   *
1026   * Uses `getBoundingClientRect` technique from John Resig.
1027   *
1028   * @see http://ejohn.org/blog/getboundingclientrect-is-awesome/
1029   *
1030   * @param  {Element} el
1031   *         Element from which to get offset.
1032   *
1033   * @return {module:dom~Position}
1034   *         The position of the element that was passed in.
1035   */
1036
1037  function findPosition(el) {
1038    var box;
1039
1040    if (el.getBoundingClientRect && el.parentNode) {
1041      box = el.getBoundingClientRect();
1042    }
1043
1044    if (!box) {
1045      return {
1046        left: 0,
1047        top: 0
1048      };
1049    }
1050
1051    var docEl = document.documentElement;
1052    var body = document.body;
1053    var clientLeft = docEl.clientLeft || body.clientLeft || 0;
1054    var scrollLeft = window$1.pageXOffset || body.scrollLeft;
1055    var left = box.left + scrollLeft - clientLeft;
1056    var clientTop = docEl.clientTop || body.clientTop || 0;
1057    var scrollTop = window$1.pageYOffset || body.scrollTop;
1058    var top = box.top + scrollTop - clientTop; // Android sometimes returns slightly off decimal values, so need to round
1059
1060    return {
1061      left: Math.round(left),
1062      top: Math.round(top)
1063    };
1064  }
1065  /**
1066   * Represents x and y coordinates for a DOM element or mouse pointer.
1067   *
1068   * @typedef  {Object} module:dom~Coordinates
1069   *
1070   * @property {number} x
1071   *           x coordinate in pixels
1072   *
1073   * @property {number} y
1074   *           y coordinate in pixels
1075   */
1076
1077  /**
1078   * Get the pointer position within an element.
1079   *
1080   * The base on the coordinates are the bottom left of the element.
1081   *
1082   * @param  {Element} el
1083   *         Element on which to get the pointer position on.
1084   *
1085   * @param  {EventTarget~Event} event
1086   *         Event object.
1087   *
1088   * @return {module:dom~Coordinates}
1089   *         A coordinates object corresponding to the mouse position.
1090   *
1091   */
1092
1093  function getPointerPosition(el, event) {
1094    var position = {};
1095    var box = findPosition(el);
1096    var boxW = el.offsetWidth;
1097    var boxH = el.offsetHeight;
1098    var boxY = box.top;
1099    var boxX = box.left;
1100    var pageY = event.pageY;
1101    var pageX = event.pageX;
1102
1103    if (event.changedTouches) {
1104      pageX = event.changedTouches[0].pageX;
1105      pageY = event.changedTouches[0].pageY;
1106    }
1107
1108    position.y = Math.max(0, Math.min(1, (boxY - pageY + boxH) / boxH));
1109    position.x = Math.max(0, Math.min(1, (pageX - boxX) / boxW));
1110    return position;
1111  }
1112  /**
1113   * Determines, via duck typing, whether or not a value is a text node.
1114   *
1115   * @param  {Mixed} value
1116   *         Check if this value is a text node.
1117   *
1118   * @return {boolean}
1119   *         Will be `true` if the value is a text node, `false` otherwise.
1120   */
1121
1122  function isTextNode(value) {
1123    return isObject(value) && value.nodeType === 3;
1124  }
1125  /**
1126   * Empties the contents of an element.
1127   *
1128   * @param  {Element} el
1129   *         The element to empty children from
1130   *
1131   * @return {Element}
1132   *         The element with no children
1133   */
1134
1135  function emptyEl(el) {
1136    while (el.firstChild) {
1137      el.removeChild(el.firstChild);
1138    }
1139
1140    return el;
1141  }
1142  /**
1143   * This is a mixed value that describes content to be injected into the DOM
1144   * via some method. It can be of the following types:
1145   *
1146   * Type       | Description
1147   * -----------|-------------
1148   * `string`   | The value will be normalized into a text node.
1149   * `Element`  | The value will be accepted as-is.
1150   * `TextNode` | The value will be accepted as-is.
1151   * `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).
1152   * `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.
1153   *
1154   * @typedef {string|Element|TextNode|Array|Function} module:dom~ContentDescriptor
1155   */
1156
1157  /**
1158   * Normalizes content for eventual insertion into the DOM.
1159   *
1160   * This allows a wide range of content definition methods, but helps protect
1161   * from falling into the trap of simply writing to `innerHTML`, which could
1162   * be an XSS concern.
1163   *
1164   * The content for an element can be passed in multiple types and
1165   * combinations, whose behavior is as follows:
1166   *
1167   * @param {module:dom~ContentDescriptor} content
1168   *        A content descriptor value.
1169   *
1170   * @return {Array}
1171   *         All of the content that was passed in, normalized to an array of
1172   *         elements or text nodes.
1173   */
1174
1175  function normalizeContent(content) {
1176    // First, invoke content if it is a function. If it produces an array,
1177    // that needs to happen before normalization.
1178    if (typeof content === 'function') {
1179      content = content();
1180    } // Next up, normalize to an array, so one or many items can be normalized,
1181    // filtered, and returned.
1182
1183
1184    return (Array.isArray(content) ? content : [content]).map(function (value) {
1185      // First, invoke value if it is a function to produce a new value,
1186      // which will be subsequently normalized to a Node of some kind.
1187      if (typeof value === 'function') {
1188        value = value();
1189      }
1190
1191      if (isEl(value) || isTextNode(value)) {
1192        return value;
1193      }
1194
1195      if (typeof value === 'string' && /\S/.test(value)) {
1196        return document.createTextNode(value);
1197      }
1198    }).filter(function (value) {
1199      return value;
1200    });
1201  }
1202  /**
1203   * Normalizes and appends content to an element.
1204   *
1205   * @param  {Element} el
1206   *         Element to append normalized content to.
1207   *
1208   * @param {module:dom~ContentDescriptor} content
1209   *        A content descriptor value.
1210   *
1211   * @return {Element}
1212   *         The element with appended normalized content.
1213   */
1214
1215  function appendContent(el, content) {
1216    normalizeContent(content).forEach(function (node) {
1217      return el.appendChild(node);
1218    });
1219    return el;
1220  }
1221  /**
1222   * Normalizes and inserts content into an element; this is identical to
1223   * `appendContent()`, except it empties the element first.
1224   *
1225   * @param {Element} el
1226   *        Element to insert normalized content into.
1227   *
1228   * @param {module:dom~ContentDescriptor} content
1229   *        A content descriptor value.
1230   *
1231   * @return {Element}
1232   *         The element with inserted normalized content.
1233   */
1234
1235  function insertContent(el, content) {
1236    return appendContent(emptyEl(el), content);
1237  }
1238  /**
1239   * Check if an event was a single left click.
1240   *
1241   * @param  {EventTarget~Event} event
1242   *         Event object.
1243   *
1244   * @return {boolean}
1245   *         Will be `true` if a single left click, `false` otherwise.
1246   */
1247
1248  function isSingleLeftClick(event) {
1249    // Note: if you create something draggable, be sure to
1250    // call it on both `mousedown` and `mousemove` event,
1251    // otherwise `mousedown` should be enough for a button
1252    if (event.button === undefined && event.buttons === undefined) {
1253      // Why do we need `buttons` ?
1254      // Because, middle mouse sometimes have this:
1255      // e.button === 0 and e.buttons === 4
1256      // Furthermore, we want to prevent combination click, something like
1257      // HOLD middlemouse then left click, that would be
1258      // e.button === 0, e.buttons === 5
1259      // just `button` is not gonna work
1260      // Alright, then what this block does ?
1261      // this is for chrome `simulate mobile devices`
vendor: 23,646 bytes, lines 1262-2044
1262      // I want to support this as well
1263      return true;
1264    }
1265
1266    if (event.button === 0 && event.buttons === undefined) {
1267      // Touch screen, sometimes on some specific device, `buttons`
1268      // doesn't have anything (safari on ios, blackberry...)
1269      return true;
1270    } // `mouseup` event on a single left click has
1271    // `button` and `buttons` equal to 0
1272
1273
1274    if (event.type === 'mouseup' && event.button === 0 && event.buttons === 0) {
1275      return true;
1276    }
1277
1278    if (event.button !== 0 || event.buttons !== 1) {
1279      // This is the reason we have those if else block above
1280      // if any special case we can catch and let it slide
1281      // we do it above, when get to here, this definitely
1282      // is-not-left-click
1283      return false;
1284    }
1285
1286    return true;
1287  }
1288  /**
1289   * Finds a single DOM element matching `selector` within the optional
1290   * `context` of another DOM element (defaulting to `document`).
1291   *
1292   * @param  {string} selector
1293   *         A valid CSS selector, which will be passed to `querySelector`.
1294   *
1295   * @param  {Element|String} [context=document]
1296   *         A DOM element within which to query. Can also be a selector
1297   *         string in which case the first matching element will be used
1298   *         as context. If missing (or no element matches selector), falls
1299   *         back to `document`.
1300   *
1301   * @return {Element|null}
1302   *         The element that was found or null.
1303   */
1304
1305  var $ = createQuerier('querySelector');
1306  /**
1307   * Finds a all DOM elements matching `selector` within the optional
1308   * `context` of another DOM element (defaulting to `document`).
1309   *
1310   * @param  {string} selector
1311   *         A valid CSS selector, which will be passed to `querySelectorAll`.
1312   *
1313   * @param  {Element|String} [context=document]
1314   *         A DOM element within which to query. Can also be a selector
1315   *         string in which case the first matching element will be used
1316   *         as context. If missing (or no element matches selector), falls
1317   *         back to `document`.
1318   *
1319   * @return {NodeList}
1320   *         A element list of elements that were found. Will be empty if none
1321   *         were found.
1322   *
1323   */
1324
1325  var $$ = createQuerier('querySelectorAll');
1326
1327  var Dom = /*#__PURE__*/Object.freeze({
1328    isReal: isReal,
1329    isEl: isEl,
1330    isInFrame: isInFrame,
1331    createEl: createEl,
1332    textContent: textContent,
1333    prependTo: prependTo,
1334    hasClass: hasClass,
1335    addClass: addClass,
1336    removeClass: removeClass,
1337    toggleClass: toggleClass,
1338    setAttributes: setAttributes,
1339    getAttributes: getAttributes,
1340    getAttribute: getAttribute,
1341    setAttribute: setAttribute,
1342    removeAttribute: removeAttribute,
1343    blockTextSelection: blockTextSelection,
1344    unblockTextSelection: unblockTextSelection,
1345    getBoundingClientRect: getBoundingClientRect,
1346    findPosition: findPosition,
1347    getPointerPosition: getPointerPosition,
1348    isTextNode: isTextNode,
1349    emptyEl: emptyEl,
1350    normalizeContent: normalizeContent,
1351    appendContent: appendContent,
1352    insertContent: insertContent,
1353    isSingleLeftClick: isSingleLeftClick,
1354    $: $,
1355    $$: $$
1356  });
1357
1358  /**
1359   * @file setup.js - Functions for setting up a player without
1360   * user interaction based on the data-setup `attribute` of the video tag.
1361   *
1362   * @module setup
1363   */
1364  var _windowLoaded = false;
1365  var videojs;
1366  /**
1367   * Set up any tags that have a data-setup `attribute` when the player is started.
1368   */
1369
1370  var autoSetup = function autoSetup() {
1371    // Protect against breakage in non-browser environments and check global autoSetup option.
1372    if (!isReal() || videojs.options.autoSetup === false) {
1373      return;
1374    }
1375
1376    var vids = Array.prototype.slice.call(document.getElementsByTagName('video'));
1377    var audios = Array.prototype.slice.call(document.getElementsByTagName('audio'));
1378    var divs = Array.prototype.slice.call(document.getElementsByTagName('video-js'));
1379    var mediaEls = vids.concat(audios, divs); // Check if any media elements exist
1380
1381    if (mediaEls && mediaEls.length > 0) {
1382      for (var i = 0, e = mediaEls.length; i < e; i++) {
1383        var mediaEl = mediaEls[i]; // Check if element exists, has getAttribute func.
1384
1385        if (mediaEl && mediaEl.getAttribute) {
1386          // Make sure this player hasn't already been set up.
1387          if (mediaEl.player === undefined) {
1388            var options = mediaEl.getAttribute('data-setup'); // Check if data-setup attr exists.
1389            // We only auto-setup if they've added the data-setup attr.
1390
1391            if (options !== null) {
1392              // Create new video.js instance.
1393              videojs(mediaEl);
1394            }
1395          } // If getAttribute isn't defined, we need to wait for the DOM.
1396
1397        } else {
1398          autoSetupTimeout(1);
1399          break;
1400        }
1401      } // No videos were found, so keep looping unless page is finished loading.
1402
1403    } else if (!_windowLoaded) {
1404      autoSetupTimeout(1);
1405    }
1406  };
1407  /**
1408   * Wait until the page is loaded before running autoSetup. This will be called in
1409   * autoSetup if `hasLoaded` returns false.
1410   *
1411   * @param {number} wait
1412   *        How long to wait in ms
1413   *
1414   * @param {module:videojs} [vjs]
1415   *        The videojs library function
1416   */
1417
1418
1419  function autoSetupTimeout(wait, vjs) {
1420    if (vjs) {
1421      videojs = vjs;
1422    }
1423
1424    window$1.setTimeout(autoSetup, wait);
1425  }
1426  /**
1427   * Used to set the internal tracking of window loaded state to true.
1428   *
1429   * @private
1430   */
1431
1432
1433  function setWindowLoaded() {
1434    _windowLoaded = true;
1435    window$1.removeEventListener('load', setWindowLoaded);
1436  }
1437
1438  if (isReal()) {
1439    if (document.readyState === 'complete') {
1440      setWindowLoaded();
1441    } else {
1442      /**
1443       * Listen for the load event on window, and set _windowLoaded to true.
1444       *
1445       * We use a standard event listener here to avoid incrementing the GUID
1446       * before any players are created.
1447       *
1448       * @listens load
1449       */
1450      window$1.addEventListener('load', setWindowLoaded);
1451    }
1452  }
1453
1454  /**
1455   * @file stylesheet.js
1456   * @module stylesheet
1457   */
1458  /**
1459   * Create a DOM syle element given a className for it.
1460   *
1461   * @param {string} className
1462   *        The className to add to the created style element.
1463   *
1464   * @return {Element}
1465   *         The element that was created.
1466   */
1467
1468  var createStyleElement = function createStyleElement(className) {
1469    var style = document.createElement('style');
1470    style.className = className;
1471    return style;
1472  };
1473  /**
1474   * Add text to a DOM element.
1475   *
1476   * @param {Element} el
1477   *        The Element to add text content to.
1478   *
1479   * @param {string} content
1480   *        The text to add to the element.
1481   */
1482
1483  var setTextContent = function setTextContent(el, content) {
1484    if (el.styleSheet) {
1485      el.styleSheet.cssText = content;
1486    } else {
1487      el.textContent = content;
1488    }
1489  };
1490
1491  /**
1492   * @file dom-data.js
1493   * @module dom-data
1494   */
1495
1496  /**
1497   * Element Data Store.
1498   *
1499   * Allows for binding data to an element without putting it directly on the
1500   * element. Ex. Event listeners are stored here.
1501   * (also from jsninja.com, slightly modified and updated for closure compiler)
1502   *
1503   * @type {Object}
1504   * @private
1505   */
1506  var DomData = new WeakMap();
1507
1508  /**
1509   * @file guid.js
1510   * @module guid
1511   */
1512  // Default value for GUIDs. This allows us to reset the GUID counter in tests.
1513  //
1514  // The initial GUID is 3 because some users have come to rely on the first
1515  // default player ID ending up as `vjs_video_3`.
1516  //
1517  // See: https://github.com/videojs/video.js/pull/6216
1518  var _initialGuid = 3;
1519  /**
1520   * Unique ID for an element or function
1521   *
1522   * @type {Number}
1523   */
1524
1525  var _guid = _initialGuid;
1526  /**
1527   * Get a unique auto-incrementing ID by number that has not been returned before.
1528   *
1529   * @return {number}
1530   *         A new unique ID.
1531   */
1532
1533  function newGUID() {
1534    return _guid++;
1535  }
1536
1537  /**
1538   * @file events.js. An Event System (John Resig - Secrets of a JS Ninja http://jsninja.com/)
1539   * (Original book version wasn't completely usable, so fixed some things and made Closure Compiler compatible)
1540   * This should work very similarly to jQuery's events, however it's based off the book version which isn't as
1541   * robust as jquery's, so there's probably some differences.
1542   *
1543   * @file events.js
1544   * @module events
1545   */
1546  /**
1547   * Clean up the listener cache and dispatchers
1548   *
1549   * @param {Element|Object} elem
1550   *        Element to clean up
1551   *
1552   * @param {string} type
1553   *        Type of event to clean up
1554   */
1555
1556  function _cleanUpEvents(elem, type) {
1557    if (!DomData.has(elem)) {
1558      return;
1559    }
1560
1561    var data = DomData.get(elem); // Remove the events of a particular type if there are none left
1562
1563    if (data.handlers[type].length === 0) {
1564      delete data.handlers[type]; // data.handlers[type] = null;
1565      // Setting to null was causing an error with data.handlers
1566      // Remove the meta-handler from the element
1567
1568      if (elem.removeEventListener) {
1569        elem.removeEventListener(type, data.dispatcher, false);
1570      } else if (elem.detachEvent) {
1571        elem.detachEvent('on' + type, data.dispatcher);
1572      }
1573    } // Remove the events object if there are no types left
1574
1575
1576    if (Object.getOwnPropertyNames(data.handlers).length <= 0) {
1577      delete data.handlers;
1578      delete data.dispatcher;
1579      delete data.disabled;
1580    } // Finally remove the element data if there is no data left
1581
1582
1583    if (Object.getOwnPropertyNames(data).length === 0) {
1584      DomData["delete"](elem);
1585    }
1586  }
1587  /**
1588   * Loops through an array of event types and calls the requested method for each type.
1589   *
1590   * @param {Function} fn
1591   *        The event method we want to use.
1592   *
1593   * @param {Element|Object} elem
1594   *        Element or object to bind listeners to
1595   *
1596   * @param {string} type
1597   *        Type of event to bind to.
1598   *
1599   * @param {EventTarget~EventListener} callback
1600   *        Event listener.
1601   */
1602
1603
1604  function _handleMultipleEvents(fn, elem, types, callback) {
1605    types.forEach(function (type) {
1606      // Call the event method for each one of the types
1607      fn(elem, type, callback);
1608    });
1609  }
1610  /**
1611   * Fix a native event to have standard property values
1612   *
1613   * @param {Object} event
1614   *        Event object to fix.
1615   *
1616   * @return {Object}
1617   *         Fixed event object.
1618   */
1619
1620
1621  function fixEvent(event) {
1622    function returnTrue() {
1623      return true;
1624    }
1625
1626    function returnFalse() {
1627      return false;
1628    } // Test if fixing up is needed
1629    // Used to check if !event.stopPropagation instead of isPropagationStopped
1630    // But native events return true for stopPropagation, but don't have
1631    // other expected methods like isPropagationStopped. Seems to be a problem
1632    // with the Javascript Ninja code. So we're just overriding all events now.
1633
1634
1635    if (!event || !event.isPropagationStopped) {
1636      var old = event || window$1.event;
1637      event = {}; // Clone the old object so that we can modify the values event = {};
1638      // IE8 Doesn't like when you mess with native event properties
1639      // Firefox returns false for event.hasOwnProperty('type') and other props
1640      //  which makes copying more difficult.
1641      // TODO: Probably best to create a whitelist of event props
1642
1643      for (var key in old) {
1644        // Safari 6.0.3 warns you if you try to copy deprecated layerX/Y
1645        // Chrome warns you if you try to copy deprecated keyboardEvent.keyLocation
1646        // and webkitMovementX/Y
1647        if (key !== 'layerX' && key !== 'layerY' && key !== 'keyLocation' && key !== 'webkitMovementX' && key !== 'webkitMovementY') {
1648          // Chrome 32+ warns if you try to copy deprecated returnValue, but
1649          // we still want to if preventDefault isn't supported (IE8).
1650          if (!(key === 'returnValue' && old.preventDefault)) {
1651            event[key] = old[key];
1652          }
1653        }
1654      } // The event occurred on this element
1655
1656
1657      if (!event.target) {
1658        event.target = event.srcElement || document;
1659      } // Handle which other element the event is related to
1660
1661
1662      if (!event.relatedTarget) {
1663        event.relatedTarget = event.fromElement === event.target ? event.toElement : event.fromElement;
1664      } // Stop the default browser action
1665
1666
1667      event.preventDefault = function () {
1668        if (old.preventDefault) {
1669          old.preventDefault();
1670        }
1671
1672        event.returnValue = false;
1673        old.returnValue = false;
1674        event.defaultPrevented = true;
1675      };
1676
1677      event.defaultPrevented = false; // Stop the event from bubbling
1678
1679      event.stopPropagation = function () {
1680        if (old.stopPropagation) {
1681          old.stopPropagation();
1682        }
1683
1684        event.cancelBubble = true;
1685        old.cancelBubble = true;
1686        event.isPropagationStopped = returnTrue;
1687      };
1688
1689      event.isPropagationStopped = returnFalse; // Stop the event from bubbling and executing other handlers
1690
1691      event.stopImmediatePropagation = function () {
1692        if (old.stopImmediatePropagation) {
1693          old.stopImmediatePropagation();
1694        }
1695
1696        event.isImmediatePropagationStopped = returnTrue;
1697        event.stopPropagation();
1698      };
1699
1700      event.isImmediatePropagationStopped = returnFalse; // Handle mouse position
1701
1702      if (event.clientX !== null && event.clientX !== undefined) {
1703        var doc = document.documentElement;
1704        var body = document.body;
1705        event.pageX = event.clientX + (doc && doc.scrollLeft || body && body.scrollLeft || 0) - (doc && doc.clientLeft || body && body.clientLeft || 0);
1706        event.pageY = event.clientY + (doc && doc.scrollTop || body && body.scrollTop || 0) - (doc && doc.clientTop || body && body.clientTop || 0);
1707      } // Handle key presses
1708
1709
1710      event.which = event.charCode || event.keyCode; // Fix button for mouse clicks:
1711      // 0 == left; 1 == middle; 2 == right
1712
1713      if (event.button !== null && event.button !== undefined) {
1714        // The following is disabled because it does not pass videojs-standard
1715        // and... yikes.
1716
1717        /* eslint-disable */
1718        event.button = event.button & 1 ? 0 : event.button & 4 ? 1 : event.button & 2 ? 2 : 0;
1719        /* eslint-enable */
1720      }
1721    } // Returns fixed-up instance
1722
1723
1724    return event;
1725  }
1726  /**
1727   * Whether passive event listeners are supported
1728   */
1729
1730  var _supportsPassive = false;
1731
1732  (function () {
1733    try {
1734      var opts = Object.defineProperty({}, 'passive', {
1735        get: function get() {
1736          _supportsPassive = true;
1737        }
1738      });
1739      window$1.addEventListener('test', null, opts);
1740      window$1.removeEventListener('test', null, opts);
1741    } catch (e) {// disregard
1742    }
1743  })();
1744  /**
1745   * Touch events Chrome expects to be passive
1746   */
1747
1748
1749  var passiveEvents = ['touchstart', 'touchmove'];
1750  /**
1751   * Add an event listener to element
1752   * It stores the handler function in a separate cache object
1753   * and adds a generic handler to the element's event,
1754   * along with a unique id (guid) to the element.
1755   *
1756   * @param {Element|Object} elem
1757   *        Element or object to bind listeners to
1758   *
1759   * @param {string|string[]} type
1760   *        Type of event to bind to.
1761   *
1762   * @param {EventTarget~EventListener} fn
1763   *        Event listener.
1764   */
1765
1766  function on(elem, type, fn) {
1767    if (Array.isArray(type)) {
1768      return _handleMultipleEvents(on, elem, type, fn);
1769    }
1770
1771    if (!DomData.has(elem)) {
1772      DomData.set(elem, {});
1773    }
1774
1775    var data = DomData.get(elem); // We need a place to store all our handler data
1776
1777    if (!data.handlers) {
1778      data.handlers = {};
1779    }
1780
1781    if (!data.handlers[type]) {
1782      data.handlers[type] = [];
1783    }
1784
1785    if (!fn.guid) {
1786      fn.guid = newGUID();
1787    }
1788
1789    data.handlers[type].push(fn);
1790
1791    if (!data.dispatcher) {
1792      data.disabled = false;
1793
1794      data.dispatcher = function (event, hash) {
1795        if (data.disabled) {
1796          return;
1797        }
1798
1799        event = fixEvent(event);
1800        var handlers = data.handlers[event.type];
1801
1802        if (handlers) {
1803          // Copy handlers so if handlers are added/removed during the process it doesn't throw everything off.
1804          var handlersCopy = handlers.slice(0);
1805
1806          for (var m = 0, n = handlersCopy.length; m < n; m++) {
1807            if (event.isImmediatePropagationStopped()) {
1808              break;
1809            } else {
1810              try {
1811                handlersCopy[m].call(elem, event, hash);
1812              } catch (e) {
1813                log.error(e);
1814              }
1815            }
1816          }
1817        }
1818      };
1819    }
1820
1821    if (data.handlers[type].length === 1) {
1822      if (elem.addEventListener) {
1823        var options = false;
1824
1825        if (_supportsPassive && passiveEvents.indexOf(type) > -1) {
1826          options = {
1827            passive: true
1828          };
1829        }
1830
1831        elem.addEventListener(type, data.dispatcher, options);
1832      } else if (elem.attachEvent) {
1833        elem.attachEvent('on' + type, data.dispatcher);
1834      }
1835    }
1836  }
1837  /**
1838   * Removes event listeners from an element
1839   *
1840   * @param {Element|Object} elem
1841   *        Object to remove listeners from.
1842   *
1843   * @param {string|string[]} [type]
1844   *        Type of listener to remove. Don't include to remove all events from element.
1845   *
1846   * @param {EventTarget~EventListener} [fn]
1847   *        Specific listener to remove. Don't include to remove listeners for an event
1848   *        type.
1849   */
1850
1851  function off(elem, type, fn) {
1852    // Don't want to add a cache object through getElData if not needed
1853    if (!DomData.has(elem)) {
1854      return;
1855    }
1856
1857    var data = DomData.get(elem); // If no events exist, nothing to unbind
1858
1859    if (!data.handlers) {
1860      return;
1861    }
1862
1863    if (Array.isArray(type)) {
1864      return _handleMultipleEvents(off, elem, type, fn);
1865    } // Utility function
1866
1867
1868    var removeType = function removeType(el, t) {
1869      data.handlers[t] = [];
1870
1871      _cleanUpEvents(el, t);
1872    }; // Are we removing all bound events?
1873
1874
1875    if (type === undefined) {
1876      for (var t in data.handlers) {
1877        if (Object.prototype.hasOwnProperty.call(data.handlers || {}, t)) {
1878          removeType(elem, t);
1879        }
1880      }
1881
1882      return;
1883    }
1884
1885    var handlers = data.handlers[type]; // If no handlers exist, nothing to unbind
1886
1887    if (!handlers) {
1888      return;
1889    } // If no listener was provided, remove all listeners for type
1890
1891
1892    if (!fn) {
1893      removeType(elem, type);
1894      return;
1895    } // We're only removing a single handler
1896
1897
1898    if (fn.guid) {
1899      for (var n = 0; n < handlers.length; n++) {
1900        if (handlers[n].guid === fn.guid) {
1901          handlers.splice(n--, 1);
1902        }
1903      }
1904    }
1905
1906    _cleanUpEvents(elem, type);
1907  }
1908  /**
1909   * Trigger an event for an element
1910   *
1911   * @param {Element|Object} elem
1912   *        Element to trigger an event on
1913   *
1914   * @param {EventTarget~Event|string} event
1915   *        A string (the type) or an event object with a type attribute
1916   *
1917   * @param {Object} [hash]
1918   *        data hash to pass along with the event
1919   *
1920   * @return {boolean|undefined}
1921   *         Returns the opposite of `defaultPrevented` if default was
1922   *         prevented. Otherwise, returns `undefined`
1923   */
1924
1925  function trigger(elem, event, hash) {
1926    // Fetches element data and a reference to the parent (for bubbling).
1927    // Don't want to add a data object to cache for every parent,
1928    // so checking hasElData first.
1929    var elemData = DomData.has(elem) ? DomData.get(elem) : {};
1930    var parent = elem.parentNode || elem.ownerDocument; // type = event.type || event,
1931    // handler;
1932    // If an event name was passed as a string, creates an event out of it
1933
1934    if (typeof event === 'string') {
1935      event = {
1936        type: event,
1937        target: elem
1938      };
1939    } else if (!event.target) {
1940      event.target = elem;
1941    } // Normalizes the event properties.
1942
1943
1944    event = fixEvent(event); // If the passed element has a dispatcher, executes the established handlers.
1945
1946    if (elemData.dispatcher) {
1947      elemData.dispatcher.call(elem, event, hash);
1948    } // Unless explicitly stopped or the event does not bubble (e.g. media events)
1949    // recursively calls this function to bubble the event up the DOM.
1950
1951
1952    if (parent && !event.isPropagationStopped() && event.bubbles === true) {
1953      trigger.call(null, parent, event, hash); // If at the top of the DOM, triggers the default action unless disabled.
1954    } else if (!parent && !event.defaultPrevented && event.target && event.target[event.type]) {
1955      if (!DomData.has(event.target)) {
1956        DomData.set(event.target, {});
1957      }
1958
1959      var targetData = DomData.get(event.target); // Checks if the target has a default action for this event.
1960
1961      if (event.target[event.type]) {
1962        // Temporarily disables event dispatching on the target as we have already executed the handler.
1963        targetData.disabled = true; // Executes the default action.
1964
1965        if (typeof event.target[event.type] === 'function') {
1966          event.target[event.type]();
1967        } // Re-enables event dispatching.
1968
1969
1970        targetData.disabled = false;
1971      }
1972    } // Inform the triggerer if the default was prevented by returning false
1973
1974
1975    return !event.defaultPrevented;
1976  }
1977  /**
1978   * Trigger a listener only once for an event.
1979   *
1980   * @param {Element|Object} elem
1981   *        Element or object to bind to.
1982   *
1983   * @param {string|string[]} type
1984   *        Name/type of event
1985   *
1986   * @param {Event~EventListener} fn
1987   *        Event listener function
1988   */
1989
1990  function one(elem, type, fn) {
1991    if (Array.isArray(type)) {
1992      return _handleMultipleEvents(one, elem, type, fn);
1993    }
1994
1995    var func = function func() {
1996      off(elem, type, func);
1997      fn.apply(this, arguments);
1998    }; // copy the guid to the new function so it can removed using the original function's ID
1999
2000
2001    func.guid = fn.guid = fn.guid || newGUID();
2002    on(elem, type, func);
2003  }
2004  /**
2005   * Trigger a listener only once and then turn if off for all
2006   * configured events
2007   *
2008   * @param {Element|Object} elem
2009   *        Element or object to bind to.
2010   *
2011   * @param {string|string[]} type
2012   *        Name/type of event
2013   *
2014   * @param {Event~EventListener} fn
2015   *        Event listener function
2016   */
2017
2018  function any(elem, type, fn) {
2019    var func = function func() {
2020      off(elem, type, func);
2021      fn.apply(this, arguments);
2022    }; // copy the guid to the new function so it can removed using the original function's ID
2023
2024
2025    func.guid = fn.guid = fn.guid || newGUID(); // multiple ons, but one off for everything
2026
2027    on(elem, type, func);
2028  }
2029
2030  var Events = /*#__PURE__*/Object.freeze({
2031    fixEvent: fixEvent,
2032    on: on,
2033    off: off,
2034    trigger: trigger,
2035    one: one,
2036    any: any
2037  });
2038
2039  /**
2040   * @file fn.js
2041   * @module fn
2042   */
2043  var UPDATE_REFRESH_INTERVAL = 30;
2044  /**
2045   * Bind (a.k.a proxy or context). A simple method for changing the context of
2046   * a function.
2047   *
2048   * It also stores a unique id on the function so it can be easily removed from
2049   * events.
2050   *
2051   * @function
2052   * @param    {Mixed} context
2053   *           The object to bind as scope.
2054   *
2055   * @param    {Function} fn
2056   *           The function to be bound to a scope.
2057   *
2058   * @param    {number} [uid]
2059   *           An optional unique ID for the function to be set
2060   *
2061   * @return   {Function}
2062   *           The new function that will be bound into the context given
2063   */
2064
2065  var bind = function bind(context, fn, uid) {
2066    // Make sure the function has a unique ID
2067    if (!fn.guid) {
2068      fn.guid = newGUID();
2069    } // Create the new function that changes the context
2070
2071
2072    var bound = fn.bind(context); // Allow for the ability to individualize this function
2073    // Needed in the case where multiple objects might share the same prototype
2074    // IF both items add an event listener with the same function, then you try to remove just one
2075    // it will remove both because they both have the same guid.
2076    // when using this, you need to use the bind method when you remove the listener as well.
2077    // currently used in text tracks
2078
2079    bound.guid = uid ? uid + '_' + fn.guid : fn.guid;
2080    return bound;
2081  };
2082  /**
2083   * Wraps the given function, `fn`, with a new function that only invokes `fn`
2084   * at most once per every `wait` milliseconds.
2085   *
2086   * @function
2087   * @param    {Function} fn
2088   *           The function to be throttled.
2089   *
2090   * @param    {number}   wait
2091   *           The number of milliseconds by which to throttle.
2092   *
2093   * @return   {Function}
2094   */
2095
2096  var throttle = function throttle(fn, wait) {
2097    var last = window$1.performance.now();
2098
2099    var throttled = function throttled() {
2100      var now = window$1.performance.now();
2101
2102      if (now - last >= wait) {
2103        fn.apply(void 0, arguments);
2104        last = now;
2105      }
2106    };
2107
2108    return throttled;
2109  };
2110  /**
2111   * Creates a debounced function that delays invoking `func` until after `wait`
2112   * milliseconds have elapsed since the last time the debounced function was
2113   * invoked.
2114   *
2115   * Inspired by lodash and underscore implementations.
2116   *
2117   * @function
2118   * @param    {Function} func
2119   *           The function to wrap with debounce behavior.
2120   *
2121   * @param    {number} wait
2122   *           The number of milliseconds to wait after the last invocation.
2123   *
2124   * @param    {boolean} [immediate]
2125   *           Whether or not to invoke the function immediately upon creation.
2126   *
2127   * @param    {Object} [context=window]
2128   *           The "context" in which the debounced function should debounce. For
2129   *           example, if this function should be tied to a Video.js player,
2130   *           the player can be passed here. Alternatively, defaults to the
2131   *           global `window` object.
2132   *
2133   * @return   {Function}
2134   *           A debounced function.
2135   */
2136
2137  var debounce = function debounce(func, wait, immediate, context) {
2138    if (context === void 0) {
2139      context = window$1;
2140    }
2141
2142    var timeout;
2143
2144    var cancel = function cancel() {
2145      context.clearTimeout(timeout);
2146      timeout = null;
2147    };
2148    /* eslint-disable consistent-this */
2149
2150
2151    var debounced = function debounced() {
2152      var self = this;
2153      var args = arguments;
2154
2155      var _later = function later() {
2156        timeout = null;
2157        _later = null;
2158
2159        if (!immediate) {
2160          func.apply(self, args);
2161        }
2162      };
2163
2164      if (!timeout && immediate) {
2165        func.apply(self, args);
2166      }
2167
2168      context.clearTimeout(timeout);
2169      timeout = context.setTimeout(_later, wait);
2170    };
2171    /* eslint-enable consistent-this */
2172
2173
2174    debounced.cancel = cancel;
2175    return debounced;
2176  };
2177
2178  /**
2179   * @file src/js/event-target.js
2180   */
2181  /**
2182   * `EventTarget` is a class that can have the same API as the DOM `EventTarget`. It
2183   * adds shorthand functions that wrap around lengthy functions. For example:
2184   * the `on` function is a wrapper around `addEventListener`.
2185   *
2186   * @see [EventTarget Spec]{@link https://www.w3.org/TR/DOM-Level-2-Events/events.html#Events-EventTarget}
2187   * @class EventTarget
2188   */
2189
2190  var EventTarget = function EventTarget() {};
2191  /**
2192   * A Custom DOM event.
2193   *
2194   * @typedef {Object} EventTarget~Event
2195   * @see [Properties]{@link https://developer.mozilla.org/en-US/docs/Web/API/CustomEvent}
2196   */
2197
2198  /**
2199   * All event listeners should follow the following format.
2200   *
2201   * @callback EventTarget~EventListener
2202   * @this {EventTarget}
2203   *
2204   * @param {EventTarget~Event} event
2205   *        the event that triggered this function
2206   *
2207   * @param {Object} [hash]
2208   *        hash of data sent during the event
2209   */
2210
2211  /**
vendor: 6,091 bytes, lines 2212-2411
2212   * An object containing event names as keys and booleans as values.
2213   *
2214   * > NOTE: If an event name is set to a true value here {@link EventTarget#trigger}
2215   *         will have extra functionality. See that function for more information.
2216   *
2217   * @property EventTarget.prototype.allowedEvents_
2218   * @private
2219   */
2220
2221
2222  EventTarget.prototype.allowedEvents_ = {};
2223  /**
2224   * Adds an `event listener` to an instance of an `EventTarget`. An `event listener` is a
2225   * function that will get called when an event with a certain name gets triggered.
2226   *
2227   * @param {string|string[]} type
2228   *        An event name or an array of event names.
2229   *
2230   * @param {EventTarget~EventListener} fn
2231   *        The function to call with `EventTarget`s
2232   */
2233
2234  EventTarget.prototype.on = function (type, fn) {
2235    // Remove the addEventListener alias before calling Events.on
2236    // so we don't get into an infinite type loop
2237    var ael = this.addEventListener;
2238
2239    this.addEventListener = function () {};
2240
2241    on(this, type, fn);
2242    this.addEventListener = ael;
2243  };
2244  /**
2245   * An alias of {@link EventTarget#on}. Allows `EventTarget` to mimic
2246   * the standard DOM API.
2247   *
2248   * @function
2249   * @see {@link EventTarget#on}
2250   */
2251
2252
2253  EventTarget.prototype.addEventListener = EventTarget.prototype.on;
2254  /**
2255   * Removes an `event listener` for a specific event from an instance of `EventTarget`.
2256   * This makes it so that the `event listener` will no longer get called when the
2257   * named event happens.
2258   *
2259   * @param {string|string[]} type
2260   *        An event name or an array of event names.
2261   *
2262   * @param {EventTarget~EventListener} fn
2263   *        The function to remove.
2264   */
2265
2266  EventTarget.prototype.off = function (type, fn) {
2267    off(this, type, fn);
2268  };
2269  /**
2270   * An alias of {@link EventTarget#off}. Allows `EventTarget` to mimic
2271   * the standard DOM API.
2272   *
2273   * @function
2274   * @see {@link EventTarget#off}
2275   */
2276
2277
2278  EventTarget.prototype.removeEventListener = EventTarget.prototype.off;
2279  /**
2280   * This function will add an `event listener` that gets triggered only once. After the
2281   * first trigger it will get removed. This is like adding an `event listener`
2282   * with {@link EventTarget#on} that calls {@link EventTarget#off} on itself.
2283   *
2284   * @param {string|string[]} type
2285   *        An event name or an array of event names.
2286   *
2287   * @param {EventTarget~EventListener} fn
2288   *        The function to be called once for each event name.
2289   */
2290
2291  EventTarget.prototype.one = function (type, fn) {
2292    // Remove the addEventListener aliasing Events.on
2293    // so we don't get into an infinite type loop
2294    var ael = this.addEventListener;
2295
2296    this.addEventListener = function () {};
2297
2298    one(this, type, fn);
2299    this.addEventListener = ael;
2300  };
2301
2302  EventTarget.prototype.any = function (type, fn) {
2303    // Remove the addEventListener aliasing Events.on
2304    // so we don't get into an infinite type loop
2305    var ael = this.addEventListener;
2306
2307    this.addEventListener = function () {};
2308
2309    any(this, type, fn);
2310    this.addEventListener = ael;
2311  };
2312  /**
2313   * This function causes an event to happen. This will then cause any `event listeners`
2314   * that are waiting for that event, to get called. If there are no `event listeners`
2315   * for an event then nothing will happen.
2316   *
2317   * If the name of the `Event` that is being triggered is in `EventTarget.allowedEvents_`.
2318   * Trigger will also call the `on` + `uppercaseEventName` function.
2319   *
2320   * Example:
2321   * 'click' is in `EventTarget.allowedEvents_`, so, trigger will attempt to call
2322   * `onClick` if it exists.
2323   *
2324   * @param {string|EventTarget~Event|Object} event
2325   *        The name of the event, an `Event`, or an object with a key of type set to
2326   *        an event name.
2327   */
2328
2329
2330  EventTarget.prototype.trigger = function (event) {
2331    var type = event.type || event; // deprecation
2332    // In a future version we should default target to `this`
2333    // similar to how we default the target to `elem` in
2334    // `Events.trigger`. Right now the default `target` will be
2335    // `document` due to the `Event.fixEvent` call.
2336
2337    if (typeof event === 'string') {
2338      event = {
2339        type: type
2340      };
2341    }
2342
2343    event = fixEvent(event);
2344
2345    if (this.allowedEvents_[type] && this['on' + type]) {
2346      this['on' + type](event);
2347    }
2348
2349    trigger(this, event);
2350  };
2351  /**
2352   * An alias of {@link EventTarget#trigger}. Allows `EventTarget` to mimic
2353   * the standard DOM API.
2354   *
2355   * @function
2356   * @see {@link EventTarget#trigger}
2357   */
2358
2359
2360  EventTarget.prototype.dispatchEvent = EventTarget.prototype.trigger;
2361  var EVENT_MAP;
2362
2363  EventTarget.prototype.queueTrigger = function (event) {
2364    var _this = this;
2365
2366    // only set up EVENT_MAP if it'll be used
2367    if (!EVENT_MAP) {
2368      EVENT_MAP = new Map();
2369    }
2370
2371    var type = event.type || event;
2372    var map = EVENT_MAP.get(this);
2373
2374    if (!map) {
2375      map = new Map();
2376      EVENT_MAP.set(this, map);
2377    }
2378
2379    var oldTimeout = map.get(type);
2380    map["delete"](type);
2381    window$1.clearTimeout(oldTimeout);
2382    var timeout = window$1.setTimeout(function () {
2383      // if we cleared out all timeouts for the current target, delete its map
2384      if (map.size === 0) {
2385        map = null;
2386        EVENT_MAP["delete"](_this);
2387      }
2388
2389      _this.trigger(event);
2390    }, 0);
2391    map.set(type, timeout);
2392  };
2393
2394  /**
2395   * @file mixins/evented.js
2396   * @module evented
2397   */
2398  /**
2399   * Returns whether or not an object has had the evented mixin applied.
2400   *
2401   * @param  {Object} object
2402   *         An object to test.
2403   *
2404   * @return {boolean}
2405   *         Whether or not the object appears to be evented.
2406   */
2407
2408  var isEvented = function isEvented(object) {
2409    return object instanceof EventTarget || !!object.eventBusEl_ && ['on', 'one', 'off', 'trigger'].every(function (k) {
2410      return typeof object[k] === 'function';
2411    });
vendor: 4,139 bytes, lines 2412-2559
2412  };
2413  /**
2414   * Adds a callback to run after the evented mixin applied.
2415   *
2416   * @param  {Object} object
2417   *         An object to Add
2418   * @param  {Function} callback
2419   *         The callback to run.
2420   */
2421
2422
2423  var addEventedCallback = function addEventedCallback(target, callback) {
2424    if (isEvented(target)) {
2425      callback();
2426    } else {
2427      if (!target.eventedCallbacks) {
2428        target.eventedCallbacks = [];
2429      }
2430
2431      target.eventedCallbacks.push(callback);
2432    }
2433  };
2434  /**
2435   * Whether a value is a valid event type - non-empty string or array.
2436   *
2437   * @private
2438   * @param  {string|Array} type
2439   *         The type value to test.
2440   *
2441   * @return {boolean}
2442   *         Whether or not the type is a valid event type.
2443   */
2444
2445
2446  var isValidEventType = function isValidEventType(type) {
2447    return (// The regex here verifies that the `type` contains at least one non-
2448      // whitespace character.
2449      typeof type === 'string' && /\S/.test(type) || Array.isArray(type) && !!type.length
2450    );
2451  };
2452  /**
2453   * Validates a value to determine if it is a valid event target. Throws if not.
2454   *
2455   * @private
2456   * @throws {Error}
2457   *         If the target does not appear to be a valid event target.
2458   *
2459   * @param  {Object} target
2460   *         The object to test.
2461   */
2462
2463
2464  var validateTarget = function validateTarget(target) {
2465    if (!target.nodeName && !isEvented(target)) {
2466      throw new Error('Invalid target; must be a DOM node or evented object.');
2467    }
2468  };
2469  /**
2470   * Validates a value to determine if it is a valid event target. Throws if not.
2471   *
2472   * @private
2473   * @throws {Error}
2474   *         If the type does not appear to be a valid event type.
2475   *
2476   * @param  {string|Array} type
2477   *         The type to test.
2478   */
2479
2480
2481  var validateEventType = function validateEventType(type) {
2482    if (!isValidEventType(type)) {
2483      throw new Error('Invalid event type; must be a non-empty string or array.');
2484    }
2485  };
2486  /**
2487   * Validates a value to determine if it is a valid listener. Throws if not.
2488   *
2489   * @private
2490   * @throws {Error}
2491   *         If the listener is not a function.
2492   *
2493   * @param  {Function} listener
2494   *         The listener to test.
2495   */
2496
2497
2498  var validateListener = function validateListener(listener) {
2499    if (typeof listener !== 'function') {
2500      throw new Error('Invalid listener; must be a function.');
2501    }
2502  };
2503  /**
2504   * Takes an array of arguments given to `on()` or `one()`, validates them, and
2505   * normalizes them into an object.
2506   *
2507   * @private
2508   * @param  {Object} self
2509   *         The evented object on which `on()` or `one()` was called. This
2510   *         object will be bound as the `this` value for the listener.
2511   *
2512   * @param  {Array} args
2513   *         An array of arguments passed to `on()` or `one()`.
2514   *
2515   * @return {Object}
2516   *         An object containing useful values for `on()` or `one()` calls.
2517   */
2518
2519
2520  var normalizeListenArgs = function normalizeListenArgs(self, args) {
2521    // If the number of arguments is less than 3, the target is always the
2522    // evented object itself.
2523    var isTargetingSelf = args.length < 3 || args[0] === self || args[0] === self.eventBusEl_;
2524    var target;
2525    var type;
2526    var listener;
2527
2528    if (isTargetingSelf) {
2529      target = self.eventBusEl_; // Deal with cases where we got 3 arguments, but we are still listening to
2530      // the evented object itself.
2531
2532      if (args.length >= 3) {
2533        args.shift();
2534      }
2535
2536      type = args[0];
2537      listener = args[1];
2538    } else {
2539      target = args[0];
2540      type = args[1];
2541      listener = args[2];
2542    }
2543
2544    validateTarget(target);
2545    validateEventType(type);
2546    validateListener(listener);
2547    listener = bind(self, listener);
2548    return {
2549      isTargetingSelf: isTargetingSelf,
2550      target: target,
2551      type: type,
2552      listener: listener
2553    };
2554  };
2555  /**
2556   * Adds the listener to the event type(s) on the target, normalizing for
2557   * the type of target.
2558   *
2559   * @private
vendor: 6,282 bytes, lines 2560-2719
2560   * @param  {Element|Object} target
2561   *         A DOM node or evented object.
2562   *
2563   * @param  {string} method
2564   *         The event binding method to use ("on" or "one").
2565   *
2566   * @param  {string|Array} type
2567   *         One or more event type(s).
2568   *
2569   * @param  {Function} listener
2570   *         A listener function.
2571   */
2572
2573
2574  var listen = function listen(target, method, type, listener) {
2575    validateTarget(target);
2576
2577    if (target.nodeName) {
2578      Events[method](target, type, listener);
2579    } else {
2580      target[method](type, listener);
2581    }
2582  };
2583  /**
2584   * Contains methods that provide event capabilities to an object which is passed
2585   * to {@link module:evented|evented}.
2586   *
2587   * @mixin EventedMixin
2588   */
2589
2590
2591  var EventedMixin = {
2592    /**
2593     * Add a listener to an event (or events) on this object or another evented
2594     * object.
2595     *
2596     * @param  {string|Array|Element|Object} targetOrType
2597     *         If this is a string or array, it represents the event type(s)
2598     *         that will trigger the listener.
2599     *
2600     *         Another evented object can be passed here instead, which will
2601     *         cause the listener to listen for events on _that_ object.
2602     *
2603     *         In either case, the listener's `this` value will be bound to
2604     *         this object.
2605     *
2606     * @param  {string|Array|Function} typeOrListener
2607     *         If the first argument was a string or array, this should be the
2608     *         listener function. Otherwise, this is a string or array of event
2609     *         type(s).
2610     *
2611     * @param  {Function} [listener]
2612     *         If the first argument was another evented object, this will be
2613     *         the listener function.
2614     */
2615    on: function on() {
2616      var _this = this;
2617
2618      for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) {
2619        args[_key] = arguments[_key];
2620      }
2621
2622      var _normalizeListenArgs = normalizeListenArgs(this, args),
2623          isTargetingSelf = _normalizeListenArgs.isTargetingSelf,
2624          target = _normalizeListenArgs.target,
2625          type = _normalizeListenArgs.type,
2626          listener = _normalizeListenArgs.listener;
2627
2628      listen(target, 'on', type, listener); // If this object is listening to another evented object.
2629
2630      if (!isTargetingSelf) {
2631        // If this object is disposed, remove the listener.
2632        var removeListenerOnDispose = function removeListenerOnDispose() {
2633          return _this.off(target, type, listener);
2634        }; // Use the same function ID as the listener so we can remove it later it
2635        // using the ID of the original listener.
2636
2637
2638        removeListenerOnDispose.guid = listener.guid; // Add a listener to the target's dispose event as well. This ensures
2639        // that if the target is disposed BEFORE this object, we remove the
2640        // removal listener that was just added. Otherwise, we create a memory leak.
2641
2642        var removeRemoverOnTargetDispose = function removeRemoverOnTargetDispose() {
2643          return _this.off('dispose', removeListenerOnDispose);
2644        }; // Use the same function ID as the listener so we can remove it later
2645        // it using the ID of the original listener.
2646
2647
2648        removeRemoverOnTargetDispose.guid = listener.guid;
2649        listen(this, 'on', 'dispose', removeListenerOnDispose);
2650        listen(target, 'on', 'dispose', removeRemoverOnTargetDispose);
2651      }
2652    },
2653
2654    /**
2655     * Add a listener to an event (or events) on this object or another evented
2656     * object. The listener will be called once per event and then removed.
2657     *
2658     * @param  {string|Array|Element|Object} targetOrType
2659     *         If this is a string or array, it represents the event type(s)
2660     *         that will trigger the listener.
2661     *
2662     *         Another evented object can be passed here instead, which will
2663     *         cause the listener to listen for events on _that_ object.
2664     *
2665     *         In either case, the listener's `this` value will be bound to
2666     *         this object.
2667     *
2668     * @param  {string|Array|Function} typeOrListener
2669     *         If the first argument was a string or array, this should be the
2670     *         listener function. Otherwise, this is a string or array of event
2671     *         type(s).
2672     *
2673     * @param  {Function} [listener]
2674     *         If the first argument was another evented object, this will be
2675     *         the listener function.
2676     */
2677    one: function one() {
2678      var _this2 = this;
2679
2680      for (var _len2 = arguments.length, args = new Array(_len2), _key2 = 0; _key2 < _len2; _key2++) {
2681        args[_key2] = arguments[_key2];
2682      }
2683
2684      var _normalizeListenArgs2 = normalizeListenArgs(this, args),
2685          isTargetingSelf = _normalizeListenArgs2.isTargetingSelf,
2686          target = _normalizeListenArgs2.target,
2687          type = _normalizeListenArgs2.type,
2688          listener = _normalizeListenArgs2.listener; // Targeting this evented object.
2689
2690
2691      if (isTargetingSelf) {
2692        listen(target, 'one', type, listener); // Targeting another evented object.
2693      } else {
2694        // TODO: This wrapper is incorrect! It should only
2695        //       remove the wrapper for the event type that called it.
2696        //       Instead all listners are removed on the first trigger!
2697        //       see https://github.com/videojs/video.js/issues/5962
2698        var wrapper = function wrapper() {
2699          _this2.off(target, type, wrapper);
2700
2701          for (var _len3 = arguments.length, largs = new Array(_len3), _key3 = 0; _key3 < _len3; _key3++) {
2702            largs[_key3] = arguments[_key3];
2703          }
2704
2705          listener.apply(null, largs);
2706        }; // Use the same function ID as the listener so we can remove it later
2707        // it using the ID of the original listener.
2708
2709
2710        wrapper.guid = listener.guid;
2711        listen(target, 'one', type, wrapper);
2712      }
2713    },
2714
2715    /**
2716     * Add a listener to an event (or events) on this object or another evented
2717     * object. The listener will only be called once for the first event that is triggered
2718     * then removed.
2719     *
2720     * @param  {string|Array|Element|Object} targetOrType
2721     *         If this is a string or array, it represents the event type(s)
2722     *         that will trigger the listener.
2723     *
2724     *         Another evented object can be passed here instead, which will
2725     *         cause the listener to listen for events on _that_ object.
2726     *
2727     *         In either case, the listener's `this` value will be bound to
2728     *         this object.
2729     *
2730     * @param  {string|Array|Function} typeOrListener
2731     *         If the first argument was a string or array, this should be the
2732     *         listener function. Otherwise, this is a string or array of event
2733     *         type(s).
2734     *
2735     * @param  {Function} [listener]
2736     *         If the first argument was another evented object, this will be
2737     *         the listener function.
2738     */
2739    any: function any() {
2740      var _this3 = this;
2741
2742      for (var _len4 = arguments.length, args = new Array(_len4), _key4 = 0; _key4 < _len4; _key4++) {
2743        args[_key4] = arguments[_key4];
2744      }
2745
2746      var _normalizeListenArgs3 = normalizeListenArgs(this, args),
2747          isTargetingSelf = _normalizeListenArgs3.isTargetingSelf,
2748          target = _normalizeListenArgs3.target,
2749          type = _normalizeListenArgs3.type,
2750          listener = _normalizeListenArgs3.listener; // Targeting this evented object.
2751
2752
2753      if (isTargetingSelf) {
2754        listen(target, 'any', type, listener); // Targeting another evented object.
2755      } else {
2756        var wrapper = function wrapper() {
2757          _this3.off(target, type, wrapper);
2758
2759          for (var _len5 = arguments.length, largs = new Array(_len5), _key5 = 0; _key5 < _len5; _key5++) {
2760            largs[_key5] = arguments[_key5];
2761          }
2762
2763          listener.apply(null, largs);
2764        }; // Use the same function ID as the listener so we can remove it later
2765        // it using the ID of the original listener.
2766
2767
2768        wrapper.guid = listener.guid;
2769        listen(target, 'any', type, wrapper);
2770      }
2771    },
2772
2773    /**
2774     * Removes listener(s) from event(s) on an evented object.
2775     *
2776     * @param  {string|Array|Element|Object} [targetOrType]
2777     *         If this is a string or array, it represents the event type(s).
2778     *
2779     *         Another evented object can be passed here instead, in which case
2780     *         ALL 3 arguments are _required_.
2781     *
2782     * @param  {string|Array|Function} [typeOrListener]
2783     *         If the first argument was a string or array, this may be the
2784     *         listener function. Otherwise, this is a string or array of event
2785     *         type(s).
2786     *
2787     * @param  {Function} [listener]
2788     *         If the first argument was another evented object, this will be
2789     *         the listener function; otherwise, _all_ listeners bound to the
2790     *         event type(s) will be removed.
2791     */
2792    off: function off$1(targetOrType, typeOrListener, listener) {
2793      // Targeting this evented object.
2794      if (!targetOrType || isValidEventType(targetOrType)) {
2795        off(this.eventBusEl_, targetOrType, typeOrListener); // Targeting another evented object.
2796      } else {
2797        var target = targetOrType;
2798        var type = typeOrListener; // Fail fast and in a meaningful way!
2799
2800        validateTarget(target);
2801        validateEventType(type);
2802        validateListener(listener); // Ensure there's at least a guid, even if the function hasn't been used
2803
2804        listener = bind(this, listener); // Remove the dispose listener on this evented object, which was given
2805        // the same guid as the event listener in on().
2806
2807        this.off('dispose', listener);
2808
2809        if (target.nodeName) {
2810          off(target, type, listener);
2811          off(target, 'dispose', listener);
2812        } else if (isEvented(target)) {
2813          target.off(type, listener);
2814          target.off('dispose', listener);
2815        }
2816      }
2817    },
2818
2819    /**
2820     * Fire an event on this evented object, causing its listeners to be called.
2821     *
2822     * @param   {string|Object} event
2823     *          An event type or an object with a type property.
2824     *
2825     * @param   {Object} [hash]
2826     *          An additional object to pass along to listeners.
2827     *
2828     * @return {boolean}
2829     *          Whether or not the default behavior was prevented.
2830     */
2831    trigger: function trigger$1(event, hash) {
2832      return trigger(this.eventBusEl_, event, hash);
2833    }
2834  };
2835  /**
2836   * Applies {@link module:evented~EventedMixin|EventedMixin} to a target object.
2837   *
2838   * @param  {Object} target
2839   *         The object to which to add event methods.
2840   *
2841   * @param  {Object} [options={}]
2842   *         Options for customizing the mixin behavior.
2843   *
2844   * @param  {string} [options.eventBusKey]
2845   *         By default, adds a `eventBusEl_` DOM element to the target object,
2846   *         which is used as an event bus. If the target object already has a
2847   *         DOM element that should be used, pass its key here.
2848   *
2849   * @return {Object}
2850   *         The target object.
2851   */
2852
2853  function evented(target, options) {
2854    if (options === void 0) {
2855      options = {};
2856    }
2857
2858    var _options = options,
2859        eventBusKey = _options.eventBusKey; // Set or create the eventBusEl_.
2860
vendor: 10,461 bytes, lines 2861-3202
2861    if (eventBusKey) {
2862      if (!target[eventBusKey].nodeName) {
2863        throw new Error("The eventBusKey \"" + eventBusKey + "\" does not refer to an element.");
2864      }
2865
2866      target.eventBusEl_ = target[eventBusKey];
2867    } else {
2868      target.eventBusEl_ = createEl('span', {
2869        className: 'vjs-event-bus'
2870      });
2871    }
2872
2873    assign(target, EventedMixin);
2874
2875    if (target.eventedCallbacks) {
2876      target.eventedCallbacks.forEach(function (callback) {
2877        callback();
2878      });
2879    } // When any evented object is disposed, it removes all its listeners.
2880
2881
2882    target.on('dispose', function () {
2883      target.off();
2884      window$1.setTimeout(function () {
2885        target.eventBusEl_ = null;
2886      }, 0);
2887    });
2888    return target;
2889  }
2890
2891  /**
2892   * @file mixins/stateful.js
2893   * @module stateful
2894   */
2895  /**
2896   * Contains methods that provide statefulness to an object which is passed
2897   * to {@link module:stateful}.
2898   *
2899   * @mixin StatefulMixin
2900   */
2901
2902  var StatefulMixin = {
2903    /**
2904     * A hash containing arbitrary keys and values representing the state of
2905     * the object.
2906     *
2907     * @type {Object}
2908     */
2909    state: {},
2910
2911    /**
2912     * Set the state of an object by mutating its
2913     * {@link module:stateful~StatefulMixin.state|state} object in place.
2914     *
2915     * @fires   module:stateful~StatefulMixin#statechanged
2916     * @param   {Object|Function} stateUpdates
2917     *          A new set of properties to shallow-merge into the plugin state.
2918     *          Can be a plain object or a function returning a plain object.
2919     *
2920     * @return {Object|undefined}
2921     *          An object containing changes that occurred. If no changes
2922     *          occurred, returns `undefined`.
2923     */
2924    setState: function setState(stateUpdates) {
2925      var _this = this;
2926
2927      // Support providing the `stateUpdates` state as a function.
2928      if (typeof stateUpdates === 'function') {
2929        stateUpdates = stateUpdates();
2930      }
2931
2932      var changes;
2933      each(stateUpdates, function (value, key) {
2934        // Record the change if the value is different from what's in the
2935        // current state.
2936        if (_this.state[key] !== value) {
2937          changes = changes || {};
2938          changes[key] = {
2939            from: _this.state[key],
2940            to: value
2941          };
2942        }
2943
2944        _this.state[key] = value;
2945      }); // Only trigger "statechange" if there were changes AND we have a trigger
2946      // function. This allows us to not require that the target object be an
2947      // evented object.
2948
2949      if (changes && isEvented(this)) {
2950        /**
2951         * An event triggered on an object that is both
2952         * {@link module:stateful|stateful} and {@link module:evented|evented}
2953         * indicating that its state has changed.
2954         *
2955         * @event    module:stateful~StatefulMixin#statechanged
2956         * @type     {Object}
2957         * @property {Object} changes
2958         *           A hash containing the properties that were changed and
2959         *           the values they were changed `from` and `to`.
2960         */
2961        this.trigger({
2962          changes: changes,
2963          type: 'statechanged'
2964        });
2965      }
2966
2967      return changes;
2968    }
2969  };
2970  /**
2971   * Applies {@link module:stateful~StatefulMixin|StatefulMixin} to a target
2972   * object.
2973   *
2974   * If the target object is {@link module:evented|evented} and has a
2975   * `handleStateChanged` method, that method will be automatically bound to the
2976   * `statechanged` event on itself.
2977   *
2978   * @param   {Object} target
2979   *          The object to be made stateful.
2980   *
2981   * @param   {Object} [defaultState]
2982   *          A default set of properties to populate the newly-stateful object's
2983   *          `state` property.
2984   *
2985   * @return {Object}
2986   *          Returns the `target`.
2987   */
2988
2989  function stateful(target, defaultState) {
2990    assign(target, StatefulMixin); // This happens after the mixing-in because we need to replace the `state`
2991    // added in that step.
2992
2993    target.state = assign({}, target.state, defaultState); // Auto-bind the `handleStateChanged` method of the target object if it exists.
2994
2995    if (typeof target.handleStateChanged === 'function' && isEvented(target)) {
2996      target.on('statechanged', target.handleStateChanged);
2997    }
2998
2999    return target;
3000  }
3001
3002  /**
3003   * @file string-cases.js
3004   * @module to-lower-case
3005   */
3006
3007  /**
3008   * Lowercase the first letter of a string.
3009   *
3010   * @param {string} string
3011   *        String to be lowercased
3012   *
3013   * @return {string}
3014   *         The string with a lowercased first letter
3015   */
3016  var toLowerCase = function toLowerCase(string) {
3017    if (typeof string !== 'string') {
3018      return string;
3019    }
3020
3021    return string.replace(/./, function (w) {
3022      return w.toLowerCase();
3023    });
3024  };
3025  /**
3026   * Uppercase the first letter of a string.
3027   *
3028   * @param {string} string
3029   *        String to be uppercased
3030   *
3031   * @return {string}
3032   *         The string with an uppercased first letter
3033   */
3034
3035  var toTitleCase = function toTitleCase(string) {
3036    if (typeof string !== 'string') {
3037      return string;
3038    }
3039
3040    return string.replace(/./, function (w) {
3041      return w.toUpperCase();
3042    });
3043  };
3044  /**
3045   * Compares the TitleCase versions of the two strings for equality.
3046   *
3047   * @param {string} str1
3048   *        The first string to compare
3049   *
3050   * @param {string} str2
3051   *        The second string to compare
3052   *
3053   * @return {boolean}
3054   *         Whether the TitleCase versions of the strings are equal
3055   */
3056
3057  var titleCaseEquals = function titleCaseEquals(str1, str2) {
3058    return toTitleCase(str1) === toTitleCase(str2);
3059  };
3060
3061  /**
3062   * @file merge-options.js
3063   * @module merge-options
3064   */
3065  /**
3066   * Merge two objects recursively.
3067   *
3068   * Performs a deep merge like
3069   * {@link https://lodash.com/docs/4.17.10#merge|lodash.merge}, but only merges
3070   * plain objects (not arrays, elements, or anything else).
3071   *
3072   * Non-plain object values will be copied directly from the right-most
3073   * argument.
3074   *
3075   * @static
3076   * @param   {Object[]} sources
3077   *          One or more objects to merge into a new object.
3078   *
3079   * @return {Object}
3080   *          A new object that is the merged result of all sources.
3081   */
3082
3083  function mergeOptions() {
3084    var result = {};
3085
3086    for (var _len = arguments.length, sources = new Array(_len), _key = 0; _key < _len; _key++) {
3087      sources[_key] = arguments[_key];
3088    }
3089
3090    sources.forEach(function (source) {
3091      if (!source) {
3092        return;
3093      }
3094
3095      each(source, function (value, key) {
3096        if (!isPlain(value)) {
3097          result[key] = value;
3098          return;
3099        }
3100
3101        if (!isPlain(result[key])) {
3102          result[key] = {};
3103        }
3104
3105        result[key] = mergeOptions(result[key], value);
3106      });
3107    });
3108    return result;
3109  }
3110
3111  /**
3112   * Player Component - Base class for all UI objects
3113   *
3114   * @file component.js
3115   */
3116  /**
3117   * Base class for all UI Components.
3118   * Components are UI objects which represent both a javascript object and an element
3119   * in the DOM. They can be children of other components, and can have
3120   * children themselves.
3121   *
3122   * Components can also use methods from {@link EventTarget}
3123   */
3124
3125  var Component =
3126  /*#__PURE__*/
3127  function () {
3128    /**
3129     * A callback that is called when a component is ready. Does not have any
3130     * paramters and any callback value will be ignored.
3131     *
3132     * @callback Component~ReadyCallback
3133     * @this Component
3134     */
3135
3136    /**
3137     * Creates an instance of this class.
3138     *
3139     * @param {Player} player
3140     *        The `Player` that this class should be attached to.
3141     *
3142     * @param {Object} [options]
3143     *        The key/value store of player options.
3144     *
3145     * @param {Object[]} [options.children]
3146     *        An array of children objects to intialize this component with. Children objects have
3147     *        a name property that will be used if more than one component of the same type needs to be
3148     *        added.
3149     *
3150     * @param {Component~ReadyCallback} [ready]
3151     *        Function that gets called when the `Component` is ready.
3152     */
3153    function Component(player, options, ready) {
3154      // The component might be the player itself and we can't pass `this` to super
3155      if (!player && this.play) {
3156        this.player_ = player = this; // eslint-disable-line
3157      } else {
3158        this.player_ = player;
3159      } // Hold the reference to the parent component via `addChild` method
3160
3161
3162      this.parentComponent_ = null; // Make a copy of prototype.options_ to protect against overriding defaults
3163
3164      this.options_ = mergeOptions({}, this.options_); // Updated options with supplied options
3165
3166      options = this.options_ = mergeOptions(this.options_, options); // Get ID from options or options element if one is supplied
3167
3168      this.id_ = options.id || options.el && options.el.id; // If there was no ID from the options, generate one
3169
3170      if (!this.id_) {
3171        // Don't require the player ID function in the case of mock players
3172        var id = player && player.id && player.id() || 'no_player';
3173        this.id_ = id + "_component_" + newGUID();
3174      }
3175
3176      this.name_ = options.name || null; // Create element if one wasn't provided in options
3177
3178      if (options.el) {
3179        this.el_ = options.el;
3180      } else if (options.createEl !== false) {
3181        this.el_ = this.createEl();
3182      } // if evented is anything except false, we want to mixin in evented
3183
3184
3185      if (options.evented !== false) {
3186        // Make this an evented object and use `el_`, if available, as its event bus
3187        evented(this, {
3188          eventBusKey: this.el_ ? 'el_' : null
3189        });
3190      }
3191
3192      stateful(this, this.constructor.defaultState);
3193      this.children_ = [];
3194      this.childIndex_ = {};
3195      this.childNameIndex_ = {};
3196      this.setTimeoutIds_ = new Set();
3197      this.setIntervalIds_ = new Set();
3198      this.rafIds_ = new Set();
3199      this.clearingTimersOnDispose_ = false; // Add any child components in options
3200
3201      if (options.initChildren !== false) {
3202        this.initChildren();
3203      }
3204
3205      this.ready(ready); // Don't want to trigger ready here or it will before init is actually
3206      // finished for all children that run this constructor
3207
3208      if (options.reportTouchActivity !== false) {
3209        this.enableTouchActivity();
3210      }
3211    }
3212    /**
3213     * Dispose of the `Component` and all child components.
3214     *
3215     * @fires Component#dispose
3216     */
3217
3218
3219    var _proto = Component.prototype;
3220
3221    _proto.dispose = function dispose() {
3222      /**
3223       * Triggered when a `Component` is disposed.
3224       *
3225       * @event Component#dispose
3226       * @type {EventTarget~Event}
3227       *
3228       * @property {boolean} [bubbles=false]
3229       *           set to false so that the close event does not
3230       *           bubble up
3231       */
3232      this.trigger({
3233        type: 'dispose',
3234        bubbles: false
3235      }); // Dispose all children.
3236
3237      if (this.children_) {
3238        for (var i = this.children_.length - 1; i >= 0; i--) {
3239          if (this.children_[i].dispose) {
3240            this.children_[i].dispose();
3241          }
3242        }
3243      } // Delete child references
3244
3245
3246      this.children_ = null;
3247      this.childIndex_ = null;
3248      this.childNameIndex_ = null;
3249      this.parentComponent_ = null;
3250
3251      if (this.el_) {
3252        // Remove element from DOM
3253        if (this.el_.parentNode) {
3254          this.el_.parentNode.removeChild(this.el_);
3255        }
3256
3257        if (DomData.has(this.el_)) {
3258          DomData["delete"](this.el_);
3259        }
3260
3261        this.el_ = null;
3262      } // remove reference to the player after disposing of the element
3263
3264
3265      this.player_ = null;
3266    }
3267    /**
3268     * Return the {@link Player} that the `Component` has attached to.
3269     *
3270     * @return {Player}
3271     *         The player that this `Component` has attached to.
3272     */
3273    ;
3274
3275    _proto.player = function player() {
3276      return this.player_;
3277    }
3278    /**
3279     * Deep merge of options objects with new options.
3280     * > Note: When both `obj` and `options` contain properties whose values are objects.
3281     *         The two properties get merged using {@link module:mergeOptions}
3282     *
3283     * @param {Object} obj
3284     *        The object that contains new options.
3285     *
3286     * @return {Object}
3287     *         A new object of `this.options_` and `obj` merged together.
3288     */
3289    ;
3290
3291    _proto.options = function options(obj) {
3292      if (!obj) {
3293        return this.options_;
3294      }
3295
3296      this.options_ = mergeOptions(this.options_, obj);
3297      return this.options_;
3298    }
3299    /**
3300     * Get the `Component`s DOM element
3301     *
3302     * @return {Element}
3303     *         The DOM element for this `Component`.
3304     */
3305    ;
3306
3307    _proto.el = function el() {
3308      return this.el_;
3309    }
3310    /**
3311     * Create the `Component`s DOM element.
3312     *
3313     * @param {string} [tagName]
3314     *        Element's DOM node type. e.g. 'div'
3315     *
3316     * @param {Object} [properties]
3317     *        An object of properties that should be set.
3318     *
3319     * @param {Object} [attributes]
3320     *        An object of attributes that should be set.
3321     *
3322     * @return {Element}
3323     *         The element that gets created.
3324     */
3325    ;
3326
3327    _proto.createEl = function createEl$1(tagName, properties, attributes) {
3328      return createEl(tagName, properties, attributes);
3329    }
3330    /**
3331     * Localize a string given the string in english.
3332     *
3333     * If tokens are provided, it'll try and run a simple token replacement on the provided string.
3334     * The tokens it looks for look like `{1}` with the index being 1-indexed into the tokens array.
3335     *
3336     * If a `defaultValue` is provided, it'll use that over `string`,
3337     * if a value isn't found in provided language files.
3338     * This is useful if you want to have a descriptive key for token replacement
3339     * but have a succinct localized string and not require `en.json` to be included.
3340     *
3341     * Currently, it is used for the progress bar timing.
3342     * ```js
3343     * {
3344     *   "progress bar timing: currentTime={1} duration={2}": "{1} of {2}"
3345     * }
3346     * ```
3347     * It is then used like so:
3348     * ```js
3349     * this.localize('progress bar timing: currentTime={1} duration{2}',
3350     *               [this.player_.currentTime(), this.player_.duration()],
3351     *               '{1} of {2}');
3352     * ```
3353     *
3354     * Which outputs something like: `01:23 of 24:56`.
vendor: 43,927 bytes, lines 3355-4624
3355     *
3356     *
3357     * @param {string} string
3358     *        The string to localize and the key to lookup in the language files.
3359     * @param {string[]} [tokens]
3360     *        If the current item has token replacements, provide the tokens here.
3361     * @param {string} [defaultValue]
3362     *        Defaults to `string`. Can be a default value to use for token replacement
3363     *        if the lookup key is needed to be separate.
3364     *
3365     * @return {string}
3366     *         The localized string or if no localization exists the english string.
3367     */
3368    ;
3369
3370    _proto.localize = function localize(string, tokens, defaultValue) {
3371      if (defaultValue === void 0) {
3372        defaultValue = string;
3373      }
3374
3375      var code = this.player_.language && this.player_.language();
3376      var languages = this.player_.languages && this.player_.languages();
3377      var language = languages && languages[code];
3378      var primaryCode = code && code.split('-')[0];
3379      var primaryLang = languages && languages[primaryCode];
3380      var localizedString = defaultValue;
3381
3382      if (language && language[string]) {
3383        localizedString = language[string];
3384      } else if (primaryLang && primaryLang[string]) {
3385        localizedString = primaryLang[string];
3386      }
3387
3388      if (tokens) {
3389        localizedString = localizedString.replace(/\{(\d+)\}/g, function (match, index) {
3390          var value = tokens[index - 1];
3391          var ret = value;
3392
3393          if (typeof value === 'undefined') {
3394            ret = match;
3395          }
3396
3397          return ret;
3398        });
3399      }
3400
3401      return localizedString;
3402    }
3403    /**
3404     * Return the `Component`s DOM element. This is where children get inserted.
3405     * This will usually be the the same as the element returned in {@link Component#el}.
3406     *
3407     * @return {Element}
3408     *         The content element for this `Component`.
3409     */
3410    ;
3411
3412    _proto.contentEl = function contentEl() {
3413      return this.contentEl_ || this.el_;
3414    }
3415    /**
3416     * Get this `Component`s ID
3417     *
3418     * @return {string}
3419     *         The id of this `Component`
3420     */
3421    ;
3422
3423    _proto.id = function id() {
3424      return this.id_;
3425    }
3426    /**
3427     * Get the `Component`s name. The name gets used to reference the `Component`
3428     * and is set during registration.
3429     *
3430     * @return {string}
3431     *         The name of this `Component`.
3432     */
3433    ;
3434
3435    _proto.name = function name() {
3436      return this.name_;
3437    }
3438    /**
3439     * Get an array of all child components
3440     *
3441     * @return {Array}
3442     *         The children
3443     */
3444    ;
3445
3446    _proto.children = function children() {
3447      return this.children_;
3448    }
3449    /**
3450     * Returns the child `Component` with the given `id`.
3451     *
3452     * @param {string} id
3453     *        The id of the child `Component` to get.
3454     *
3455     * @return {Component|undefined}
3456     *         The child `Component` with the given `id` or undefined.
3457     */
3458    ;
3459
3460    _proto.getChildById = function getChildById(id) {
3461      return this.childIndex_[id];
3462    }
3463    /**
3464     * Returns the child `Component` with the given `name`.
3465     *
3466     * @param {string} name
3467     *        The name of the child `Component` to get.
3468     *
3469     * @return {Component|undefined}
3470     *         The child `Component` with the given `name` or undefined.
3471     */
3472    ;
3473
3474    _proto.getChild = function getChild(name) {
3475      if (!name) {
3476        return;
3477      }
3478
3479      return this.childNameIndex_[name];
3480    }
3481    /**
3482     * Add a child `Component` inside the current `Component`.
3483     *
3484     *
3485     * @param {string|Component} child
3486     *        The name or instance of a child to add.
3487     *
3488     * @param {Object} [options={}]
3489     *        The key/value store of options that will get passed to children of
3490     *        the child.
3491     *
3492     * @param {number} [index=this.children_.length]
3493     *        The index to attempt to add a child into.
3494     *
3495     * @return {Component}
3496     *         The `Component` that gets added as a child. When using a string the
3497     *         `Component` will get created by this process.
3498     */
3499    ;
3500
3501    _proto.addChild = function addChild(child, options, index) {
3502      if (options === void 0) {
3503        options = {};
3504      }
3505
3506      if (index === void 0) {
3507        index = this.children_.length;
3508      }
3509
3510      var component;
3511      var componentName; // If child is a string, create component with options
3512
3513      if (typeof child === 'string') {
3514        componentName = toTitleCase(child);
3515        var componentClassName = options.componentClass || componentName; // Set name through options
3516
3517        options.name = componentName; // Create a new object & element for this controls set
3518        // If there's no .player_, this is a player
3519
3520        var ComponentClass = Component.getComponent(componentClassName);
3521
3522        if (!ComponentClass) {
3523          throw new Error("Component " + componentClassName + " does not exist");
3524        } // data stored directly on the videojs object may be
3525        // misidentified as a component to retain
3526        // backwards-compatibility with 4.x. check to make sure the
3527        // component class can be instantiated.
3528
3529
3530        if (typeof ComponentClass !== 'function') {
3531          return null;
3532        }
3533
3534        component = new ComponentClass(this.player_ || this, options); // child is a component instance
3535      } else {
3536        component = child;
3537      }
3538
3539      if (component.parentComponent_) {
3540        component.parentComponent_.removeChild(component);
3541      }
3542
3543      this.children_.splice(index, 0, component);
3544      component.parentComponent_ = this;
3545
3546      if (typeof component.id === 'function') {
3547        this.childIndex_[component.id()] = component;
3548      } // If a name wasn't used to create the component, check if we can use the
3549      // name function of the component
3550
3551
3552      componentName = componentName || component.name && toTitleCase(component.name());
3553
3554      if (componentName) {
3555        this.childNameIndex_[componentName] = component;
3556        this.childNameIndex_[toLowerCase(componentName)] = component;
3557      } // Add the UI object's element to the container div (box)
3558      // Having an element is not required
3559
3560
3561      if (typeof component.el === 'function' && component.el()) {
3562        var childNodes = this.contentEl().children;
3563        var refNode = childNodes[index] || null;
3564        this.contentEl().insertBefore(component.el(), refNode);
3565      } // Return so it can stored on parent object if desired.
3566
3567
3568      return component;
3569    }
3570    /**
3571     * Remove a child `Component` from this `Component`s list of children. Also removes
3572     * the child `Component`s element from this `Component`s element.
3573     *
3574     * @param {Component} component
3575     *        The child `Component` to remove.
3576     */
3577    ;
3578
3579    _proto.removeChild = function removeChild(component) {
3580      if (typeof component === 'string') {
3581        component = this.getChild(component);
3582      }
3583
3584      if (!component || !this.children_) {
3585        return;
3586      }
3587
3588      var childFound = false;
3589
3590      for (var i = this.children_.length - 1; i >= 0; i--) {
3591        if (this.children_[i] === component) {
3592          childFound = true;
3593          this.children_.splice(i, 1);
3594          break;
3595        }
3596      }
3597
3598      if (!childFound) {
3599        return;
3600      }
3601
3602      component.parentComponent_ = null;
3603      this.childIndex_[component.id()] = null;
3604      this.childNameIndex_[toTitleCase(component.name())] = null;
3605      this.childNameIndex_[toLowerCase(component.name())] = null;
3606      var compEl = component.el();
3607
3608      if (compEl && compEl.parentNode === this.contentEl()) {
3609        this.contentEl().removeChild(component.el());
3610      }
3611    }
3612    /**
3613     * Add and initialize default child `Component`s based upon options.
3614     */
3615    ;
3616
3617    _proto.initChildren = function initChildren() {
3618      var _this = this;
3619
3620      var children = this.options_.children;
3621
3622      if (children) {
3623        // `this` is `parent`
3624        var parentOptions = this.options_;
3625
3626        var handleAdd = function handleAdd(child) {
3627          var name = child.name;
3628          var opts = child.opts; // Allow options for children to be set at the parent options
3629          // e.g. videojs(id, { controlBar: false });
3630          // instead of videojs(id, { children: { controlBar: false });
3631
3632          if (parentOptions[name] !== undefined) {
3633            opts = parentOptions[name];
3634          } // Allow for disabling default components
3635          // e.g. options['children']['posterImage'] = false
3636
3637
3638          if (opts === false) {
3639            return;
3640          } // Allow options to be passed as a simple boolean if no configuration
3641          // is necessary.
3642
3643
3644          if (opts === true) {
3645            opts = {};
3646          } // We also want to pass the original player options
3647          // to each component as well so they don't need to
3648          // reach back into the player for options later.
3649
3650
3651          opts.playerOptions = _this.options_.playerOptions; // Create and add the child component.
3652          // Add a direct reference to the child by name on the parent instance.
3653          // If two of the same component are used, different names should be supplied
3654          // for each
3655
3656          var newChild = _this.addChild(name, opts);
3657
3658          if (newChild) {
3659            _this[name] = newChild;
3660          }
3661        }; // Allow for an array of children details to passed in the options
3662
3663
3664        var workingChildren;
3665        var Tech = Component.getComponent('Tech');
3666
3667        if (Array.isArray(children)) {
3668          workingChildren = children;
3669        } else {
3670          workingChildren = Object.keys(children);
3671        }
3672
3673        workingChildren // children that are in this.options_ but also in workingChildren  would
3674        // give us extra children we do not want. So, we want to filter them out.
3675        .concat(Object.keys(this.options_).filter(function (child) {
3676          return !workingChildren.some(function (wchild) {
3677            if (typeof wchild === 'string') {
3678              return child === wchild;
3679            }
3680
3681            return child === wchild.name;
3682          });
3683        })).map(function (child) {
3684          var name;
3685          var opts;
3686
3687          if (typeof child === 'string') {
3688            name = child;
3689            opts = children[name] || _this.options_[name] || {};
3690          } else {
3691            name = child.name;
3692            opts = child;
3693          }
3694
3695          return {
3696            name: name,
3697            opts: opts
3698          };
3699        }).filter(function (child) {
3700          // we have to make sure that child.name isn't in the techOrder since
3701          // techs are registerd as Components but can't aren't compatible
3702          // See https://github.com/videojs/video.js/issues/2772
3703          var c = Component.getComponent(child.opts.componentClass || toTitleCase(child.name));
3704          return c && !Tech.isTech(c);
3705        }).forEach(handleAdd);
3706      }
3707    }
3708    /**
3709     * Builds the default DOM class name. Should be overriden by sub-components.
3710     *
3711     * @return {string}
3712     *         The DOM class name for this object.
3713     *
3714     * @abstract
3715     */
3716    ;
3717
3718    _proto.buildCSSClass = function buildCSSClass() {
3719      // Child classes can include a function that does:
3720      // return 'CLASS NAME' + this._super();
3721      return '';
3722    }
3723    /**
3724     * Bind a listener to the component's ready state.
3725     * Different from event listeners in that if the ready event has already happened
3726     * it will trigger the function immediately.
3727     *
3728     * @return {Component}
3729     *         Returns itself; method can be chained.
3730     */
3731    ;
3732
3733    _proto.ready = function ready(fn, sync) {
3734      if (sync === void 0) {
3735        sync = false;
3736      }
3737
3738      if (!fn) {
3739        return;
3740      }
3741
3742      if (!this.isReady_) {
3743        this.readyQueue_ = this.readyQueue_ || [];
3744        this.readyQueue_.push(fn);
3745        return;
3746      }
3747
3748      if (sync) {
3749        fn.call(this);
3750      } else {
3751        // Call the function asynchronously by default for consistency
3752        this.setTimeout(fn, 1);
3753      }
3754    }
3755    /**
3756     * Trigger all the ready listeners for this `Component`.
3757     *
3758     * @fires Component#ready
3759     */
3760    ;
3761
3762    _proto.triggerReady = function triggerReady() {
3763      this.isReady_ = true; // Ensure ready is triggered asynchronously
3764
3765      this.setTimeout(function () {
3766        var readyQueue = this.readyQueue_; // Reset Ready Queue
3767
3768        this.readyQueue_ = [];
3769
3770        if (readyQueue && readyQueue.length > 0) {
3771          readyQueue.forEach(function (fn) {
3772            fn.call(this);
3773          }, this);
3774        } // Allow for using event listeners also
3775
3776        /**
3777         * Triggered when a `Component` is ready.
3778         *
3779         * @event Component#ready
3780         * @type {EventTarget~Event}
3781         */
3782
3783
3784        this.trigger('ready');
3785      }, 1);
3786    }
3787    /**
3788     * Find a single DOM element matching a `selector`. This can be within the `Component`s
3789     * `contentEl()` or another custom context.
3790     *
3791     * @param {string} selector
3792     *        A valid CSS selector, which will be passed to `querySelector`.
3793     *
3794     * @param {Element|string} [context=this.contentEl()]
3795     *        A DOM element within which to query. Can also be a selector string in
3796     *        which case the first matching element will get used as context. If
3797     *        missing `this.contentEl()` gets used. If  `this.contentEl()` returns
3798     *        nothing it falls back to `document`.
3799     *
3800     * @return {Element|null}
3801     *         the dom element that was found, or null
3802     *
3803     * @see [Information on CSS Selectors](https://developer.mozilla.org/en-US/docs/Web/Guide/CSS/Getting_Started/Selectors)
3804     */
3805    ;
3806
3807    _proto.$ = function $$1(selector, context) {
3808      return $(selector, context || this.contentEl());
3809    }
3810    /**
3811     * Finds all DOM element matching a `selector`. This can be within the `Component`s
3812     * `contentEl()` or another custom context.
3813     *
3814     * @param {string} selector
3815     *        A valid CSS selector, which will be passed to `querySelectorAll`.
3816     *
3817     * @param {Element|string} [context=this.contentEl()]
3818     *        A DOM element within which to query. Can also be a selector string in
3819     *        which case the first matching element will get used as context. If
3820     *        missing `this.contentEl()` gets used. If  `this.contentEl()` returns
3821     *        nothing it falls back to `document`.
3822     *
3823     * @return {NodeList}
3824     *         a list of dom elements that were found
3825     *
3826     * @see [Information on CSS Selectors](https://developer.mozilla.org/en-US/docs/Web/Guide/CSS/Getting_Started/Selectors)
3827     */
3828    ;
3829
3830    _proto.$$ = function $$$1(selector, context) {
3831      return $$(selector, context || this.contentEl());
3832    }
3833    /**
3834     * Check if a component's element has a CSS class name.
3835     *
3836     * @param {string} classToCheck
3837     *        CSS class name to check.
3838     *
3839     * @return {boolean}
3840     *         - True if the `Component` has the class.
3841     *         - False if the `Component` does not have the class`
3842     */
3843    ;
3844
3845    _proto.hasClass = function hasClass$1(classToCheck) {
3846      return hasClass(this.el_, classToCheck);
3847    }
3848    /**
3849     * Add a CSS class name to the `Component`s element.
3850     *
3851     * @param {string} classToAdd
3852     *        CSS class name to add
3853     */
3854    ;
3855
3856    _proto.addClass = function addClass$1(classToAdd) {
3857      addClass(this.el_, classToAdd);
3858    }
3859    /**
3860     * Remove a CSS class name from the `Component`s element.
3861     *
3862     * @param {string} classToRemove
3863     *        CSS class name to remove
3864     */
3865    ;
3866
3867    _proto.removeClass = function removeClass$1(classToRemove) {
3868      removeClass(this.el_, classToRemove);
3869    }
3870    /**
3871     * Add or remove a CSS class name from the component's element.
3872     * - `classToToggle` gets added when {@link Component#hasClass} would return false.
3873     * - `classToToggle` gets removed when {@link Component#hasClass} would return true.
3874     *
3875     * @param  {string} classToToggle
3876     *         The class to add or remove based on (@link Component#hasClass}
3877     *
3878     * @param  {boolean|Dom~predicate} [predicate]
3879     *         An {@link Dom~predicate} function or a boolean
3880     */
3881    ;
3882
3883    _proto.toggleClass = function toggleClass$1(classToToggle, predicate) {
3884      toggleClass(this.el_, classToToggle, predicate);
3885    }
3886    /**
3887     * Show the `Component`s element if it is hidden by removing the
3888     * 'vjs-hidden' class name from it.
3889     */
3890    ;
3891
3892    _proto.show = function show() {
3893      this.removeClass('vjs-hidden');
3894    }
3895    /**
3896     * Hide the `Component`s element if it is currently showing by adding the
3897     * 'vjs-hidden` class name to it.
3898     */
3899    ;
3900
3901    _proto.hide = function hide() {
3902      this.addClass('vjs-hidden');
3903    }
3904    /**
3905     * Lock a `Component`s element in its visible state by adding the 'vjs-lock-showing'
3906     * class name to it. Used during fadeIn/fadeOut.
3907     *
3908     * @private
3909     */
3910    ;
3911
3912    _proto.lockShowing = function lockShowing() {
3913      this.addClass('vjs-lock-showing');
3914    }
3915    /**
3916     * Unlock a `Component`s element from its visible state by removing the 'vjs-lock-showing'
3917     * class name from it. Used during fadeIn/fadeOut.
3918     *
3919     * @private
3920     */
3921    ;
3922
3923    _proto.unlockShowing = function unlockShowing() {
3924      this.removeClass('vjs-lock-showing');
3925    }
3926    /**
3927     * Get the value of an attribute on the `Component`s element.
3928     *
3929     * @param {string} attribute
3930     *        Name of the attribute to get the value from.
3931     *
3932     * @return {string|null}
3933     *         - The value of the attribute that was asked for.
3934     *         - Can be an empty string on some browsers if the attribute does not exist
3935     *           or has no value
3936     *         - Most browsers will return null if the attibute does not exist or has
3937     *           no value.
3938     *
3939     * @see [DOM API]{@link https://developer.mozilla.org/en-US/docs/Web/API/Element/getAttribute}
3940     */
3941    ;
3942
3943    _proto.getAttribute = function getAttribute$1(attribute) {
3944      return getAttribute(this.el_, attribute);
3945    }
3946    /**
3947     * Set the value of an attribute on the `Component`'s element
3948     *
3949     * @param {string} attribute
3950     *        Name of the attribute to set.
3951     *
3952     * @param {string} value
3953     *        Value to set the attribute to.
3954     *
3955     * @see [DOM API]{@link https://developer.mozilla.org/en-US/docs/Web/API/Element/setAttribute}
3956     */
3957    ;
3958
3959    _proto.setAttribute = function setAttribute$1(attribute, value) {
3960      setAttribute(this.el_, attribute, value);
3961    }
3962    /**
3963     * Remove an attribute from the `Component`s element.
3964     *
3965     * @param {string} attribute
3966     *        Name of the attribute to remove.
3967     *
3968     * @see [DOM API]{@link https://developer.mozilla.org/en-US/docs/Web/API/Element/removeAttribute}
3969     */
3970    ;
3971
3972    _proto.removeAttribute = function removeAttribute$1(attribute) {
3973      removeAttribute(this.el_, attribute);
3974    }
3975    /**
3976     * Get or set the width of the component based upon the CSS styles.
3977     * See {@link Component#dimension} for more detailed information.
3978     *
3979     * @param {number|string} [num]
3980     *        The width that you want to set postfixed with '%', 'px' or nothing.
3981     *
3982     * @param {boolean} [skipListeners]
3983     *        Skip the componentresize event trigger
3984     *
3985     * @return {number|string}
3986     *         The width when getting, zero if there is no width. Can be a string
3987     *           postpixed with '%' or 'px'.
3988     */
3989    ;
3990
3991    _proto.width = function width(num, skipListeners) {
3992      return this.dimension('width', num, skipListeners);
3993    }
3994    /**
3995     * Get or set the height of the component based upon the CSS styles.
3996     * See {@link Component#dimension} for more detailed information.
3997     *
3998     * @param {number|string} [num]
3999     *        The height that you want to set postfixed with '%', 'px' or nothing.
4000     *
4001     * @param {boolean} [skipListeners]
4002     *        Skip the componentresize event trigger
4003     *
4004     * @return {number|string}
4005     *         The width when getting, zero if there is no width. Can be a string
4006     *         postpixed with '%' or 'px'.
4007     */
4008    ;
4009
4010    _proto.height = function height(num, skipListeners) {
4011      return this.dimension('height', num, skipListeners);
4012    }
4013    /**
4014     * Set both the width and height of the `Component` element at the same time.
4015     *
4016     * @param  {number|string} width
4017     *         Width to set the `Component`s element to.
4018     *
4019     * @param  {number|string} height
4020     *         Height to set the `Component`s element to.
4021     */
4022    ;
4023
4024    _proto.dimensions = function dimensions(width, height) {
4025      // Skip componentresize listeners on width for optimization
4026      this.width(width, true);
4027      this.height(height);
4028    }
4029    /**
4030     * Get or set width or height of the `Component` element. This is the shared code
4031     * for the {@link Component#width} and {@link Component#height}.
4032     *
4033     * Things to know:
4034     * - If the width or height in an number this will return the number postfixed with 'px'.
4035     * - If the width/height is a percent this will return the percent postfixed with '%'
4036     * - Hidden elements have a width of 0 with `window.getComputedStyle`. This function
4037     *   defaults to the `Component`s `style.width` and falls back to `window.getComputedStyle`.
4038     *   See [this]{@link http://www.foliotek.com/devblog/getting-the-width-of-a-hidden-element-with-jquery-using-width/}
4039     *   for more information
4040     * - If you want the computed style of the component, use {@link Component#currentWidth}
4041     *   and {@link {Component#currentHeight}
4042     *
4043     * @fires Component#componentresize
4044     *
4045     * @param {string} widthOrHeight
4046     8        'width' or 'height'
4047     *
4048     * @param  {number|string} [num]
4049     8         New dimension
4050     *
4051     * @param  {boolean} [skipListeners]
4052     *         Skip componentresize event trigger
4053     *
4054     * @return {number}
4055     *         The dimension when getting or 0 if unset
4056     */
4057    ;
4058
4059    _proto.dimension = function dimension(widthOrHeight, num, skipListeners) {
4060      if (num !== undefined) {
4061        // Set to zero if null or literally NaN (NaN !== NaN)
4062        if (num === null || num !== num) {
4063          num = 0;
4064        } // Check if using css width/height (% or px) and adjust
4065
4066
4067        if (('' + num).indexOf('%') !== -1 || ('' + num).indexOf('px') !== -1) {
4068          this.el_.style[widthOrHeight] = num;
4069        } else if (num === 'auto') {
4070          this.el_.style[widthOrHeight] = '';
4071        } else {
4072          this.el_.style[widthOrHeight] = num + 'px';
4073        } // skipListeners allows us to avoid triggering the resize event when setting both width and height
4074
4075
4076        if (!skipListeners) {
4077          /**
4078           * Triggered when a component is resized.
4079           *
4080           * @event Component#componentresize
4081           * @type {EventTarget~Event}
4082           */
4083          this.trigger('componentresize');
4084        }
4085
4086        return;
4087      } // Not setting a value, so getting it
4088      // Make sure element exists
4089
4090
4091      if (!this.el_) {
4092        return 0;
4093      } // Get dimension value from style
4094
4095
4096      var val = this.el_.style[widthOrHeight];
4097      var pxIndex = val.indexOf('px');
4098
4099      if (pxIndex !== -1) {
4100        // Return the pixel value with no 'px'
4101        return parseInt(val.slice(0, pxIndex), 10);
4102      } // No px so using % or no style was set, so falling back to offsetWidth/height
4103      // If component has display:none, offset will return 0
4104      // TODO: handle display:none and no dimension style using px
4105
4106
4107      return parseInt(this.el_['offset' + toTitleCase(widthOrHeight)], 10);
4108    }
4109    /**
4110     * Get the computed width or the height of the component's element.
4111     *
4112     * Uses `window.getComputedStyle`.
4113     *
4114     * @param {string} widthOrHeight
4115     *        A string containing 'width' or 'height'. Whichever one you want to get.
4116     *
4117     * @return {number}
4118     *         The dimension that gets asked for or 0 if nothing was set
4119     *         for that dimension.
4120     */
4121    ;
4122
4123    _proto.currentDimension = function currentDimension(widthOrHeight) {
4124      var computedWidthOrHeight = 0;
4125
4126      if (widthOrHeight !== 'width' && widthOrHeight !== 'height') {
4127        throw new Error('currentDimension only accepts width or height value');
4128      }
4129
4130      computedWidthOrHeight = computedStyle(this.el_, widthOrHeight); // remove 'px' from variable and parse as integer
4131
4132      computedWidthOrHeight = parseFloat(computedWidthOrHeight); // if the computed value is still 0, it's possible that the browser is lying
4133      // and we want to check the offset values.
4134      // This code also runs wherever getComputedStyle doesn't exist.
4135
4136      if (computedWidthOrHeight === 0 || isNaN(computedWidthOrHeight)) {
4137        var rule = "offset" + toTitleCase(widthOrHeight);
4138        computedWidthOrHeight = this.el_[rule];
4139      }
4140
4141      return computedWidthOrHeight;
4142    }
4143    /**
4144     * An object that contains width and height values of the `Component`s
4145     * computed style. Uses `window.getComputedStyle`.
4146     *
4147     * @typedef {Object} Component~DimensionObject
4148     *
4149     * @property {number} width
4150     *           The width of the `Component`s computed style.
4151     *
4152     * @property {number} height
4153     *           The height of the `Component`s computed style.
4154     */
4155
4156    /**
4157     * Get an object that contains computed width and height values of the
4158     * component's element.
4159     *
4160     * Uses `window.getComputedStyle`.
4161     *
4162     * @return {Component~DimensionObject}
4163     *         The computed dimensions of the component's element.
4164     */
4165    ;
4166
4167    _proto.currentDimensions = function currentDimensions() {
4168      return {
4169        width: this.currentDimension('width'),
4170        height: this.currentDimension('height')
4171      };
4172    }
4173    /**
4174     * Get the computed width of the component's element.
4175     *
4176     * Uses `window.getComputedStyle`.
4177     *
4178     * @return {number}
4179     *         The computed width of the component's element.
4180     */
4181    ;
4182
4183    _proto.currentWidth = function currentWidth() {
4184      return this.currentDimension('width');
4185    }
4186    /**
4187     * Get the computed height of the component's element.
4188     *
4189     * Uses `window.getComputedStyle`.
4190     *
4191     * @return {number}
4192     *         The computed height of the component's element.
4193     */
4194    ;
4195
4196    _proto.currentHeight = function currentHeight() {
4197      return this.currentDimension('height');
4198    }
4199    /**
4200     * Set the focus to this component
4201     */
4202    ;
4203
4204    _proto.focus = function focus() {
4205      this.el_.focus();
4206    }
4207    /**
4208     * Remove the focus from this component
4209     */
4210    ;
4211
4212    _proto.blur = function blur() {
4213      this.el_.blur();
4214    }
4215    /**
4216     * When this Component receives a `keydown` event which it does not process,
4217     *  it passes the event to the Player for handling.
4218     *
4219     * @param {EventTarget~Event} event
4220     *        The `keydown` event that caused this function to be called.
4221     */
4222    ;
4223
4224    _proto.handleKeyDown = function handleKeyDown(event) {
4225      if (this.player_) {
4226        // We only stop propagation here because we want unhandled events to fall
4227        // back to the browser.
4228        event.stopPropagation();
4229        this.player_.handleKeyDown(event);
4230      }
4231    }
4232    /**
4233     * Many components used to have a `handleKeyPress` method, which was poorly
4234     * named because it listened to a `keydown` event. This method name now
4235     * delegates to `handleKeyDown`. This means anyone calling `handleKeyPress`
4236     * will not see their method calls stop working.
4237     *
4238     * @param {EventTarget~Event} event
4239     *        The event that caused this function to be called.
4240     */
4241    ;
4242
4243    _proto.handleKeyPress = function handleKeyPress(event) {
4244      this.handleKeyDown(event);
4245    }
4246    /**
4247     * Emit a 'tap' events when touch event support gets detected. This gets used to
4248     * support toggling the controls through a tap on the video. They get enabled
4249     * because every sub-component would have extra overhead otherwise.
4250     *
4251     * @private
4252     * @fires Component#tap
4253     * @listens Component#touchstart
4254     * @listens Component#touchmove
4255     * @listens Component#touchleave
4256     * @listens Component#touchcancel
4257     * @listens Component#touchend
4258      */
4259    ;
4260
4261    _proto.emitTapEvents = function emitTapEvents() {
4262      // Track the start time so we can determine how long the touch lasted
4263      var touchStart = 0;
4264      var firstTouch = null; // Maximum movement allowed during a touch event to still be considered a tap
4265      // Other popular libs use anywhere from 2 (hammer.js) to 15,
4266      // so 10 seems like a nice, round number.
4267
4268      var tapMovementThreshold = 10; // The maximum length a touch can be while still being considered a tap
4269
4270      var touchTimeThreshold = 200;
4271      var couldBeTap;
4272      this.on('touchstart', function (event) {
4273        // If more than one finger, don't consider treating this as a click
4274        if (event.touches.length === 1) {
4275          // Copy pageX/pageY from the object
4276          firstTouch = {
4277            pageX: event.touches[0].pageX,
4278            pageY: event.touches[0].pageY
4279          }; // Record start time so we can detect a tap vs. "touch and hold"
4280
4281          touchStart = window$1.performance.now(); // Reset couldBeTap tracking
4282
4283          couldBeTap = true;
4284        }
4285      });
4286      this.on('touchmove', function (event) {
4287        // If more than one finger, don't consider treating this as a click
4288        if (event.touches.length > 1) {
4289          couldBeTap = false;
4290        } else if (firstTouch) {
4291          // Some devices will throw touchmoves for all but the slightest of taps.
4292          // So, if we moved only a small distance, this could still be a tap
4293          var xdiff = event.touches[0].pageX - firstTouch.pageX;
4294          var ydiff = event.touches[0].pageY - firstTouch.pageY;
4295          var touchDistance = Math.sqrt(xdiff * xdiff + ydiff * ydiff);
4296
4297          if (touchDistance > tapMovementThreshold) {
4298            couldBeTap = false;
4299          }
4300        }
4301      });
4302
4303      var noTap = function noTap() {
4304        couldBeTap = false;
4305      }; // TODO: Listen to the original target. http://youtu.be/DujfpXOKUp8?t=13m8s
4306
4307
4308      this.on('touchleave', noTap);
4309      this.on('touchcancel', noTap); // When the touch ends, measure how long it took and trigger the appropriate
4310      // event
4311
4312      this.on('touchend', function (event) {
4313        firstTouch = null; // Proceed only if the touchmove/leave/cancel event didn't happen
4314
4315        if (couldBeTap === true) {
4316          // Measure how long the touch lasted
4317          var touchTime = window$1.performance.now() - touchStart; // Make sure the touch was less than the threshold to be considered a tap
4318
4319          if (touchTime < touchTimeThreshold) {
4320            // Don't let browser turn this into a click
4321            event.preventDefault();
4322            /**
4323             * Triggered when a `Component` is tapped.
4324             *
4325             * @event Component#tap
4326             * @type {EventTarget~Event}
4327             */
4328
4329            this.trigger('tap'); // It may be good to copy the touchend event object and change the
4330            // type to tap, if the other event properties aren't exact after
4331            // Events.fixEvent runs (e.g. event.target)
4332          }
4333        }
4334      });
4335    }
4336    /**
4337     * This function reports user activity whenever touch events happen. This can get
4338     * turned off by any sub-components that wants touch events to act another way.
4339     *
4340     * Report user touch activity when touch events occur. User activity gets used to
4341     * determine when controls should show/hide. It is simple when it comes to mouse
4342     * events, because any mouse event should show the controls. So we capture mouse
4343     * events that bubble up to the player and report activity when that happens.
4344     * With touch events it isn't as easy as `touchstart` and `touchend` toggle player
4345     * controls. So touch events can't help us at the player level either.
4346     *
4347     * User activity gets checked asynchronously. So what could happen is a tap event
4348     * on the video turns the controls off. Then the `touchend` event bubbles up to
4349     * the player. Which, if it reported user activity, would turn the controls right
4350     * back on. We also don't want to completely block touch events from bubbling up.
4351     * Furthermore a `touchmove` event and anything other than a tap, should not turn
4352     * controls back on.
4353     *
4354     * @listens Component#touchstart
4355     * @listens Component#touchmove
4356     * @listens Component#touchend
4357     * @listens Component#touchcancel
4358     */
4359    ;
4360
4361    _proto.enableTouchActivity = function enableTouchActivity() {
4362      // Don't continue if the root player doesn't support reporting user activity
4363      if (!this.player() || !this.player().reportUserActivity) {
4364        return;
4365      } // listener for reporting that the user is active
4366
4367
4368      var report = bind(this.player(), this.player().reportUserActivity);
4369      var touchHolding;
4370      this.on('touchstart', function () {
4371        report(); // For as long as the they are touching the device or have their mouse down,
4372        // we consider them active even if they're not moving their finger or mouse.
4373        // So we want to continue to update that they are active
4374
4375        this.clearInterval(touchHolding); // report at the same interval as activityCheck
4376
4377        touchHolding = this.setInterval(report, 250);
4378      });
4379
4380      var touchEnd = function touchEnd(event) {
4381        report(); // stop the interval that maintains activity if the touch is holding
4382
4383        this.clearInterval(touchHolding);
4384      };
4385
4386      this.on('touchmove', report);
4387      this.on('touchend', touchEnd);
4388      this.on('touchcancel', touchEnd);
4389    }
4390    /**
4391     * A callback that has no parameters and is bound into `Component`s context.
4392     *
4393     * @callback Component~GenericCallback
4394     * @this Component
4395     */
4396
4397    /**
4398     * Creates a function that runs after an `x` millisecond timeout. This function is a
4399     * wrapper around `window.setTimeout`. There are a few reasons to use this one
4400     * instead though:
4401     * 1. It gets cleared via  {@link Component#clearTimeout} when
4402     *    {@link Component#dispose} gets called.
4403     * 2. The function callback will gets turned into a {@link Component~GenericCallback}
4404     *
4405     * > Note: You can't use `window.clearTimeout` on the id returned by this function. This
4406     *         will cause its dispose listener not to get cleaned up! Please use
4407     *         {@link Component#clearTimeout} or {@link Component#dispose} instead.
4408     *
4409     * @param {Component~GenericCallback} fn
4410     *        The function that will be run after `timeout`.
4411     *
4412     * @param {number} timeout
4413     *        Timeout in milliseconds to delay before executing the specified function.
4414     *
4415     * @return {number}
4416     *         Returns a timeout ID that gets used to identify the timeout. It can also
4417     *         get used in {@link Component#clearTimeout} to clear the timeout that
4418     *         was set.
4419     *
4420     * @listens Component#dispose
4421     * @see [Similar to]{@link https://developer.mozilla.org/en-US/docs/Web/API/WindowTimers/setTimeout}
4422     */
4423    ;
4424
4425    _proto.setTimeout = function setTimeout(fn, timeout) {
4426      var _this2 = this;
4427
4428      // declare as variables so they are properly available in timeout function
4429      // eslint-disable-next-line
4430      var timeoutId;
4431      fn = bind(this, fn);
4432      this.clearTimersOnDispose_();
4433      timeoutId = window$1.setTimeout(function () {
4434        if (_this2.setTimeoutIds_.has(timeoutId)) {
4435          _this2.setTimeoutIds_["delete"](timeoutId);
4436        }
4437
4438        fn();
4439      }, timeout);
4440      this.setTimeoutIds_.add(timeoutId);
4441      return timeoutId;
4442    }
4443    /**
4444     * Clears a timeout that gets created via `window.setTimeout` or
4445     * {@link Component#setTimeout}. If you set a timeout via {@link Component#setTimeout}
4446     * use this function instead of `window.clearTimout`. If you don't your dispose
4447     * listener will not get cleaned up until {@link Component#dispose}!
4448     *
4449     * @param {number} timeoutId
4450     *        The id of the timeout to clear. The return value of
4451     *        {@link Component#setTimeout} or `window.setTimeout`.
4452     *
4453     * @return {number}
4454     *         Returns the timeout id that was cleared.
4455     *
4456     * @see [Similar to]{@link https://developer.mozilla.org/en-US/docs/Web/API/WindowTimers/clearTimeout}
4457     */
4458    ;
4459
4460    _proto.clearTimeout = function clearTimeout(timeoutId) {
4461      if (this.setTimeoutIds_.has(timeoutId)) {
4462        this.setTimeoutIds_["delete"](timeoutId);
4463        window$1.clearTimeout(timeoutId);
4464      }
4465
4466      return timeoutId;
4467    }
4468    /**
4469     * Creates a function that gets run every `x` milliseconds. This function is a wrapper
4470     * around `window.setInterval`. There are a few reasons to use this one instead though.
4471     * 1. It gets cleared via  {@link Component#clearInterval} when
4472     *    {@link Component#dispose} gets called.
4473     * 2. The function callback will be a {@link Component~GenericCallback}
4474     *
4475     * @param {Component~GenericCallback} fn
4476     *        The function to run every `x` seconds.
4477     *
4478     * @param {number} interval
4479     *        Execute the specified function every `x` milliseconds.
4480     *
4481     * @return {number}
4482     *         Returns an id that can be used to identify the interval. It can also be be used in
4483     *         {@link Component#clearInterval} to clear the interval.
4484     *
4485     * @listens Component#dispose
4486     * @see [Similar to]{@link https://developer.mozilla.org/en-US/docs/Web/API/WindowTimers/setInterval}
4487     */
4488    ;
4489
4490    _proto.setInterval = function setInterval(fn, interval) {
4491      fn = bind(this, fn);
4492      this.clearTimersOnDispose_();
4493      var intervalId = window$1.setInterval(fn, interval);
4494      this.setIntervalIds_.add(intervalId);
4495      return intervalId;
4496    }
4497    /**
4498     * Clears an interval that gets created via `window.setInterval` or
4499     * {@link Component#setInterval}. If you set an inteval via {@link Component#setInterval}
4500     * use this function instead of `window.clearInterval`. If you don't your dispose
4501     * listener will not get cleaned up until {@link Component#dispose}!
4502     *
4503     * @param {number} intervalId
4504     *        The id of the interval to clear. The return value of
4505     *        {@link Component#setInterval} or `window.setInterval`.
4506     *
4507     * @return {number}
4508     *         Returns the interval id that was cleared.
4509     *
4510     * @see [Similar to]{@link https://developer.mozilla.org/en-US/docs/Web/API/WindowTimers/clearInterval}
4511     */
4512    ;
4513
4514    _proto.clearInterval = function clearInterval(intervalId) {
4515      if (this.setIntervalIds_.has(intervalId)) {
4516        this.setIntervalIds_["delete"](intervalId);
4517        window$1.clearInterval(intervalId);
4518      }
4519
4520      return intervalId;
4521    }
4522    /**
4523     * Queues up a callback to be passed to requestAnimationFrame (rAF), but
4524     * with a few extra bonuses:
4525     *
4526     * - Supports browsers that do not support rAF by falling back to
4527     *   {@link Component#setTimeout}.
4528     *
4529     * - The callback is turned into a {@link Component~GenericCallback} (i.e.
4530     *   bound to the component).
4531     *
4532     * - Automatic cancellation of the rAF callback is handled if the component
4533     *   is disposed before it is called.
4534     *
4535     * @param  {Component~GenericCallback} fn
4536     *         A function that will be bound to this component and executed just
4537     *         before the browser's next repaint.
4538     *
4539     * @return {number}
4540     *         Returns an rAF ID that gets used to identify the timeout. It can
4541     *         also be used in {@link Component#cancelAnimationFrame} to cancel
4542     *         the animation frame callback.
4543     *
4544     * @listens Component#dispose
4545     * @see [Similar to]{@link https://developer.mozilla.org/en-US/docs/Web/API/window/requestAnimationFrame}
4546     */
4547    ;
4548
4549    _proto.requestAnimationFrame = function requestAnimationFrame(fn) {
4550      var _this3 = this;
4551
4552      // Fall back to using a timer.
4553      if (!this.supportsRaf_) {
4554        return this.setTimeout(fn, 1000 / 60);
4555      }
4556
4557      this.clearTimersOnDispose_(); // declare as variables so they are properly available in rAF function
4558      // eslint-disable-next-line
4559
4560      var id;
4561      fn = bind(this, fn);
4562      id = window$1.requestAnimationFrame(function () {
4563        if (_this3.rafIds_.has(id)) {
4564          _this3.rafIds_["delete"](id);
4565        }
4566
4567        fn();
4568      });
4569      this.rafIds_.add(id);
4570      return id;
4571    }
4572    /**
4573     * Cancels a queued callback passed to {@link Component#requestAnimationFrame}
4574     * (rAF).
4575     *
4576     * If you queue an rAF callback via {@link Component#requestAnimationFrame},
4577     * use this function instead of `window.cancelAnimationFrame`. If you don't,
4578     * your dispose listener will not get cleaned up until {@link Component#dispose}!
4579     *
4580     * @param {number} id
4581     *        The rAF ID to clear. The return value of {@link Component#requestAnimationFrame}.
4582     *
4583     * @return {number}
4584     *         Returns the rAF ID that was cleared.
4585     *
4586     * @see [Similar to]{@link https://developer.mozilla.org/en-US/docs/Web/API/window/cancelAnimationFrame}
4587     */
4588    ;
4589
4590    _proto.cancelAnimationFrame = function cancelAnimationFrame(id) {
4591      // Fall back to using a timer.
4592      if (!this.supportsRaf_) {
4593        return this.clearTimeout(id);
4594      }
4595
4596      if (this.rafIds_.has(id)) {
4597        this.rafIds_["delete"](id);
4598        window$1.cancelAnimationFrame(id);
4599      }
4600
4601      return id;
4602    }
4603    /**
4604     * A function to setup `requestAnimationFrame`, `setTimeout`,
4605     * and `setInterval`, clearing on dispose.
4606     *
4607     * > Previously each timer added and removed dispose listeners on it's own.
4608     * For better performance it was decided to batch them all, and use `Set`s
4609     * to track outstanding timer ids.
4610     *
4611     * @private
4612     */
4613    ;
4614
4615    _proto.clearTimersOnDispose_ = function clearTimersOnDispose_() {
4616      var _this4 = this;
4617
4618      if (this.clearingTimersOnDispose_) {
4619        return;
4620      }
4621
4622      this.clearingTimersOnDispose_ = true;
4623      this.one('dispose', function () {
4624        [['rafIds_', 'cancelAnimationFrame'], ['setTimeoutIds_', 'clearTimeout'], ['setIntervalIds_', 'clearInterval']].forEac
4624h(function (_ref) {
4625          var idName = _ref[0],
4626              cancelName = _ref[1];
4627
4628          _this4[idName].forEach(_this4[cancelName], _this4);
4629        });
4630        _this4.clearingTimersOnDispose_ = false;
4631      });
4632    }
4633    /**
4634     * Register a `Component` with `videojs` given the name and the component.
4635     *
4636     * > NOTE: {@link Tech}s should not be registered as a `Component`. {@link Tech}s
4637     *         should be registered using {@link Tech.registerTech} or
4638     *         {@link videojs:videojs.registerTech}.
4639     *
4640     * > NOTE: This function can also be seen on videojs as
4641     *         {@link videojs:videojs.registerComponent}.
4642     *
4643     * @param {string} name
4644     *        The name of the `Component` to register.
4645     *
4646     * @param {Component} ComponentToRegister
4647     *        The `Component` class to register.
4648     *
4649     * @return {Component}
4650     *         The `Component` that was registered.
4651     */
4652    ;
4653
4654    Component.registerComponent = function registerComponent(name, ComponentToRegister) {
4655      if (typeof name !== 'string' || !name) {
4656        throw new Error("Illegal component name, \"" + name + "\"; must be a non-empty string.");
4657      }
4658
4659      var Tech = Component.getComponent('Tech'); // We need to make sure this check is only done if Tech has been registered.
4660
4661      var isTech = Tech && Tech.isTech(ComponentToRegister);
4662      var isComp = Component === ComponentToRegister || Component.prototype.isPrototypeOf(ComponentToRegister.prototype);
4663
4664      if (isTech || !isComp) {
4665        var reason;
4666
4667        if (isTech) {
4668          reason = 'techs must be registered using Tech.registerTech()';
4669        } else {
4670          reason = 'must be a Component subclass';
4671        }
4672
4673        throw new Error("Illegal component, \"" + name + "\"; " + reason + ".");
4674      }
4675
4676      name = toTitleCase(name);
4677
4678      if (!Component.components_) {
4679        Component.components_ = {};
4680      }
4681
4682      var Player = Component.getComponent('Player');
4683
4684      if (name === 'Player' && Player && Player.players) {
4685        var players = Player.players;
4686        var playerNames = Object.keys(players); // If we have players that were disposed, then their name will still be
4687        // in Players.players. So, we must loop through and verify that the value
4688        // for each item is not null. This allows registration of the Player component
4689        // after all players have been disposed or before any were created.
4690
4691        if (players && playerNames.length > 0 && playerNames.map(function (pname) {
4692          return players[pname];
4693        }).every(Boolean)) {
4694          throw new Error('Can not register Player component after player has been created.');
4695        }
4696      }
4697
4698      Component.components_[name] = ComponentToRegister;
4699      Component.components_[toLowerCase(name)] = ComponentToRegister;
4700      return ComponentToRegister;
4701    }
4702    /**
4703     * Get a `Component` based on the name it was registered with.
4704     *
4705     * @param {string} name
4706     *        The Name of the component to get.
4707     *
4708     * @return {Component}
4709     *         The `Component` that got registered under the given name.
4710     *
4711     * @deprecated In `videojs` 6 this will not return `Component`s that were not
4712     *             registered using {@link Component.registerComponent}. Currently we
4713     *             check the global `videojs` object for a `Component` name and
4714     *             return that if it exists.
4715     */
4716    ;
4717
4718    Component.getComponent = function getComponent(name) {
4719      if (!name || !Component.components_) {
4720        return;
4721      }
4722
4723      return Component.components_[name];
4724    };
4725
4726    return Component;
4727  }();
4728  /**
4729   * Whether or not this component supports `requestAnimationFrame`.
4730   *
4731   * This is exposed primarily for testing purposes.
4732   *
4733   * @private
4734   * @type {Boolean}
4735   */
4736
4737
4738  Component.prototype.supportsRaf_ = typeof window$1.requestAnimationFrame === 'function' && typeof window$1.cancelAnimationFrame === 'function';
4739  Component.registerComponent('Component', Component);
4740
4741  function _inheritsLoose(subClass, superClass) {
4742    subClass.prototype = Object.create(superClass.prototype);
4743    subClass.prototype.constructor = subClass;
4744    subClass.__proto__ = superClass;
4745  }
4746
4747  function _setPrototypeOf(o, p) {
4748    _setPrototypeOf = Object.setPrototypeOf || function _setPrototypeOf(o, p) {
4749      o.__proto__ = p;
4750      return o;
4751    };
4752
4753    return _setPrototypeOf(o, p);
4754  }
4755
4756  function isNativeReflectConstruct() {
4757    if (typeof Reflect === "undefined" || !Reflect.construct) return false;
4758    if (Reflect.construct.sham) return false;
4759    if (typeof Proxy === "function") return true;
4760
4761    try {
4762      Date.prototype.toString.call(Reflect.construct(Date, [], function () {}));
4763      return true;
4764    } catch (e) {
4765      return false;
4766    }
4767  }
4768
4769  function _construct(Parent, args, Class) {
4770    if (isNativeReflectConstruct()) {
4771      _construct = Reflect.construct;
4772    } else {
4773      _construct = function _construct(Parent, args, Class) {
4774        var a = [null];
4775        a.push.apply(a, args);
4776        var Constructor = Function.bind.apply(Parent, a);
4777        var instance = new Constructor();
4778        if (Class) _setPrototypeOf(instance, Class.prototype);
4779        return instance;
4780      };
4781    }
4782
4783    return _construct.apply(null, arguments);
4784  }
4785
4786  function _assertThisInitialized(self) {
4787    if (self === void 0) {
4788      throw new ReferenceError("this hasn't been initialised - super() hasn't been called");
4789    }
4790
4791    return self;
4792  }
4793
4794  /**
4795   * @file browser.js
4796   * @module browser
4797   */
4798  var USER_AGENT = window$1.navigator && window$1.navigator.userAgent || '';
vendor: 13,550 bytes, lines 4799-5281
4799  var webkitVersionMap = /AppleWebKit\/([\d.]+)/i.exec(USER_AGENT);
4800  var appleWebkitVersion = webkitVersionMap ? parseFloat(webkitVersionMap.pop()) : null;
4801  /**
4802   * Whether or not this device is an iPad.
4803   *
4804   * @static
4805   * @const
4806   * @type {Boolean}
4807   */
4808
4809  var IS_IPAD = /iPad/i.test(USER_AGENT);
4810  /**
4811   * Whether or not this device is an iPhone.
4812   *
4813   * @static
4814   * @const
4815   * @type {Boolean}
4816   */
4817  // The Facebook app's UIWebView identifies as both an iPhone and iPad, so
4818  // to identify iPhones, we need to exclude iPads.
4819  // http://artsy.github.io/blog/2012/10/18/the-perils-of-ios-user-agent-sniffing/
4820
4821  var IS_IPHONE = /iPhone/i.test(USER_AGENT) && !IS_IPAD;
4822  /**
4823   * Whether or not this device is an iPod.
4824   *
4825   * @static
4826   * @const
4827   * @type {Boolean}
4828   */
4829
4830  var IS_IPOD = /iPod/i.test(USER_AGENT);
4831  /**
4832   * Whether or not this is an iOS device.
4833   *
4834   * @static
4835   * @const
4836   * @type {Boolean}
4837   */
4838
4839  var IS_IOS = IS_IPHONE || IS_IPAD || IS_IPOD;
4840  /**
4841   * The detected iOS version - or `null`.
4842   *
4843   * @static
4844   * @const
4845   * @type {string|null}
4846   */
4847
4848  var IOS_VERSION = function () {
4849    var match = USER_AGENT.match(/OS (\d+)_/i);
4850
4851    if (match && match[1]) {
4852      return match[1];
4853    }
4854
4855    return null;
4856  }();
4857  /**
4858   * Whether or not this is an Android device.
4859   *
4860   * @static
4861   * @const
4862   * @type {Boolean}
4863   */
4864
4865  var IS_ANDROID = /Android/i.test(USER_AGENT);
4866  /**
4867   * The detected Android version - or `null`.
4868   *
4869   * @static
4870   * @const
4871   * @type {number|string|null}
4872   */
4873
4874  var ANDROID_VERSION = function () {
4875    // This matches Android Major.Minor.Patch versions
4876    // ANDROID_VERSION is Major.Minor as a Number, if Minor isn't available, then only Major is returned
4877    var match = USER_AGENT.match(/Android (\d+)(?:\.(\d+))?(?:\.(\d+))*/i);
4878
4879    if (!match) {
4880      return null;
4881    }
4882
4883    var major = match[1] && parseFloat(match[1]);
4884    var minor = match[2] && parseFloat(match[2]);
4885
4886    if (major && minor) {
4887      return parseFloat(match[1] + '.' + match[2]);
4888    } else if (major) {
4889      return major;
4890    }
4891
4892    return null;
4893  }();
4894  /**
4895   * Whether or not this is a native Android browser.
4896   *
4897   * @static
4898   * @const
4899   * @type {Boolean}
4900   */
4901
4902  var IS_NATIVE_ANDROID = IS_ANDROID && ANDROID_VERSION < 5 && appleWebkitVersion < 537;
4903  /**
4904   * Whether or not this is Mozilla Firefox.
4905   *
4906   * @static
4907   * @const
4908   * @type {Boolean}
4909   */
4910
4911  var IS_FIREFOX = /Firefox/i.test(USER_AGENT);
4912  /**
4913   * Whether or not this is Microsoft Edge.
4914   *
4915   * @static
4916   * @const
4917   * @type {Boolean}
4918   */
4919
4920  var IS_EDGE = /Edge/i.test(USER_AGENT);
4921  /**
4922   * Whether or not this is Google Chrome.
4923   *
4924   * This will also be `true` for Chrome on iOS, which will have different support
4925   * as it is actually Safari under the hood.
4926   *
4927   * @static
4928   * @const
4929   * @type {Boolean}
4930   */
4931
4932  var IS_CHROME = !IS_EDGE && (/Chrome/i.test(USER_AGENT) || /CriOS/i.test(USER_AGENT));
4933  /**
4934   * The detected Google Chrome version - or `null`.
4935   *
4936   * @static
4937   * @const
4938   * @type {number|null}
4939   */
4940
4941  var CHROME_VERSION = function () {
4942    var match = USER_AGENT.match(/(Chrome|CriOS)\/(\d+)/);
4943
4944    if (match && match[2]) {
4945      return parseFloat(match[2]);
4946    }
4947
4948    return null;
4949  }();
4950  /**
4951   * The detected Internet Explorer version - or `null`.
4952   *
4953   * @static
4954   * @const
4955   * @type {number|null}
4956   */
4957
4958  var IE_VERSION = function () {
4959    var result = /MSIE\s(\d+)\.\d/.exec(USER_AGENT);
4960    var version = result && parseFloat(result[1]);
4961
4962    if (!version && /Trident\/7.0/i.test(USER_AGENT) && /rv:11.0/.test(USER_AGENT)) {
4963      // IE 11 has a different user agent string than other IE versions
4964      version = 11.0;
4965    }
4966
4967    return version;
4968  }();
4969  /**
4970   * Whether or not this is desktop Safari.
4971   *
4972   * @static
4973   * @const
4974   * @type {Boolean}
4975   */
4976
4977  var IS_SAFARI = /Safari/i.test(USER_AGENT) && !IS_CHROME && !IS_ANDROID && !IS_EDGE;
4978  /**
4979   * Whether or not this is any flavor of Safari - including iOS.
4980   *
4981   * @static
4982   * @const
4983   * @type {Boolean}
4984   */
4985
4986  var IS_ANY_SAFARI = (IS_SAFARI || IS_IOS) && !IS_CHROME;
4987  /**
4988   * Whether or not this is a Windows machine.
4989   *
4990   * @static
4991   * @const
4992   * @type {Boolean}
4993   */
4994
4995  var IS_WINDOWS = /Windows/i.test(USER_AGENT);
4996  /**
4997   * Whether or not this device is touch-enabled.
4998   *
4999   * @static
5000   * @const
5001   * @type {Boolean}
5002   */
5003
5004  var TOUCH_ENABLED = isReal() && ('ontouchstart' in window$1 || window$1.navigator.maxTouchPoints || window$1.DocumentTouch && window$1.document instanceof window$1.DocumentTouch);
5005
5006  var browser = /*#__PURE__*/Object.freeze({
5007    IS_IPAD: IS_IPAD,
5008    IS_IPHONE: IS_IPHONE,
5009    IS_IPOD: IS_IPOD,
5010    IS_IOS: IS_IOS,
5011    IOS_VERSION: IOS_VERSION,
5012    IS_ANDROID: IS_ANDROID,
5013    ANDROID_VERSION: ANDROID_VERSION,
5014    IS_NATIVE_ANDROID: IS_NATIVE_ANDROID,
5015    IS_FIREFOX: IS_FIREFOX,
5016    IS_EDGE: IS_EDGE,
5017    IS_CHROME: IS_CHROME,
5018    CHROME_VERSION: CHROME_VERSION,
5019    IE_VERSION: IE_VERSION,
5020    IS_SAFARI: IS_SAFARI,
5021    IS_ANY_SAFARI: IS_ANY_SAFARI,
5022    IS_WINDOWS: IS_WINDOWS,
5023    TOUCH_ENABLED: TOUCH_ENABLED
5024  });
5025
5026  /**
5027   * @file time-ranges.js
5028   * @module time-ranges
5029   */
5030
5031  /**
5032   * Returns the time for the specified index at the start or end
5033   * of a TimeRange object.
5034   *
5035   * @typedef    {Function} TimeRangeIndex
5036   *
5037   * @param      {number} [index=0]
5038   *             The range number to return the time for.
5039   *
5040   * @return     {number}
5041   *             The time offset at the specified index.
5042   *
5043   * @deprecated The index argument must be provided.
5044   *             In the future, leaving it out will throw an error.
5045   */
5046
5047  /**
5048   * An object that contains ranges of time.
5049   *
5050   * @typedef  {Object} TimeRange
5051   *
5052   * @property {number} length
5053   *           The number of time ranges represented by this object.
5054   *
5055   * @property {module:time-ranges~TimeRangeIndex} start
5056   *           Returns the time offset at which a specified time range begins.
5057   *
5058   * @property {module:time-ranges~TimeRangeIndex} end
5059   *           Returns the time offset at which a specified time range ends.
5060   *
5061   * @see https://developer.mozilla.org/en-US/docs/Web/API/TimeRanges
5062   */
5063
5064  /**
5065   * Check if any of the time ranges are over the maximum index.
5066   *
5067   * @private
5068   * @param   {string} fnName
5069   *          The function name to use for logging
5070   *
5071   * @param   {number} index
5072   *          The index to check
5073   *
5074   * @param   {number} maxIndex
5075   *          The maximum possible index
5076   *
5077   * @throws  {Error} if the timeRanges provided are over the maxIndex
5078   */
5079  function rangeCheck(fnName, index, maxIndex) {
5080    if (typeof index !== 'number' || index < 0 || index > maxIndex) {
5081      throw new Error("Failed to execute '" + fnName + "' on 'TimeRanges': The index provided (" + index + ") is non-numeric or out of bounds (0-" + maxIndex + ").");
5082    }
5083  }
5084  /**
5085   * Get the time for the specified index at the start or end
5086   * of a TimeRange object.
5087   *
5088   * @private
5089   * @param      {string} fnName
5090   *             The function name to use for logging
5091   *
5092   * @param      {string} valueIndex
5093   *             The property that should be used to get the time. should be
5094   *             'start' or 'end'
5095   *
5096   * @param      {Array} ranges
5097   *             An array of time ranges
5098   *
5099   * @param      {Array} [rangeIndex=0]
5100   *             The index to start the search at
5101   *
5102   * @return     {number}
5103   *             The time that offset at the specified index.
5104   *
5105   * @deprecated rangeIndex must be set to a value, in the future this will throw an error.
5106   * @throws     {Error} if rangeIndex is more than the length of ranges
5107   */
5108
5109
5110  function getRange(fnName, valueIndex, ranges, rangeIndex) {
5111    rangeCheck(fnName, rangeIndex, ranges.length - 1);
5112    return ranges[rangeIndex][valueIndex];
5113  }
5114  /**
5115   * Create a time range object given ranges of time.
5116   *
5117   * @private
5118   * @param   {Array} [ranges]
5119   *          An array of time ranges.
5120   */
5121
5122
5123  function createTimeRangesObj(ranges) {
5124    if (ranges === undefined || ranges.length === 0) {
5125      return {
5126        length: 0,
5127        start: function start() {
5128          throw new Error('This TimeRanges object is empty');
5129        },
5130        end: function end() {
5131          throw new Error('This TimeRanges object is empty');
5132        }
5133      };
5134    }
5135
5136    return {
5137      length: ranges.length,
5138      start: getRange.bind(null, 'start', 0, ranges),
5139      end: getRange.bind(null, 'end', 1, ranges)
5140    };
5141  }
5142  /**
5143   * Create a `TimeRange` object which mimics an
5144   * {@link https://developer.mozilla.org/en-US/docs/Web/API/TimeRanges|HTML5 TimeRanges instance}.
5145   *
5146   * @param {number|Array[]} start
5147   *        The start of a single range (a number) or an array of ranges (an
5148   *        array of arrays of two numbers each).
5149   *
5150   * @param {number} end
5151   *        The end of a single range. Cannot be used with the array form of
5152   *        the `start` argument.
5153   */
5154
5155
5156  function createTimeRanges(start, end) {
5157    if (Array.isArray(start)) {
5158      return createTimeRangesObj(start);
5159    } else if (start === undefined || end === undefined) {
5160      return createTimeRangesObj();
5161    }
5162
5163    return createTimeRangesObj([[start, end]]);
5164  }
5165
5166  /**
5167   * @file buffer.js
5168   * @module buffer
5169   */
5170  /**
5171   * Compute the percentage of the media that has been buffered.
5172   *
5173   * @param {TimeRange} buffered
5174   *        The current `TimeRange` object representing buffered time ranges
5175   *
5176   * @param {number} duration
5177   *        Total duration of the media
5178   *
5179   * @return {number}
5180   *         Percent buffered of the total duration in decimal form.
5181   */
5182
5183  function bufferedPercent(buffered, duration) {
5184    var bufferedDuration = 0;
5185    var start;
5186    var end;
5187
5188    if (!duration) {
5189      return 0;
5190    }
5191
5192    if (!buffered || !buffered.length) {
5193      buffered = createTimeRanges(0, 0);
5194    }
5195
5196    for (var i = 0; i < buffered.length; i++) {
5197      start = buffered.start(i);
5198      end = buffered.end(i); // buffered end can be bigger than duration by a very small fraction
5199
5200      if (end > duration) {
5201        end = duration;
5202      }
5203
5204      bufferedDuration += end - start;
5205    }
5206
5207    return bufferedDuration / duration;
5208  }
5209
5210  /**
5211   * @file fullscreen-api.js
5212   * @module fullscreen-api
5213   * @private
5214   */
5215  /**
5216   * Store the browser-specific methods for the fullscreen API.
5217   *
5218   * @type {Object}
5219   * @see [Specification]{@link https://fullscreen.spec.whatwg.org}
5220   * @see [Map Approach From Screenfull.js]{@link https://github.com/sindresorhus/screenfull.js}
5221   */
5222
5223  var FullscreenApi = {
5224    prefixed: true
5225  }; // browser API methods
5226
5227  var apiMap = [['requestFullscreen', 'exitFullscreen', 'fullscreenElement', 'fullscreenEnabled', 'fullscreenchange', 'fullscreenerror', 'fullscreen'], // WebKit
5228  ['webkitRequestFullscreen', 'webkitExitFullscreen', 'webkitFullscreenElement', 'webkitFullscreenEnabled', 'webkitfullscreenchange', 'webkitfullscreenerror', '-webkit-full-screen'], // Mozilla
5229  ['mozRequestFullScreen', 'mozCancelFullScreen', 'mozFullScreenElement', 'mozFullScreenEnabled', 'mozfullscreenchange', 'mozfullscreenerror', '-moz-full-screen'], // Microsoft
5230  ['msRequestFullscreen', 'msExitFullscreen', 'msFullscreenElement', 'msFullscreenEnabled', 'MSFullscreenChange', 'MSFullscreenError', '-ms-fullscreen']];
5231  var specApi = apiMap[0];
5232  var browserApi; // determine the supported set of functions
5233
5234  for (var i = 0; i < apiMap.length; i++) {
5235    // check for exitFullscreen function
5236    if (apiMap[i][1] in document) {
5237      browserApi = apiMap[i];
5238      break;
5239    }
5240  } // map the browser API names to the spec API names
5241
5242
5243  if (browserApi) {
5244    for (var _i = 0; _i < browserApi.length; _i++) {
5245      FullscreenApi[specApi[_i]] = browserApi[_i];
5246    }
5247
5248    FullscreenApi.prefixed = browserApi[0] !== specApi[0];
5249  }
5250
5251  /**
5252   * @file media-error.js
5253   */
5254  /**
5255   * A Custom `MediaError` class which mimics the standard HTML5 `MediaError` class.
5256   *
5257   * @param {number|string|Object|MediaError} value
5258   *        This can be of multiple types:
5259   *        - number: should be a standard error code
5260   *        - string: an error message (the code will be 0)
5261   *        - Object: arbitrary properties
5262   *        - `MediaError` (native): used to populate a video.js `MediaError` object
5263   *        - `MediaError` (video.js): will return itself if it's already a
5264   *          video.js `MediaError` object.
5265   *
5266   * @see [MediaError Spec]{@link https://dev.w3.org/html5/spec-author-view/video.html#mediaerror}
5267   * @see [Encrypted MediaError Spec]{@link https://www.w3.org/TR/2013/WD-encrypted-media-20130510/#error-codes}
5268   *
5269   * @class MediaError
5270   */
5271
5272  function MediaError(value) {
5273    // Allow redundant calls to this constructor to avoid having `instanceof`
5274    // checks peppered around the code.
5275    if (value instanceof MediaError) {
5276      return value;
5277    }
5278
5279    if (typeof value === 'number') {
5280      this.code = value;
5281    } else if (typeof value === 'string') {
vendor: 11,383 bytes, lines 5282-5663
5282      // default code is zero, so this is a custom error
5283      this.message = value;
5284    } else if (isObject(value)) {
5285      // We assign the `code` property manually because native `MediaError` objects
5286      // do not expose it as an own/enumerable property of the object.
5287      if (typeof value.code === 'number') {
5288        this.code = value.code;
5289      }
5290
5291      assign(this, value);
5292    }
5293
5294    if (!this.message) {
5295      this.message = MediaError.defaultMessages[this.code] || '';
5296    }
5297  }
5298  /**
5299   * The error code that refers two one of the defined `MediaError` types
5300   *
5301   * @type {Number}
5302   */
5303
5304
5305  MediaError.prototype.code = 0;
5306  /**
5307   * An optional message that to show with the error. Message is not part of the HTML5
5308   * video spec but allows for more informative custom errors.
5309   *
5310   * @type {String}
5311   */
5312
5313  MediaError.prototype.message = '';
5314  /**
5315   * An optional status code that can be set by plugins to allow even more detail about
5316   * the error. For example a plugin might provide a specific HTTP status code and an
5317   * error message for that code. Then when the plugin gets that error this class will
5318   * know how to display an error message for it. This allows a custom message to show
5319   * up on the `Player` error overlay.
5320   *
5321   * @type {Array}
5322   */
5323
5324  MediaError.prototype.status = null;
5325  /**
5326   * Errors indexed by the W3C standard. The order **CANNOT CHANGE**! See the
5327   * specification listed under {@link MediaError} for more information.
5328   *
5329   * @enum {array}
5330   * @readonly
5331   * @property {string} 0 - MEDIA_ERR_CUSTOM
5332   * @property {string} 1 - MEDIA_ERR_ABORTED
5333   * @property {string} 2 - MEDIA_ERR_NETWORK
5334   * @property {string} 3 - MEDIA_ERR_DECODE
5335   * @property {string} 4 - MEDIA_ERR_SRC_NOT_SUPPORTED
5336   * @property {string} 5 - MEDIA_ERR_ENCRYPTED
5337   */
5338
5339  MediaError.errorTypes = ['MEDIA_ERR_CUSTOM', 'MEDIA_ERR_ABORTED', 'MEDIA_ERR_NETWORK', 'MEDIA_ERR_DECODE', 'MEDIA_ERR_SRC_NOT_SUPPORTED', 'MEDIA_ERR_ENCRYPTED'];
5340  /**
5341   * The default `MediaError` messages based on the {@link MediaError.errorTypes}.
5342   *
5343   * @type {Array}
5344   * @constant
5345   */
5346
5347  MediaError.defaultMessages = {
5348    1: 'You aborted the media playback',
5349    2: 'A network error caused the media download to fail part-way.',
5350    3: 'The media playback was aborted due to a corruption problem or because the media used features your browser did not support.',
5351    4: 'The media could not be loaded, either because the server or network failed or because the format is not supported.',
5352    5: 'The media is encrypted and we do not have the keys to decrypt it.'
5353  }; // Add types as properties on MediaError
5354  // e.g. MediaError.MEDIA_ERR_SRC_NOT_SUPPORTED = 4;
5355
5356  for (var errNum = 0; errNum < MediaError.errorTypes.length; errNum++) {
5357    MediaError[MediaError.errorTypes[errNum]] = errNum; // values should be accessible on both the class and instance
5358
5359    MediaError.prototype[MediaError.errorTypes[errNum]] = errNum;
5360  } // jsdocs for instance/static members added above
5361
5362  var tuple = SafeParseTuple;
5363
5364  function SafeParseTuple(obj, reviver) {
5365    var json;
5366    var error = null;
5367
5368    try {
5369      json = JSON.parse(obj, reviver);
5370    } catch (err) {
5371      error = err;
5372    }
5373
5374    return [error, json];
5375  }
5376
5377  /**
5378   * Returns whether an object is `Promise`-like (i.e. has a `then` method).
5379   *
5380   * @param  {Object}  value
5381   *         An object that may or may not be `Promise`-like.
5382   *
5383   * @return {boolean}
5384   *         Whether or not the object is `Promise`-like.
5385   */
5386  function isPromise(value) {
5387    return value !== undefined && value !== null && typeof value.then === 'function';
5388  }
5389  /**
5390   * Silence a Promise-like object.
5391   *
5392   * This is useful for avoiding non-harmful, but potentially confusing "uncaught
5393   * play promise" rejection error messages.
5394   *
5395   * @param  {Object} value
5396   *         An object that may or may not be `Promise`-like.
5397   */
5398
5399  function silencePromise(value) {
5400    if (isPromise(value)) {
5401      value.then(null, function (e) {});
5402    }
5403  }
5404
5405  /**
5406   * @file text-track-list-converter.js Utilities for capturing text track state and
5407   * re-creating tracks based on a capture.
5408   *
5409   * @module text-track-list-converter
5410   */
5411
5412  /**
5413   * Examine a single {@link TextTrack} and return a JSON-compatible javascript object that
5414   * represents the {@link TextTrack}'s state.
5415   *
5416   * @param {TextTrack} track
5417   *        The text track to query.
5418   *
5419   * @return {Object}
5420   *         A serializable javascript representation of the TextTrack.
5421   * @private
5422   */
5423  var trackToJson_ = function trackToJson_(track) {
5424    var ret = ['kind', 'label', 'language', 'id', 'inBandMetadataTrackDispatchType', 'mode', 'src'].reduce(function (acc, prop, i) {
5425      if (track[prop]) {
5426        acc[prop] = track[prop];
5427      }
5428
5429      return acc;
5430    }, {
5431      cues: track.cues && Array.prototype.map.call(track.cues, function (cue) {
5432        return {
5433          startTime: cue.startTime,
5434          endTime: cue.endTime,
5435          text: cue.text,
5436          id: cue.id
5437        };
5438      })
5439    });
5440    return ret;
5441  };
5442  /**
5443   * Examine a {@link Tech} and return a JSON-compatible javascript array that represents the
5444   * state of all {@link TextTrack}s currently configured. The return array is compatible with
5445   * {@link text-track-list-converter:jsonToTextTracks}.
5446   *
5447   * @param {Tech} tech
5448   *        The tech object to query
5449   *
5450   * @return {Array}
5451   *         A serializable javascript representation of the {@link Tech}s
5452   *         {@link TextTrackList}.
5453   */
5454
5455
5456  var textTracksToJson = function textTracksToJson(tech) {
5457    var trackEls = tech.$$('track');
5458    var trackObjs = Array.prototype.map.call(trackEls, function (t) {
5459      return t.track;
5460    });
5461    var tracks = Array.prototype.map.call(trackEls, function (trackEl) {
5462      var json = trackToJson_(trackEl.track);
5463
5464      if (trackEl.src) {
5465        json.src = trackEl.src;
5466      }
5467
5468      return json;
5469    });
5470    return tracks.concat(Array.prototype.filter.call(tech.textTracks(), function (track) {
5471      return trackObjs.indexOf(track) === -1;
5472    }).map(trackToJson_));
5473  };
5474  /**
5475   * Create a set of remote {@link TextTrack}s on a {@link Tech} based on an array of javascript
5476   * object {@link TextTrack} representations.
5477   *
5478   * @param {Array} json
5479   *        An array of `TextTrack` representation objects, like those that would be
5480   *        produced by `textTracksToJson`.
5481   *
5482   * @param {Tech} tech
5483   *        The `Tech` to create the `TextTrack`s on.
5484   */
5485
5486
5487  var jsonToTextTracks = function jsonToTextTracks(json, tech) {
5488    json.forEach(function (track) {
5489      var addedTrack = tech.addRemoteTextTrack(track).track;
5490
5491      if (!track.src && track.cues) {
5492        track.cues.forEach(function (cue) {
5493          return addedTrack.addCue(cue);
5494        });
5495      }
5496    });
5497    return tech.textTracks();
5498  };
5499
5500  var textTrackConverter = {
5501    textTracksToJson: textTracksToJson,
5502    jsonToTextTracks: jsonToTextTracks,
5503    trackToJson_: trackToJson_
5504  };
5505
5506  function createCommonjsModule(fn, module) {
5507  	return module = { exports: {} }, fn(module, module.exports), module.exports;
5508  }
5509
5510  var keycode = createCommonjsModule(function (module, exports) {
5511    // Source: http://jsfiddle.net/vWx8V/
5512    // http://stackoverflow.com/questions/5603195/full-list-of-javascript-keycodes
5513
5514    /**
5515     * Conenience method returns corresponding value for given keyName or keyCode.
5516     *
5517     * @param {Mixed} keyCode {Number} or keyName {String}
5518     * @return {Mixed}
5519     * @api public
5520     */
5521    function keyCode(searchInput) {
5522      // Keyboard Events
5523      if (searchInput && 'object' === typeof searchInput) {
5524        var hasKeyCode = searchInput.which || searchInput.keyCode || searchInput.charCode;
5525        if (hasKeyCode) searchInput = hasKeyCode;
5526      } // Numbers
5527
5528
5529      if ('number' === typeof searchInput) return names[searchInput]; // Everything else (cast to string)
5530
5531      var search = String(searchInput); // check codes
5532
5533      var foundNamedKey = codes[search.toLowerCase()];
5534      if (foundNamedKey) return foundNamedKey; // check aliases
5535
5536      var foundNamedKey = aliases[search.toLowerCase()];
5537      if (foundNamedKey) return foundNamedKey; // weird character?
5538
5539      if (search.length === 1) return search.charCodeAt(0);
5540      return undefined;
5541    }
5542    /**
5543     * Compares a keyboard event with a given keyCode or keyName.
5544     *
5545     * @param {Event} event Keyboard event that should be tested
5546     * @param {Mixed} keyCode {Number} or keyName {String}
5547     * @return {Boolean}
5548     * @api public
5549     */
5550
5551
5552    keyCode.isEventKey = function isEventKey(event, nameOrCode) {
5553      if (event && 'object' === typeof event) {
5554        var keyCode = event.which || event.keyCode || event.charCode;
5555
5556        if (keyCode === null || keyCode === undefined) {
5557          return false;
5558        }
5559
5560        if (typeof nameOrCode === 'string') {
5561          // check codes
5562          var foundNamedKey = codes[nameOrCode.toLowerCase()];
5563
5564          if (foundNamedKey) {
5565            return foundNamedKey === keyCode;
5566          } // check aliases
5567
5568
5569          var foundNamedKey = aliases[nameOrCode.toLowerCase()];
5570
5571          if (foundNamedKey) {
5572            return foundNamedKey === keyCode;
5573          }
5574        } else if (typeof nameOrCode === 'number') {
5575          return nameOrCode === keyCode;
5576        }
5577
5578        return false;
5579      }
5580    };
5581
5582    exports = module.exports = keyCode;
5583    /**
5584     * Get by name
5585     *
5586     *   exports.code['enter'] // => 13
5587     */
5588
5589    var codes = exports.code = exports.codes = {
5590      'backspace': 8,
5591      'tab': 9,
5592      'enter': 13,
5593      'shift': 16,
5594      'ctrl': 17,
5595      'alt': 18,
5596      'pause/break': 19,
5597      'caps lock': 20,
5598      'esc': 27,
5599      'space': 32,
5600      'page up': 33,
5601      'page down': 34,
5602      'end': 35,
5603      'home': 36,
5604      'left': 37,
5605      'up': 38,
5606      'right': 39,
5607      'down': 40,
5608      'insert': 45,
5609      'delete': 46,
5610      'command': 91,
5611      'left command': 91,
5612      'right command': 93,
5613      'numpad *': 106,
5614      'numpad +': 107,
5615      'numpad -': 109,
5616      'numpad .': 110,
5617      'numpad /': 111,
5618      'num lock': 144,
5619      'scroll lock': 145,
5620      'my computer': 182,
5621      'my calculator': 183,
5622      ';': 186,
5623      '=': 187,
5624      ',': 188,
5625      '-': 189,
5626      '.': 190,
5627      '/': 191,
5628      '`': 192,
5629      '[': 219,
5630      '\\': 220,
5631      ']': 221,
5632      "'": 222 // Helper aliases
5633
5634    };
5635    var aliases = exports.aliases = {
5636      'windows': 91,
5637      '⇧': 16,
5638      '⌥': 18,
5639      '⌃': 17,
5640      '⌘': 91,
5641      'ctl': 17,
5642      'control': 17,
5643      'option': 18,
5644      'pause': 19,
5645      'break': 19,
5646      'caps': 20,
5647      'return': 13,
5648      'escape': 27,
5649      'spc': 32,
5650      'spacebar': 32,
5651      'pgup': 33,
5652      'pgdn': 34,
5653      'ins': 45,
5654      'del': 46,
5655      'cmd': 91
5656      /*!
5657       * Programatically add the following
5658       */
5659      // lower case chars
5660
5661    };
5662
5663    for (i = 97; i < 123; i++) {
5664      codes[String.fromCharCode(i)] = i - 32;
5665    } // numbers
5666
5667
5668    for (var i = 48; i < 58; i++) {
5669      codes[i - 48] = i;
5670    } // function keys
5671
5672
5673    for (i = 1; i < 13; i++) {
5674      codes['f' + i] = i + 111;
5675    } // numpad keys
5676
5677
5678    for (i = 0; i < 10; i++) {
5679      codes['numpad ' + i] = i + 96;
5680    }
5681    /**
5682     * Get by code
5683     *
5684     *   exports.name[13] // => 'Enter'
5685     */
5686
5687
5688    var names = exports.names = exports.title = {}; // title for backward compat
5689    // Create reverse mapping
5690
5691    for (i in codes) {
5692      names[codes[i]] = i;
5693    } // Add aliases
5694
5695
5696    for (var alias in aliases) {
5697      codes[alias] = aliases[alias];
5698    }
5699  });
5700  var keycode_1 = keycode.code;
5701  var keycode_2 = keycode.codes;
5702  var keycode_3 = keycode.aliases;
5703  var keycode_4 = keycode.names;
5704  var keycode_5 = keycode.title;
5705
5706  var MODAL_CLASS_NAME = 'vjs-modal-dialog';
5707  /**
5708   * The `ModalDialog` displays over the video and its controls, which blocks
5709   * interaction with the player until it is closed.
5710   *
5711   * Modal dialogs include a "Close" button and will close when that button
5712   * is activated - or when ESC is pressed anywhere.
5713   *
5714   * @extends Component
5715   */
5716
5717  var ModalDialog =
5718  /*#__PURE__*/
5719  function (_Component) {
5720    _inheritsLoose(ModalDialog, _Component);
5721
5722    /**
5723     * Create an instance of this class.
5724     *
5725     * @param {Player} player
5726     *        The `Player` that this class should be attached to.
5727     *
5728     * @param {Object} [options]
5729     *        The key/value store of player options.
5730     *
5731     * @param {Mixed} [options.content=undefined]
5732     *        Provide customized content for this modal.
5733     *
5734     * @param {string} [options.description]
5735     *        A text description for the modal, primarily for accessibility.
5736     *
5737     * @param {boolean} [options.fillAlways=false]
5738     *        Normally, modals are automatically filled only the first time
5739     *        they open. This tells the modal to refresh its content
5740     *        every time it opens.
5741     *
5742     * @param {string} [options.label]
5743     *        A text label for the modal, primarily for accessibility.
5744     *
5745     * @param {boolean} [options.pauseOnOpen=true]
5746     *        If `true`, playback will will be paused if playing when
5747     *        the modal opens, and resumed when it closes.
5748     *
5749     * @param {boolean} [options.temporary=true]
5750     *        If `true`, the modal can only be opened once; it will be
5751     *        disposed as soon as it's closed.
5752     *
5753     * @param {boolean} [options.uncloseable=false]
5754     *        If `true`, the user will not be able to close the modal
5755     *        through the UI in the normal ways. Programmatic closing is
5756     *        still possible.
5757     */
5758    function ModalDialog(player, options) {
5759      var _this;
5760
5761      _this = _Component.call(this, player, options) || this;
5762      _this.opened_ = _this.hasBeenOpened_ = _this.hasBeenFilled_ = false;
5763
5764      _this.closeable(!_this.options_.uncloseable);
5765
5766      _this.content(_this.options_.content); // Make sure the contentEl is defined AFTER any children are initialized
5767      // because we only want the contents of the modal in the contentEl
5768      // (not the UI elements like the close button).
5769
5770
5771      _this.contentEl_ = createEl('div', {
5772        className: MODAL_CLASS_NAME + "-content"
5773      }, {
5774        role: 'document'
5775      });
5776      _this.descEl_ = createEl('p', {
5777        className: MODAL_CLASS_NAME + "-description vjs-control-text",
5778        id: _this.el().getAttribute('aria-describedby')
5779      });
5780      textContent(_this.descEl_, _this.description());
5781
5782      _this.el_.appendChild(_this.descEl_);
5783
5784      _this.el_.appendChild(_this.contentEl_);
5785
5786      return _this;
5787    }
5788    /**
5789     * Create the `ModalDialog`'s DOM element
5790     *
5791     * @return {Element}
5792     *         The DOM element that gets created.
5793     */
5794
5795
5796    var _proto = ModalDialog.prototype;
5797
5798    _proto.createEl = function createEl() {
5799      return _Component.prototype.createEl.call(this, 'div', {
5800        className: this.buildCSSClass(),
5801        tabIndex: -1
5802      }, {
5803        'aria-describedby': this.id() + "_description",
5804        'aria-hidden': 'true',
5805        'aria-label': this.label(),
5806        'role': 'dialog'
5807      });
5808    };
5809
5810    _proto.dispose = function dispose() {
5811      this.contentEl_ = null;
5812      this.descEl_ = null;
5813      this.previouslyActiveEl_ = null;
5814
5815      _Component.prototype.dispose.call(this);
5816    }
5817    /**
5818     * Builds the default DOM `className`.
5819     *
5820     * @return {string}
5821     *         The DOM `className` for this object.
5822     */
5823    ;
5824
5825    _proto.buildCSSClass = function buildCSSClass() {
5826      return MODAL_CLASS_NAME + " vjs-hidden " + _Component.prototype.buildCSSClass.call(this);
5827    }
5828    /**
vendor: 37,031 bytes, lines 5829-7207
5829     * Returns the label string for this modal. Primarily used for accessibility.
5830     *
5831     * @return {string}
5832     *         the localized or raw label of this modal.
5833     */
5834    ;
5835
5836    _proto.label = function label() {
5837      return this.localize(this.options_.label || 'Modal Window');
5838    }
5839    /**
5840     * Returns the description string for this modal. Primarily used for
5841     * accessibility.
5842     *
5843     * @return {string}
5844     *         The localized or raw description of this modal.
5845     */
5846    ;
5847
5848    _proto.description = function description() {
5849      var desc = this.options_.description || this.localize('This is a modal window.'); // Append a universal closeability message if the modal is closeable.
5850
5851      if (this.closeable()) {
5852        desc += ' ' + this.localize('This modal can be closed by pressing the Escape key or activating the close button.');
5853      }
5854
5855      return desc;
5856    }
5857    /**
5858     * Opens the modal.
5859     *
5860     * @fires ModalDialog#beforemodalopen
5861     * @fires ModalDialog#modalopen
5862     */
5863    ;
5864
5865    _proto.open = function open() {
5866      if (!this.opened_) {
5867        var player = this.player();
5868        /**
5869          * Fired just before a `ModalDialog` is opened.
5870          *
5871          * @event ModalDialog#beforemodalopen
5872          * @type {EventTarget~Event}
5873          */
5874
5875        this.trigger('beforemodalopen');
5876        this.opened_ = true; // Fill content if the modal has never opened before and
5877        // never been filled.
5878
5879        if (this.options_.fillAlways || !this.hasBeenOpened_ && !this.hasBeenFilled_) {
5880          this.fill();
5881        } // If the player was playing, pause it and take note of its previously
5882        // playing state.
5883
5884
5885        this.wasPlaying_ = !player.paused();
5886
5887        if (this.options_.pauseOnOpen && this.wasPlaying_) {
5888          player.pause();
5889        }
5890
5891        this.on('keydown', this.handleKeyDown); // Hide controls and note if they were enabled.
5892
5893        this.hadControls_ = player.controls();
5894        player.controls(false);
5895        this.show();
5896        this.conditionalFocus_();
5897        this.el().setAttribute('aria-hidden', 'false');
5898        /**
5899          * Fired just after a `ModalDialog` is opened.
5900          *
5901          * @event ModalDialog#modalopen
5902          * @type {EventTarget~Event}
5903          */
5904
5905        this.trigger('modalopen');
5906        this.hasBeenOpened_ = true;
5907      }
5908    }
5909    /**
5910     * If the `ModalDialog` is currently open or closed.
5911     *
5912     * @param  {boolean} [value]
5913     *         If given, it will open (`true`) or close (`false`) the modal.
5914     *
5915     * @return {boolean}
5916     *         the current open state of the modaldialog
5917     */
5918    ;
5919
5920    _proto.opened = function opened(value) {
5921      if (typeof value === 'boolean') {
5922        this[value ? 'open' : 'close']();
5923      }
5924
5925      return this.opened_;
5926    }
5927    /**
5928     * Closes the modal, does nothing if the `ModalDialog` is
5929     * not open.
5930     *
5931     * @fires ModalDialog#beforemodalclose
5932     * @fires ModalDialog#modalclose
5933     */
5934    ;
5935
5936    _proto.close = function close() {
5937      if (!this.opened_) {
5938        return;
5939      }
5940
5941      var player = this.player();
5942      /**
5943        * Fired just before a `ModalDialog` is closed.
5944        *
5945        * @event ModalDialog#beforemodalclose
5946        * @type {EventTarget~Event}
5947        */
5948
5949      this.trigger('beforemodalclose');
5950      this.opened_ = false;
5951
5952      if (this.wasPlaying_ && this.options_.pauseOnOpen) {
5953        player.play();
5954      }
5955
5956      this.off('keydown', this.handleKeyDown);
5957
5958      if (this.hadControls_) {
5959        player.controls(true);
5960      }
5961
5962      this.hide();
5963      this.el().setAttribute('aria-hidden', 'true');
5964      /**
5965        * Fired just after a `ModalDialog` is closed.
5966        *
5967        * @event ModalDialog#modalclose
5968        * @type {EventTarget~Event}
5969        */
5970
5971      this.trigger('modalclose');
5972      this.conditionalBlur_();
5973
5974      if (this.options_.temporary) {
5975        this.dispose();
5976      }
5977    }
5978    /**
5979     * Check to see if the `ModalDialog` is closeable via the UI.
5980     *
5981     * @param  {boolean} [value]
5982     *         If given as a boolean, it will set the `closeable` option.
5983     *
5984     * @return {boolean}
5985     *         Returns the final value of the closable option.
5986     */
5987    ;
5988
5989    _proto.closeable = function closeable(value) {
5990      if (typeof value === 'boolean') {
5991        var closeable = this.closeable_ = !!value;
5992        var close = this.getChild('closeButton'); // If this is being made closeable and has no close button, add one.
5993
5994        if (closeable && !close) {
5995          // The close button should be a child of the modal - not its
5996          // content element, so temporarily change the content element.
5997          var temp = this.contentEl_;
5998          this.contentEl_ = this.el_;
5999          close = this.addChild('closeButton', {
6000            controlText: 'Close Modal Dialog'
6001          });
6002          this.contentEl_ = temp;
6003          this.on(close, 'close', this.close);
6004        } // If this is being made uncloseable and has a close button, remove it.
6005
6006
6007        if (!closeable && close) {
6008          this.off(close, 'close', this.close);
6009          this.removeChild(close);
6010          close.dispose();
6011        }
6012      }
6013
6014      return this.closeable_;
6015    }
6016    /**
6017     * Fill the modal's content element with the modal's "content" option.
6018     * The content element will be emptied before this change takes place.
6019     */
6020    ;
6021
6022    _proto.fill = function fill() {
6023      this.fillWith(this.content());
6024    }
6025    /**
6026     * Fill the modal's content element with arbitrary content.
6027     * The content element will be emptied before this change takes place.
6028     *
6029     * @fires ModalDialog#beforemodalfill
6030     * @fires ModalDialog#modalfill
6031     *
6032     * @param {Mixed} [content]
6033     *        The same rules apply to this as apply to the `content` option.
6034     */
6035    ;
6036
6037    _proto.fillWith = function fillWith(content) {
6038      var contentEl = this.contentEl();
6039      var parentEl = contentEl.parentNode;
6040      var nextSiblingEl = contentEl.nextSibling;
6041      /**
6042        * Fired just before a `ModalDialog` is filled with content.
6043        *
6044        * @event ModalDialog#beforemodalfill
6045        * @type {EventTarget~Event}
6046        */
6047
6048      this.trigger('beforemodalfill');
6049      this.hasBeenFilled_ = true; // Detach the content element from the DOM before performing
6050      // manipulation to avoid modifying the live DOM multiple times.
6051
6052      parentEl.removeChild(contentEl);
6053      this.empty();
6054      insertContent(contentEl, content);
6055      /**
6056       * Fired just after a `ModalDialog` is filled with content.
6057       *
6058       * @event ModalDialog#modalfill
6059       * @type {EventTarget~Event}
6060       */
6061
6062      this.trigger('modalfill'); // Re-inject the re-filled content element.
6063
6064      if (nextSiblingEl) {
6065        parentEl.insertBefore(contentEl, nextSiblingEl);
6066      } else {
6067        parentEl.appendChild(contentEl);
6068      } // make sure that the close button is last in the dialog DOM
6069
6070
6071      var closeButton = this.getChild('closeButton');
6072
6073      if (closeButton) {
6074        parentEl.appendChild(closeButton.el_);
6075      }
6076    }
6077    /**
6078     * Empties the content element. This happens anytime the modal is filled.
6079     *
6080     * @fires ModalDialog#beforemodalempty
6081     * @fires ModalDialog#modalempty
6082     */
6083    ;
6084
6085    _proto.empty = function empty() {
6086      /**
6087      * Fired just before a `ModalDialog` is emptied.
6088      *
6089      * @event ModalDialog#beforemodalempty
6090      * @type {EventTarget~Event}
6091      */
6092      this.trigger('beforemodalempty');
6093      emptyEl(this.contentEl());
6094      /**
6095      * Fired just after a `ModalDialog` is emptied.
6096      *
6097      * @event ModalDialog#modalempty
6098      * @type {EventTarget~Event}
6099      */
6100
6101      this.trigger('modalempty');
6102    }
6103    /**
6104     * Gets or sets the modal content, which gets normalized before being
6105     * rendered into the DOM.
6106     *
6107     * This does not update the DOM or fill the modal, but it is called during
6108     * that process.
6109     *
6110     * @param  {Mixed} [value]
6111     *         If defined, sets the internal content value to be used on the
6112     *         next call(s) to `fill`. This value is normalized before being
6113     *         inserted. To "clear" the internal content value, pass `null`.
6114     *
6115     * @return {Mixed}
6116     *         The current content of the modal dialog
6117     */
6118    ;
6119
6120    _proto.content = function content(value) {
6121      if (typeof value !== 'undefined') {
6122        this.content_ = value;
6123      }
6124
6125      return this.content_;
6126    }
6127    /**
6128     * conditionally focus the modal dialog if focus was previously on the player.
6129     *
6130     * @private
6131     */
6132    ;
6133
6134    _proto.conditionalFocus_ = function conditionalFocus_() {
6135      var activeEl = document.activeElement;
6136      var playerEl = this.player_.el_;
6137      this.previouslyActiveEl_ = null;
6138
6139      if (playerEl.contains(activeEl) || playerEl === activeEl) {
6140        this.previouslyActiveEl_ = activeEl;
6141        this.focus();
6142      }
6143    }
6144    /**
6145     * conditionally blur the element and refocus the last focused element
6146     *
6147     * @private
6148     */
6149    ;
6150
6151    _proto.conditionalBlur_ = function conditionalBlur_() {
6152      if (this.previouslyActiveEl_) {
6153        this.previouslyActiveEl_.focus();
6154        this.previouslyActiveEl_ = null;
6155      }
6156    }
6157    /**
6158     * Keydown handler. Attached when modal is focused.
6159     *
6160     * @listens keydown
6161     */
6162    ;
6163
6164    _proto.handleKeyDown = function handleKeyDown(event) {
6165      // Do not allow keydowns to reach out of the modal dialog.
6166      event.stopPropagation();
6167
6168      if (keycode.isEventKey(event, 'Escape') && this.closeable()) {
6169        event.preventDefault();
6170        this.close();
6171        return;
6172      } // exit early if it isn't a tab key
6173
6174
6175      if (!keycode.isEventKey(event, 'Tab')) {
6176        return;
6177      }
6178
6179      var focusableEls = this.focusableEls_();
6180      var activeEl = this.el_.querySelector(':focus');
6181      var focusIndex;
6182
6183      for (var i = 0; i < focusableEls.length; i++) {
6184        if (activeEl === focusableEls[i]) {
6185          focusIndex = i;
6186          break;
6187        }
6188      }
6189
6190      if (document.activeElement === this.el_) {
6191        focusIndex = 0;
6192      }
6193
6194      if (event.shiftKey && focusIndex === 0) {
6195        focusableEls[focusableEls.length - 1].focus();
6196        event.preventDefault();
6197      } else if (!event.shiftKey && focusIndex === focusableEls.length - 1) {
6198        focusableEls[0].focus();
6199        event.preventDefault();
6200      }
6201    }
6202    /**
6203     * get all focusable elements
6204     *
6205     * @private
6206     */
6207    ;
6208
6209    _proto.focusableEls_ = function focusableEls_() {
6210      var allChildren = this.el_.querySelectorAll('*');
6211      return Array.prototype.filter.call(allChildren, function (child) {
6212        return (child instanceof window$1.HTMLAnchorElement || child instanceof window$1.HTMLAreaElement) && child.hasAttribute('href') || (child instanceof window$1.HTMLInputElement || child instanceof window$1.HTMLSelectElement || child instanceof window$1.HTMLTextAreaElement || child instanceof window$1.HTMLButtonElement) && !child.hasAttribute('disabled') || child instanceof window$1.HTMLIFrameElement || child instanceof window$1.HTMLObjectElement || child instanceof window$1.HTMLEmbedElement || child.hasAttribute('tabindex') && child.getAttribute('tabindex') !== -1 || child.hasAttribute('contenteditable');
6213      });
6214    };
6215
6216    return ModalDialog;
6217  }(Component);
6218  /**
6219   * Default options for `ModalDialog` default options.
6220   *
6221   * @type {Object}
6222   * @private
6223   */
6224
6225
6226  ModalDialog.prototype.options_ = {
6227    pauseOnOpen: true,
6228    temporary: true
6229  };
6230  Component.registerComponent('ModalDialog', ModalDialog);
6231
6232  /**
6233   * Common functionaliy between {@link TextTrackList}, {@link AudioTrackList}, and
6234   * {@link VideoTrackList}
6235   *
6236   * @extends EventTarget
6237   */
6238
6239  var TrackList =
6240  /*#__PURE__*/
6241  function (_EventTarget) {
6242    _inheritsLoose(TrackList, _EventTarget);
6243
6244    /**
6245     * Create an instance of this class
6246     *
6247     * @param {Track[]} tracks
6248     *        A list of tracks to initialize the list with.
6249     *
6250     * @abstract
6251     */
6252    function TrackList(tracks) {
6253      var _this;
6254
6255      if (tracks === void 0) {
6256        tracks = [];
6257      }
6258
6259      _this = _EventTarget.call(this) || this;
6260      _this.tracks_ = [];
6261      /**
6262       * @memberof TrackList
6263       * @member {number} length
6264       *         The current number of `Track`s in the this Trackist.
6265       * @instance
6266       */
6267
6268      Object.defineProperty(_assertThisInitialized(_this), 'length', {
6269        get: function get() {
6270          return this.tracks_.length;
6271        }
6272      });
6273
6274      for (var i = 0; i < tracks.length; i++) {
6275        _this.addTrack(tracks[i]);
6276      }
6277
6278      return _this;
6279    }
6280    /**
6281     * Add a {@link Track} to the `TrackList`
6282     *
6283     * @param {Track} track
6284     *        The audio, video, or text track to add to the list.
6285     *
6286     * @fires TrackList#addtrack
6287     */
6288
6289
6290    var _proto = TrackList.prototype;
6291
6292    _proto.addTrack = function addTrack(track) {
6293      var index = this.tracks_.length;
6294
6295      if (!('' + index in this)) {
6296        Object.defineProperty(this, index, {
6297          get: function get() {
6298            return this.tracks_[index];
6299          }
6300        });
6301      } // Do not add duplicate tracks
6302
6303
6304      if (this.tracks_.indexOf(track) === -1) {
6305        this.tracks_.push(track);
6306        /**
6307         * Triggered when a track is added to a track list.
6308         *
6309         * @event TrackList#addtrack
6310         * @type {EventTarget~Event}
6311         * @property {Track} track
6312         *           A reference to track that was added.
6313         */
6314
6315        this.trigger({
6316          track: track,
6317          type: 'addtrack',
6318          target: this
6319        });
6320      }
6321    }
6322    /**
6323     * Remove a {@link Track} from the `TrackList`
6324     *
6325     * @param {Track} rtrack
6326     *        The audio, video, or text track to remove from the list.
6327     *
6328     * @fires TrackList#removetrack
6329     */
6330    ;
6331
6332    _proto.removeTrack = function removeTrack(rtrack) {
6333      var track;
6334
6335      for (var i = 0, l = this.length; i < l; i++) {
6336        if (this[i] === rtrack) {
6337          track = this[i];
6338
6339          if (track.off) {
6340            track.off();
6341          }
6342
6343          this.tracks_.splice(i, 1);
6344          break;
6345        }
6346      }
6347
6348      if (!track) {
6349        return;
6350      }
6351      /**
6352       * Triggered when a track is removed from track list.
6353       *
6354       * @event TrackList#removetrack
6355       * @type {EventTarget~Event}
6356       * @property {Track} track
6357       *           A reference to track that was removed.
6358       */
6359
6360
6361      this.trigger({
6362        track: track,
6363        type: 'removetrack',
6364        target: this
6365      });
6366    }
6367    /**
6368     * Get a Track from the TrackList by a tracks id
6369     *
6370     * @param {string} id - the id of the track to get
6371     * @method getTrackById
6372     * @return {Track}
6373     * @private
6374     */
6375    ;
6376
6377    _proto.getTrackById = function getTrackById(id) {
6378      var result = null;
6379
6380      for (var i = 0, l = this.length; i < l; i++) {
6381        var track = this[i];
6382
6383        if (track.id === id) {
6384          result = track;
6385          break;
6386        }
6387      }
6388
6389      return result;
6390    };
6391
6392    return TrackList;
6393  }(EventTarget);
6394  /**
6395   * Triggered when a different track is selected/enabled.
6396   *
6397   * @event TrackList#change
6398   * @type {EventTarget~Event}
6399   */
6400
6401  /**
6402   * Events that can be called with on + eventName. See {@link EventHandler}.
6403   *
6404   * @property {Object} TrackList#allowedEvents_
6405   * @private
6406   */
6407
6408
6409  TrackList.prototype.allowedEvents_ = {
6410    change: 'change',
6411    addtrack: 'addtrack',
6412    removetrack: 'removetrack'
6413  }; // emulate attribute EventHandler support to allow for feature detection
6414
6415  for (var event in TrackList.prototype.allowedEvents_) {
6416    TrackList.prototype['on' + event] = null;
6417  }
6418
6419  /**
6420   * Anywhere we call this function we diverge from the spec
6421   * as we only support one enabled audiotrack at a time
6422   *
6423   * @param {AudioTrackList} list
6424   *        list to work on
6425   *
6426   * @param {AudioTrack} track
6427   *        The track to skip
6428   *
6429   * @private
6430   */
6431
6432  var disableOthers = function disableOthers(list, track) {
6433    for (var i = 0; i < list.length; i++) {
6434      if (!Object.keys(list[i]).length || track.id === list[i].id) {
6435        continue;
6436      } // another audio track is enabled, disable it
6437
6438
6439      list[i].enabled = false;
6440    }
6441  };
6442  /**
6443   * The current list of {@link AudioTrack} for a media file.
6444   *
6445   * @see [Spec]{@link https://html.spec.whatwg.org/multipage/embedded-content.html#audiotracklist}
6446   * @extends TrackList
6447   */
6448
6449
6450  var AudioTrackList =
6451  /*#__PURE__*/
6452  function (_TrackList) {
6453    _inheritsLoose(AudioTrackList, _TrackList);
6454
6455    /**
6456     * Create an instance of this class.
6457     *
6458     * @param {AudioTrack[]} [tracks=[]]
6459     *        A list of `AudioTrack` to instantiate the list with.
6460     */
6461    function AudioTrackList(tracks) {
6462      var _this;
6463
6464      if (tracks === void 0) {
6465        tracks = [];
6466      }
6467
6468      // make sure only 1 track is enabled
6469      // sorted from last index to first index
6470      for (var i = tracks.length - 1; i >= 0; i--) {
6471        if (tracks[i].enabled) {
6472          disableOthers(tracks, tracks[i]);
6473          break;
6474        }
6475      }
6476
6477      _this = _TrackList.call(this, tracks) || this;
6478      _this.changing_ = false;
6479      return _this;
6480    }
6481    /**
6482     * Add an {@link AudioTrack} to the `AudioTrackList`.
6483     *
6484     * @param {AudioTrack} track
6485     *        The AudioTrack to add to the list
6486     *
6487     * @fires TrackList#addtrack
6488     */
6489
6490
6491    var _proto = AudioTrackList.prototype;
6492
6493    _proto.addTrack = function addTrack(track) {
6494      var _this2 = this;
6495
6496      if (track.enabled) {
6497        disableOthers(this, track);
6498      }
6499
6500      _TrackList.prototype.addTrack.call(this, track); // native tracks don't have this
6501
6502
6503      if (!track.addEventListener) {
6504        return;
6505      }
6506
6507      track.enabledChange_ = function () {
6508        // when we are disabling other tracks (since we don't support
6509        // more than one track at a time) we will set changing_
6510        // to true so that we don't trigger additional change events
6511        if (_this2.changing_) {
6512          return;
6513        }
6514
6515        _this2.changing_ = true;
6516        disableOthers(_this2, track);
6517        _this2.changing_ = false;
6518
6519        _this2.trigger('change');
6520      };
6521      /**
6522       * @listens AudioTrack#enabledchange
6523       * @fires TrackList#change
6524       */
6525
6526
6527      track.addEventListener('enabledchange', track.enabledChange_);
6528    };
6529
6530    _proto.removeTrack = function removeTrack(rtrack) {
6531      _TrackList.prototype.removeTrack.call(this, rtrack);
6532
6533      if (rtrack.removeEventListener && rtrack.enabledChange_) {
6534        rtrack.removeEventListener('enabledchange', rtrack.enabledChange_);
6535        rtrack.enabledChange_ = null;
6536      }
6537    };
6538
6539    return AudioTrackList;
6540  }(TrackList);
6541
6542  /**
6543   * Un-select all other {@link VideoTrack}s that are selected.
6544   *
6545   * @param {VideoTrackList} list
6546   *        list to work on
6547   *
6548   * @param {VideoTrack} track
6549   *        The track to skip
6550   *
6551   * @private
6552   */
6553
6554  var disableOthers$1 = function disableOthers(list, track) {
6555    for (var i = 0; i < list.length; i++) {
6556      if (!Object.keys(list[i]).length || track.id === list[i].id) {
6557        continue;
6558      } // another video track is enabled, disable it
6559
6560
6561      list[i].selected = false;
6562    }
6563  };
6564  /**
6565   * The current list of {@link VideoTrack} for a video.
6566   *
6567   * @see [Spec]{@link https://html.spec.whatwg.org/multipage/embedded-content.html#videotracklist}
6568   * @extends TrackList
6569   */
6570
6571
6572  var VideoTrackList =
6573  /*#__PURE__*/
6574  function (_TrackList) {
6575    _inheritsLoose(VideoTrackList, _TrackList);
6576
6577    /**
6578     * Create an instance of this class.
6579     *
6580     * @param {VideoTrack[]} [tracks=[]]
6581     *        A list of `VideoTrack` to instantiate the list with.
6582     */
6583    function VideoTrackList(tracks) {
6584      var _this;
6585
6586      if (tracks === void 0) {
6587        tracks = [];
6588      }
6589
6590      // make sure only 1 track is enabled
6591      // sorted from last index to first index
6592      for (var i = tracks.length - 1; i >= 0; i--) {
6593        if (tracks[i].selected) {
6594          disableOthers$1(tracks, tracks[i]);
6595          break;
6596        }
6597      }
6598
6599      _this = _TrackList.call(this, tracks) || this;
6600      _this.changing_ = false;
6601      /**
6602       * @member {number} VideoTrackList#selectedIndex
6603       *         The current index of the selected {@link VideoTrack`}.
6604       */
6605
6606      Object.defineProperty(_assertThisInitialized(_this), 'selectedIndex', {
6607        get: function get() {
6608          for (var _i = 0; _i < this.length; _i++) {
6609            if (this[_i].selected) {
6610              return _i;
6611            }
6612          }
6613
6614          return -1;
6615        },
6616        set: function set() {}
6617      });
6618      return _this;
6619    }
6620    /**
6621     * Add a {@link VideoTrack} to the `VideoTrackList`.
6622     *
6623     * @param {VideoTrack} track
6624     *        The VideoTrack to add to the list
6625     *
6626     * @fires TrackList#addtrack
6627     */
6628
6629
6630    var _proto = VideoTrackList.prototype;
6631
6632    _proto.addTrack = function addTrack(track) {
6633      var _this2 = this;
6634
6635      if (track.selected) {
6636        disableOthers$1(this, track);
6637      }
6638
6639      _TrackList.prototype.addTrack.call(this, track); // native tracks don't have this
6640
6641
6642      if (!track.addEventListener) {
6643        return;
6644      }
6645
6646      track.selectedChange_ = function () {
6647        if (_this2.changing_) {
6648          return;
6649        }
6650
6651        _this2.changing_ = true;
6652        disableOthers$1(_this2, track);
6653        _this2.changing_ = false;
6654
6655        _this2.trigger('change');
6656      };
6657      /**
6658       * @listens VideoTrack#selectedchange
6659       * @fires TrackList#change
6660       */
6661
6662
6663      track.addEventListener('selectedchange', track.selectedChange_);
6664    };
6665
6666    _proto.removeTrack = function removeTrack(rtrack) {
6667      _TrackList.prototype.removeTrack.call(this, rtrack);
6668
6669      if (rtrack.removeEventListener && rtrack.selectedChange_) {
6670        rtrack.removeEventListener('selectedchange', rtrack.selectedChange_);
6671        rtrack.selectedChange_ = null;
6672      }
6673    };
6674
6675    return VideoTrackList;
6676  }(TrackList);
6677
6678  /**
6679   * The current list of {@link TextTrack} for a media file.
6680   *
6681   * @see [Spec]{@link https://html.spec.whatwg.org/multipage/embedded-content.html#texttracklist}
6682   * @extends TrackList
6683   */
6684
6685  var TextTrackList =
6686  /*#__PURE__*/
6687  function (_TrackList) {
6688    _inheritsLoose(TextTrackList, _TrackList);
6689
6690    function TextTrackList() {
6691      return _TrackList.apply(this, arguments) || this;
6692    }
6693
6694    var _proto = TextTrackList.prototype;
6695
6696    /**
6697     * Add a {@link TextTrack} to the `TextTrackList`
6698     *
6699     * @param {TextTrack} track
6700     *        The text track to add to the list.
6701     *
6702     * @fires TrackList#addtrack
6703     */
6704    _proto.addTrack = function addTrack(track) {
6705      var _this = this;
6706
6707      _TrackList.prototype.addTrack.call(this, track);
6708
6709      if (!this.queueChange_) {
6710        this.queueChange_ = function () {
6711          return _this.queueTrigger('change');
6712        };
6713      }
6714
6715      if (!this.triggerSelectedlanguagechange) {
6716        this.triggerSelectedlanguagechange_ = function () {
6717          return _this.trigger('selectedlanguagechange');
6718        };
6719      }
6720      /**
6721       * @listens TextTrack#modechange
6722       * @fires TrackList#change
6723       */
6724
6725
6726      track.addEventListener('modechange', this.queueChange_);
6727      var nonLanguageTextTrackKind = ['metadata', 'chapters'];
6728
6729      if (nonLanguageTextTrackKind.indexOf(track.kind) === -1) {
6730        track.addEventListener('modechange', this.triggerSelectedlanguagechange_);
6731      }
6732    };
6733
6734    _proto.removeTrack = function removeTrack(rtrack) {
6735      _TrackList.prototype.removeTrack.call(this, rtrack); // manually remove the event handlers we added
6736
6737
6738      if (rtrack.removeEventListener) {
6739        if (this.queueChange_) {
6740          rtrack.removeEventListener('modechange', this.queueChange_);
6741        }
6742
6743        if (this.selectedlanguagechange_) {
6744          rtrack.removeEventListener('modechange', this.triggerSelectedlanguagechange_);
6745        }
6746      }
6747    };
6748
6749    return TextTrackList;
6750  }(TrackList);
6751
6752  /**
6753   * @file html-track-element-list.js
6754   */
6755
6756  /**
6757   * The current list of {@link HtmlTrackElement}s.
6758   */
6759  var HtmlTrackElementList =
6760  /*#__PURE__*/
6761  function () {
6762    /**
6763     * Create an instance of this class.
6764     *
6765     * @param {HtmlTrackElement[]} [tracks=[]]
6766     *        A list of `HtmlTrackElement` to instantiate the list with.
6767     */
6768    function HtmlTrackElementList(trackElements) {
6769      if (trackElements === void 0) {
6770        trackElements = [];
6771      }
6772
6773      this.trackElements_ = [];
6774      /**
6775       * @memberof HtmlTrackElementList
6776       * @member {number} length
6777       *         The current number of `Track`s in the this Trackist.
6778       * @instance
6779       */
6780
6781      Object.defineProperty(this, 'length', {
6782        get: function get() {
6783          return this.trackElements_.length;
6784        }
6785      });
6786
6787      for (var i = 0, length = trackElements.length; i < length; i++) {
6788        this.addTrackElement_(trackElements[i]);
6789      }
6790    }
6791    /**
6792     * Add an {@link HtmlTrackElement} to the `HtmlTrackElementList`
6793     *
6794     * @param {HtmlTrackElement} trackElement
6795     *        The track element to add to the list.
6796     *
6797     * @private
6798     */
6799
6800
6801    var _proto = HtmlTrackElementList.prototype;
6802
6803    _proto.addTrackElement_ = function addTrackElement_(trackElement) {
6804      var index = this.trackElements_.length;
6805
6806      if (!('' + index in this)) {
6807        Object.defineProperty(this, index, {
6808          get: function get() {
6809            return this.trackElements_[index];
6810          }
6811        });
6812      } // Do not add duplicate elements
6813
6814
6815      if (this.trackElements_.indexOf(trackElement) === -1) {
6816        this.trackElements_.push(trackElement);
6817      }
6818    }
6819    /**
6820     * Get an {@link HtmlTrackElement} from the `HtmlTrackElementList` given an
6821     * {@link TextTrack}.
6822     *
6823     * @param {TextTrack} track
6824     *        The track associated with a track element.
6825     *
6826     * @return {HtmlTrackElement|undefined}
6827     *         The track element that was found or undefined.
6828     *
6829     * @private
6830     */
6831    ;
6832
6833    _proto.getTrackElementByTrack_ = function getTrackElementByTrack_(track) {
6834      var trackElement_;
6835
6836      for (var i = 0, length = this.trackElements_.length; i < length; i++) {
6837        if (track === this.trackElements_[i].track) {
6838          trackElement_ = this.trackElements_[i];
6839          break;
6840        }
6841      }
6842
6843      return trackElement_;
6844    }
6845    /**
6846     * Remove a {@link HtmlTrackElement} from the `HtmlTrackElementList`
6847     *
6848     * @param {HtmlTrackElement} trackElement
6849     *        The track element to remove from the list.
6850     *
6851     * @private
6852     */
6853    ;
6854
6855    _proto.removeTrackElement_ = function removeTrackElement_(trackElement) {
6856      for (var i = 0, length = this.trackElements_.length; i < length; i++) {
6857        if (trackElement === this.trackElements_[i]) {
6858          if (this.trackElements_[i].track && typeof this.trackElements_[i].track.off === 'function') {
6859            this.trackElements_[i].track.off();
6860          }
6861
6862          if (typeof this.trackElements_[i].off === 'function') {
6863            this.trackElements_[i].off();
6864          }
6865
6866          this.trackElements_.splice(i, 1);
6867          break;
6868        }
6869      }
6870    };
6871
6872    return HtmlTrackElementList;
6873  }();
6874
6875  /**
6876   * @file text-track-cue-list.js
6877   */
6878
6879  /**
6880   * @typedef {Object} TextTrackCueList~TextTrackCue
6881   *
6882   * @property {string} id
6883   *           The unique id for this text track cue
6884   *
6885   * @property {number} startTime
6886   *           The start time for this text track cue
6887   *
6888   * @property {number} endTime
6889   *           The end time for this text track cue
6890   *
6891   * @property {boolean} pauseOnExit
6892   *           Pause when the end time is reached if true.
6893   *
6894   * @see [Spec]{@link https://html.spec.whatwg.org/multipage/embedded-content.html#texttrackcue}
6895   */
6896
6897  /**
6898   * A List of TextTrackCues.
6899   *
6900   * @see [Spec]{@link https://html.spec.whatwg.org/multipage/embedded-content.html#texttrackcuelist}
6901   */
6902  var TextTrackCueList =
6903  /*#__PURE__*/
6904  function () {
6905    /**
6906     * Create an instance of this class..
6907     *
6908     * @param {Array} cues
6909     *        A list of cues to be initialized with
6910     */
6911    function TextTrackCueList(cues) {
6912      TextTrackCueList.prototype.setCues_.call(this, cues);
6913      /**
6914       * @memberof TextTrackCueList
6915       * @member {number} length
6916       *         The current number of `TextTrackCue`s in the TextTrackCueList.
6917       * @instance
6918       */
6919
6920      Object.defineProperty(this, 'length', {
6921        get: function get() {
6922          return this.length_;
6923        }
6924      });
6925    }
6926    /**
6927     * A setter for cues in this list. Creates getters
6928     * an an index for the cues.
6929     *
6930     * @param {Array} cues
6931     *        An array of cues to set
6932     *
6933     * @private
6934     */
6935
6936
6937    var _proto = TextTrackCueList.prototype;
6938
6939    _proto.setCues_ = function setCues_(cues) {
6940      var oldLength = this.length || 0;
6941      var i = 0;
6942      var l = cues.length;
6943      this.cues_ = cues;
6944      this.length_ = cues.length;
6945
6946      var defineProp = function defineProp(index) {
6947        if (!('' + index in this)) {
6948          Object.defineProperty(this, '' + index, {
6949            get: function get() {
6950              return this.cues_[index];
6951            }
6952          });
6953        }
6954      };
6955
6956      if (oldLength < l) {
6957        i = oldLength;
6958
6959        for (; i < l; i++) {
6960          defineProp.call(this, i);
6961        }
6962      }
6963    }
6964    /**
6965     * Get a `TextTrackCue` that is currently in the `TextTrackCueList` by id.
6966     *
6967     * @param {string} id
6968     *        The id of the cue that should be searched for.
6969     *
6970     * @return {TextTrackCueList~TextTrackCue|null}
6971     *         A single cue or null if none was found.
6972     */
6973    ;
6974
6975    _proto.getCueById = function getCueById(id) {
6976      var result = null;
6977
6978      for (var i = 0, l = this.length; i < l; i++) {
6979        var cue = this[i];
6980
6981        if (cue.id === id) {
6982          result = cue;
6983          break;
6984        }
6985      }
6986
6987      return result;
6988    };
6989
6990    return TextTrackCueList;
6991  }();
6992
6993  /**
6994   * @file track-kinds.js
6995   */
6996
6997  /**
6998   * All possible `VideoTrackKind`s
6999   *
7000   * @see https://html.spec.whatwg.org/multipage/embedded-content.html#dom-videotrack-kind
7001   * @typedef VideoTrack~Kind
7002   * @enum
7003   */
7004  var VideoTrackKind = {
7005    alternative: 'alternative',
7006    captions: 'captions',
7007    main: 'main',
7008    sign: 'sign',
7009    subtitles: 'subtitles',
7010    commentary: 'commentary'
7011  };
7012  /**
7013   * All possible `AudioTrackKind`s
7014   *
7015   * @see https://html.spec.whatwg.org/multipage/embedded-content.html#dom-audiotrack-kind
7016   * @typedef AudioTrack~Kind
7017   * @enum
7018   */
7019
7020  var AudioTrackKind = {
7021    'alternative': 'alternative',
7022    'descriptions': 'descriptions',
7023    'main': 'main',
7024    'main-desc': 'main-desc',
7025    'translation': 'translation',
7026    'commentary': 'commentary'
7027  };
7028  /**
7029   * All possible `TextTrackKind`s
7030   *
7031   * @see https://html.spec.whatwg.org/multipage/embedded-content.html#dom-texttrack-kind
7032   * @typedef TextTrack~Kind
7033   * @enum
7034   */
7035
7036  var TextTrackKind = {
7037    subtitles: 'subtitles',
7038    captions: 'captions',
7039    descriptions: 'descriptions',
7040    chapters: 'chapters',
7041    metadata: 'metadata'
7042  };
7043  /**
7044   * All possible `TextTrackMode`s
7045   *
7046   * @see https://html.spec.whatwg.org/multipage/embedded-content.html#texttrackmode
7047   * @typedef TextTrack~Mode
7048   * @enum
7049   */
7050
7051  var TextTrackMode = {
7052    disabled: 'disabled',
7053    hidden: 'hidden',
7054    showing: 'showing'
7055  };
7056
7057  /**
7058   * A Track class that contains all of the common functionality for {@link AudioTrack},
7059   * {@link VideoTrack}, and {@link TextTrack}.
7060   *
7061   * > Note: This class should not be used directly
7062   *
7063   * @see {@link https://html.spec.whatwg.org/multipage/embedded-content.html}
7064   * @extends EventTarget
7065   * @abstract
7066   */
7067
7068  var Track =
7069  /*#__PURE__*/
7070  function (_EventTarget) {
7071    _inheritsLoose(Track, _EventTarget);
7072
7073    /**
7074     * Create an instance of this class.
7075     *
7076     * @param {Object} [options={}]
7077     *        Object of option names and values
7078     *
7079     * @param {string} [options.kind='']
7080     *        A valid kind for the track type you are creating.
7081     *
7082     * @param {string} [options.id='vjs_track_' + Guid.newGUID()]
7083     *        A unique id for this AudioTrack.
7084     *
7085     * @param {string} [options.label='']
7086     *        The menu label for this track.
7087     *
7088     * @param {string} [options.language='']
7089     *        A valid two character language code.
7090     *
7091     * @abstract
7092     */
7093    function Track(options) {
7094      var _this;
7095
7096      if (options === void 0) {
7097        options = {};
7098      }
7099
7100      _this = _EventTarget.call(this) || this;
7101      var trackProps = {
7102        id: options.id || 'vjs_track_' + newGUID(),
7103        kind: options.kind || '',
7104        label: options.label || '',
7105        language: options.language || ''
7106      };
7107      /**
7108       * @memberof Track
7109       * @member {string} id
7110       *         The id of this track. Cannot be changed after creation.
7111       * @instance
7112       *
7113       * @readonly
7114       */
7115
7116      /**
7117       * @memberof Track
7118       * @member {string} kind
7119       *         The kind of track that this is. Cannot be changed after creation.
7120       * @instance
7121       *
7122       * @readonly
7123       */
7124
7125      /**
7126       * @memberof Track
7127       * @member {string} label
7128       *         The label of this track. Cannot be changed after creation.
7129       * @instance
7130       *
7131       * @readonly
7132       */
7133
7134      /**
7135       * @memberof Track
7136       * @member {string} language
7137       *         The two letter language code for this track. Cannot be changed after
7138       *         creation.
7139       * @instance
7140       *
7141       * @readonly
7142       */
7143
7144      var _loop = function _loop(key) {
7145        Object.defineProperty(_assertThisInitialized(_this), key, {
7146          get: function get() {
7147            return trackProps[key];
7148          },
7149          set: function set() {}
7150        });
7151      };
7152
7153      for (var key in trackProps) {
7154        _loop(key);
7155      }
7156
7157      return _this;
7158    }
7159
7160    return Track;
7161  }(EventTarget);
7162
7163  /**
7164   * @file url.js
7165   * @module url
7166   */
7167  /**
7168   * @typedef {Object} url:URLObject
7169   *
7170   * @property {string} protocol
7171   *           The protocol of the url that was parsed.
7172   *
7173   * @property {string} hostname
7174   *           The hostname of the url that was parsed.
7175   *
7176   * @property {string} port
7177   *           The port of the url that was parsed.
7178   *
7179   * @property {string} pathname
7180   *           The pathname of the url that was parsed.
7181   *
7182   * @property {string} search
7183   *           The search query of the url that was parsed.
7184   *
7185   * @property {string} hash
7186   *           The hash of the url that was parsed.
7187   *
7188   * @property {string} host
7189   *           The host of the url that was parsed.
7190   */
7191
7192  /**
7193   * Resolve and parse the elements of a URL.
7194   *
7195   * @function
7196   * @param    {String} url
7197   *           The url to parse
7198   *
7199   * @return   {url:URLObject}
7200   *           An object of url details
7201   */
7202
7203  var parseUrl = function parseUrl(url) {
7204    var props = ['protocol', 'hostname', 'port', 'pathname', 'search', 'hash', 'host']; // add the url to an anchor and let the browser parse the URL
7205
7206    var a = document.createElement('a');
7207    a.href = url; // IE8 (and 9?) Fix
7208    // ie8 doesn't parse the URL correctly until the anchor is actually
7209    // added to the body, and an innerHTML is needed to trigger the parsing
7210
7211    var addToBody = a.host === '' && a.protocol !== 'file:';
7212    var div;
7213
7214    if (addToBody) {
7215      div = document.createElement('div');
7216      div.innerHTML = "<a href=\"" + url + "\"></a>";
7217      a = div.firstChild; // prevent the div from affecting layout
7218
7219      div.setAttribute('style', 'display:none; position:absolute;');
7220      document.body.appendChild(div);
7221    } // Copy the specific URL properties to a new object
7222    // This is also needed for IE8 because the anchor loses its
7223    // properties when it's removed from the dom
7224
7225
7226    var details = {};
7227
7228    for (var i = 0; i < props.length; i++) {
7229      details[props[i]] = a[props[i]];
7230    } // IE9 adds the port to the host property unlike everyone else. If
7231    // a port identifier is added for standard ports, strip it.
7232
7233
7234    if (details.protocol === 'http:') {
7235      details.host = details.host.replace(/:80$/, '');
7236    }
7237
7238    if (details.protocol === 'https:') {
7239      details.host = details.host.replace(/:443$/, '');
7240    }
7241
7242    if (!details.protocol) {
7243      details.protocol = window$1.location.protocol;
7244    }
7245
7246    if (addToBody) {
7247      document.body.removeChild(div);
7248    }
7249
7250    return details;
7251  };
7252  /**
7253   * Get absolute version of relative URL. Used to tell Flash the correct URL.
7254   *
7255   * @function
7256   * @param    {string} url
7257   *           URL to make absolute
7258   *
7259   * @return   {string}
7260   *           Absolute URL
7261   *
7262   * @see      http://stackoverflow.com/questions/470832/getting-an-absolute-url-from-a-relative-one-ie6-issue
7263   */
7264
7265  var getAbsoluteURL = function getAbsoluteURL(url) {
7266    // Check if absolute URL
7267    if (!url.match(/^https?:\/\//)) {
7268      // Convert to absolute URL. Flash hosted off-site needs an absolute URL.
7269      var div = document.createElement('div');
7270      div.innerHTML = "<a href=\"" + url + "\">x</a>";
7271      url = div.firstChild.href;
7272    }
7273
7274    return url;
7275  };
7276  /**
7277   * Returns the extension of the passed file name. It will return an empty string
7278   * if passed an invalid path.
7279   *
7280   * @function
7281   * @param    {string} path
7282   *           The fileName path like '/path/to/file.mp4'
7283   *
7284   * @return  {string}
7285   *           The extension in lower case or an empty string if no
7286   *           extension could be found.
7287   */
7288
7289  var getFileExtension = function getFileExtension(path) {
7290    if (typeof path === 'string') {
7291      var splitPathRe = /^(\/?)([\s\S]*?)((?:\.{1,2}|[^\/]+?)(\.([^\.\/\?]+)))(?:[\/]*|[\?].*)$/;
7292      var pathParts = splitPathRe.exec(path);
7293
7294      if (pathParts) {
7295        return pathParts.pop().toLowerCase();
7296      }
7297    }
7298
7299    return '';
7300  };
7301  /**
7302   * Returns whether the url passed is a cross domain request or not.
7303   *
7304   * @function
7305   * @param    {string} url
7306   *           The url to check.
7307   *
7308   * @return   {boolean}
7309   *           Whether it is a cross domain request or not.
7310   */
7311
7312  var isCrossOrigin = function isCrossOrigin(url) {
7313    var winLoc = window$1.location;
7314    var urlInfo = parseUrl(url); // IE8 protocol relative urls will return ':' for protocol
7315
7316    var srcProtocol = urlInfo.protocol === ':' ? winLoc.protocol : urlInfo.protocol; // Check if url is for another domain/origin
7317    // IE8 doesn't know location.origin, so we won't rely on it here
7318
7319    var crossOrigin = srcProtocol + urlInfo.host !== winLoc.protocol + winLoc.host;
7320    return crossOrigin;
7321  };
7322
7323  var Url = /*#__PURE__*/Object.freeze({
7324    parseUrl: parseUrl,
7325    getAbsoluteURL: getAbsoluteURL,
7326    getFileExtension: getFileExtension,
7327    isCrossOrigin: isCrossOrigin
7328  });
7329
7330  var isFunction_1 = isFunction;
7331  var toString$1 = Object.prototype.toString;
7332
7333  function isFunction(fn) {
7334    var string = toString$1.call(fn);
7335    return string === '[object Function]' || typeof fn === 'function' && string !== '[object RegExp]' || typeof window !== 'undefined' && ( // IE8 and below
7336    fn === window.setTimeout || fn === window.alert || fn === window.confirm || fn === window.prompt);
7337  }
7338
7339  /* eslint no-invalid-this: 1 */
7340
7341  var ERROR_MESSAGE = 'Function.prototype.bind called on incompatible ';
7342  var slice = Array.prototype.slice;
7343  var toStr = Object.prototype.toString;
7344  var funcType = '[object Function]';
7345
7346  var implementation = function bind(that) {
7347    var target = this;
7348
7349    if (typeof target !== 'function' || toStr.call(target) !== funcType) {
7350      throw new TypeError(ERROR_MESSAGE + target);
7351    }
7352
7353    var args = slice.call(arguments, 1);
7354    var bound;
7355
7356    var binder = function binder() {
7357      if (this instanceof bound) {
7358        var result = target.apply(this, args.concat(slice.call(arguments)));
7359
7360        if (Object(result) === result) {
7361          return result;
7362        }
7363
7364        return this;
7365      } else {
7366        return target.apply(that, args.concat(slice.call(arguments)));
7367      }
7368    };
7369
7370    var boundLength = Math.max(0, target.length - args.length);
7371    var boundArgs = [];
7372
7373    for (var i = 0; i < boundLength; i++) {
7374      boundArgs.push('$' + i);
7375    }
7376
7377    bound = Function('binder', 'return function (' + boundArgs.join(',') + '){ return binder.apply(this,arguments); }')(binder);
7378
7379    if (target.prototype) {
7380      var Empty = function Empty() {};
7381
7382      Empty.prototype = target.prototype;
7383      bound.prototype = new Empty();
7384      Empty.prototype = null;
7385    }
7386
7387    return bound;
7388  };
7389
7390  var functionBind = Function.prototype.bind || implementation;
7391
7392  var toStr$1 = Object.prototype.toString;
7393
7394  var isArguments = function isArguments(value) {
7395    var str = toStr$1.call(value);
7396    var isArgs = str === '[object Arguments]';
7397
7398    if (!isArgs) {
7399      isArgs = str !== '[object Array]' && value !== null && typeof value === 'object' && typeof value.length === 'number' && value.length >= 0 && toStr$1.call(value.callee) === '[object Function]';
7400    }
7401
7402    return isArgs;
7403  };
7404
7405  var keysShim;
7406
7407  if (!Object.keys) {
7408    // modified from https://github.com/es-shims/es5-shim
7409    var has = Object.prototype.hasOwnProperty;
7410    var toStr$2 = Object.prototype.toString;
7411    var isArgs = isArguments; // eslint-disable-line global-require
7412
7413    var isEnumerable = Object.prototype.propertyIsEnumerable;
7414    var hasDontEnumBug = !isEnumerable.call({
7415      toString: null
7416    }, 'toString');
7417    var hasProtoEnumBug = isEnumerable.call(function () {}, 'prototype');
vendor: 8,170 bytes, lines 7418-7695
7418    var dontEnums = ['toString', 'toLocaleString', 'valueOf', 'hasOwnProperty', 'isPrototypeOf', 'propertyIsEnumerable', 'constructor'];
7419
7420    var equalsConstructorPrototype = function equalsConstructorPrototype(o) {
7421      var ctor = o.constructor;
7422      return ctor && ctor.prototype === o;
7423    };
7424
7425    var excludedKeys = {
7426      $applicationCache: true,
7427      $console: true,
7428      $external: true,
7429      $frame: true,
7430      $frameElement: true,
7431      $frames: true,
7432      $innerHeight: true,
7433      $innerWidth: true,
7434      $onmozfullscreenchange: true,
7435      $onmozfullscreenerror: true,
7436      $outerHeight: true,
7437      $outerWidth: true,
7438      $pageXOffset: true,
7439      $pageYOffset: true,
7440      $parent: true,
7441      $scrollLeft: true,
7442      $scrollTop: true,
7443      $scrollX: true,
7444      $scrollY: true,
7445      $self: true,
7446      $webkitIndexedDB: true,
7447      $webkitStorageInfo: true,
7448      $window: true
7449    };
7450
7451    var hasAutomationEqualityBug = function () {
7452      /* global window */
7453      if (typeof window === 'undefined') {
7454        return false;
7455      }
7456
7457      for (var k in window) {
7458        try {
7459          if (!excludedKeys['$' + k] && has.call(window, k) && window[k] !== null && typeof window[k] === 'object') {
7460            try {
7461              equalsConstructorPrototype(window[k]);
7462            } catch (e) {
7463              return true;
7464            }
7465          }
7466        } catch (e) {
7467          return true;
7468        }
7469      }
7470
7471      return false;
7472    }();
7473
7474    var equalsConstructorPrototypeIfNotBuggy = function equalsConstructorPrototypeIfNotBuggy(o) {
7475      /* global window */
7476      if (typeof window === 'undefined' || !hasAutomationEqualityBug) {
7477        return equalsConstructorPrototype(o);
7478      }
7479
7480      try {
7481        return equalsConstructorPrototype(o);
7482      } catch (e) {
7483        return false;
7484      }
7485    };
7486
7487    keysShim = function keys(object) {
7488      var isObject = object !== null && typeof object === 'object';
7489      var isFunction = toStr$2.call(object) === '[object Function]';
7490      var isArguments = isArgs(object);
7491      var isString = isObject && toStr$2.call(object) === '[object String]';
7492      var theKeys = [];
7493
7494      if (!isObject && !isFunction && !isArguments) {
7495        throw new TypeError('Object.keys called on a non-object');
7496      }
7497
7498      var skipProto = hasProtoEnumBug && isFunction;
7499
7500      if (isString && object.length > 0 && !has.call(object, 0)) {
7501        for (var i = 0; i < object.length; ++i) {
7502          theKeys.push(String(i));
7503        }
7504      }
7505
7506      if (isArguments && object.length > 0) {
7507        for (var j = 0; j < object.length; ++j) {
7508          theKeys.push(String(j));
7509        }
7510      } else {
7511        for (var name in object) {
7512          if (!(skipProto && name === 'prototype') && has.call(object, name)) {
7513            theKeys.push(String(name));
7514          }
7515        }
7516      }
7517
7518      if (hasDontEnumBug) {
7519        var skipConstructor = equalsConstructorPrototypeIfNotBuggy(object);
7520
7521        for (var k = 0; k < dontEnums.length; ++k) {
7522          if (!(skipConstructor && dontEnums[k] === 'constructor') && has.call(object, dontEnums[k])) {
7523            theKeys.push(dontEnums[k]);
7524          }
7525        }
7526      }
7527
7528      return theKeys;
7529    };
7530  }
7531
7532  var implementation$1 = keysShim;
7533
7534  var slice$1 = Array.prototype.slice;
7535  var origKeys = Object.keys;
7536  var keysShim$1 = origKeys ? function keys(o) {
7537    return origKeys(o);
7538  } : implementation$1;
7539  var originalKeys = Object.keys;
7540
7541  keysShim$1.shim = function shimObjectKeys() {
7542    if (Object.keys) {
7543      var keysWorksWithArguments = function () {
7544        // Safari 5.0 bug
7545        var args = Object.keys(arguments);
7546        return args && args.length === arguments.length;
7547      }(1, 2);
7548
7549      if (!keysWorksWithArguments) {
7550        Object.keys = function keys(object) {
7551          // eslint-disable-line func-name-matching
7552          if (isArguments(object)) {
7553            return originalKeys(slice$1.call(object));
7554          }
7555
7556          return originalKeys(object);
7557        };
7558      }
7559    } else {
7560      Object.keys = keysShim$1;
7561    }
7562
7563    return Object.keys || keysShim$1;
7564  };
7565
7566  var objectKeys = keysShim$1;
7567
7568  var hasSymbols = typeof Symbol === 'function' && typeof Symbol('foo') === 'symbol';
7569  var toStr$3 = Object.prototype.toString;
7570  var concat = Array.prototype.concat;
7571  var origDefineProperty = Object.defineProperty;
7572
7573  var isFunction$1 = function isFunction(fn) {
7574    return typeof fn === 'function' && toStr$3.call(fn) === '[object Function]';
7575  };
7576
7577  var arePropertyDescriptorsSupported = function arePropertyDescriptorsSupported() {
7578    var obj = {};
7579
7580    try {
7581      origDefineProperty(obj, 'x', {
7582        enumerable: false,
7583        value: obj
7584      }); // eslint-disable-next-line no-unused-vars, no-restricted-syntax
7585
7586      for (var _ in obj) {
7587        // jscs:ignore disallowUnusedVariables
7588        return false;
7589      }
7590
7591      return obj.x === obj;
7592    } catch (e) {
7593      /* this is IE 8. */
7594      return false;
7595    }
7596  };
7597
7598  var supportsDescriptors = origDefineProperty && arePropertyDescriptorsSupported();
7599
7600  var defineProperty = function defineProperty(object, name, value, predicate) {
7601    if (name in object && (!isFunction$1(predicate) || !predicate())) {
7602      return;
7603    }
7604
7605    if (supportsDescriptors) {
7606      origDefineProperty(object, name, {
7607        configurable: true,
7608        enumerable: false,
7609        value: value,
7610        writable: true
7611      });
7612    } else {
7613      object[name] = value;
7614    }
7615  };
7616
7617  var defineProperties = function defineProperties(object, map) {
7618    var predicates = arguments.length > 2 ? arguments[2] : {};
7619    var props = objectKeys(map);
7620
7621    if (hasSymbols) {
7622      props = concat.call(props, Object.getOwnPropertySymbols(map));
7623    }
7624
7625    for (var i = 0; i < props.length; i += 1) {
7626      defineProperty(object, props[i], map[props[i]], predicates[props[i]]);
7627    }
7628  };
7629
7630  defineProperties.supportsDescriptors = !!supportsDescriptors;
7631  var defineProperties_1 = defineProperties;
7632
7633  /* globals
7634  	Set,
7635  	Map,
7636  	WeakSet,
7637  	WeakMap,
7638
7639  	Promise,
7640
7641  	Symbol,
7642  	Proxy,
7643
7644  	Atomics,
7645  	SharedArrayBuffer,
7646
7647  	ArrayBuffer,
7648  	DataView,
7649  	Uint8Array,
7650  	Float32Array,
7651  	Float64Array,
7652  	Int8Array,
7653  	Int16Array,
7654  	Int32Array,
7655  	Uint8ClampedArray,
7656  	Uint16Array,
7657  	Uint32Array,
7658  */
7659
7660  var undefined$1; // eslint-disable-line no-shadow-restricted-names
7661
7662  var ThrowTypeError = Object.getOwnPropertyDescriptor ? function () {
7663    return Object.getOwnPropertyDescriptor(arguments, 'callee').get;
7664  }() : function () {
7665    throw new TypeError();
7666  };
7667  var hasSymbols$1 = typeof Symbol === 'function' && typeof Symbol.iterator === 'symbol';
7668
7669  var getProto = Object.getPrototypeOf || function (x) {
7670    return x.__proto__;
7671  }; // eslint-disable-line no-proto
7672
7673  var generatorFunction =  undefined$1;
7674
7675  var asyncFunction =  undefined$1;
7676
7677  var asyncGenFunction =  undefined$1;
7678  var TypedArray = typeof Uint8Array === 'undefined' ? undefined$1 : getProto(Uint8Array);
7679  var INTRINSICS = {
7680    '$ %Array%': Array,
7681    '$ %ArrayBuffer%': typeof ArrayBuffer === 'undefined' ? undefined$1 : ArrayBuffer,
7682    '$ %ArrayBufferPrototype%': typeof ArrayBuffer === 'undefined' ? undefined$1 : ArrayBuffer.prototype,
7683    '$ %ArrayIteratorPrototype%': hasSymbols$1 ? getProto([][Symbol.iterator]()) : undefined$1,
7684    '$ %ArrayPrototype%': Array.prototype,
7685    '$ %ArrayProto_entries%': Array.prototype.entries,
7686    '$ %ArrayProto_forEach%': Array.prototype.forEach,
7687    '$ %ArrayProto_keys%': Array.prototype.keys,
7688    '$ %ArrayProto_values%': Array.prototype.values,
7689    '$ %AsyncFromSyncIteratorPrototype%': undefined$1,
7690    '$ %AsyncFunction%': asyncFunction,
7691    '$ %AsyncFunctionPrototype%':  undefined$1,
7692    '$ %AsyncGenerator%':  undefined$1,
7693    '$ %AsyncGeneratorFunction%': asyncGenFunction,
7694    '$ %AsyncGeneratorPrototype%':  undefined$1,
7695    '$ %AsyncIteratorPrototype%':  undefined$1,
vendor: 5,733 bytes, lines 7696-7784
7696    '$ %Atomics%': typeof Atomics === 'undefined' ? undefined$1 : Atomics,
7697    '$ %Boolean%': Boolean,
7698    '$ %BooleanPrototype%': Boolean.prototype,
7699    '$ %DataView%': typeof DataView === 'undefined' ? undefined$1 : DataView,
7700    '$ %DataViewPrototype%': typeof DataView === 'undefined' ? undefined$1 : DataView.prototype,
7701    '$ %Date%': Date,
7702    '$ %DatePrototype%': Date.prototype,
7703    '$ %decodeURI%': decodeURI,
7704    '$ %decodeURIComponent%': decodeURIComponent,
7705    '$ %encodeURI%': encodeURI,
7706    '$ %encodeURIComponent%': encodeURIComponent,
7707    '$ %Error%': Error,
7708    '$ %ErrorPrototype%': Error.prototype,
7709    '$ %eval%': eval,
7710    // eslint-disable-line no-eval
7711    '$ %EvalError%': EvalError,
7712    '$ %EvalErrorPrototype%': EvalError.prototype,
7713    '$ %Float32Array%': typeof Float32Array === 'undefined' ? undefined$1 : Float32Array,
7714    '$ %Float32ArrayPrototype%': typeof Float32Array === 'undefined' ? undefined$1 : Float32Array.prototype,
7715    '$ %Float64Array%': typeof Float64Array === 'undefined' ? undefined$1 : Float64Array,
7716    '$ %Float64ArrayPrototype%': typeof Float64Array === 'undefined' ? undefined$1 : Float64Array.prototype,
7717    '$ %Function%': Function,
7718    '$ %FunctionPrototype%': Function.prototype,
7719    '$ %Generator%':  undefined$1,
7720    '$ %GeneratorFunction%': generatorFunction,
7721    '$ %GeneratorPrototype%':  undefined$1,
7722    '$ %Int8Array%': typeof Int8Array === 'undefined' ? undefined$1 : Int8Array,
7723    '$ %Int8ArrayPrototype%': typeof Int8Array === 'undefined' ? undefined$1 : Int8Array.prototype,
7724    '$ %Int16Array%': typeof Int16Array === 'undefined' ? undefined$1 : Int16Array,
7725    '$ %Int16ArrayPrototype%': typeof Int16Array === 'undefined' ? undefined$1 : Int8Array.prototype,
7726    '$ %Int32Array%': typeof Int32Array === 'undefined' ? undefined$1 : Int32Array,
7727    '$ %Int32ArrayPrototype%': typeof Int32Array === 'undefined' ? undefined$1 : Int32Array.prototype,
7728    '$ %isFinite%': isFinite,
7729    '$ %isNaN%': isNaN,
7730    '$ %IteratorPrototype%': hasSymbols$1 ? getProto(getProto([][Symbol.iterator]())) : undefined$1,
7731    '$ %JSON%': JSON,
7732    '$ %JSONParse%': JSON.parse,
7733    '$ %Map%': typeof Map === 'undefined' ? undefined$1 : Map,
7734    '$ %MapIteratorPrototype%': typeof Map === 'undefined' || !hasSymbols$1 ? undefined$1 : getProto(new Map()[Symbol.iterator]()),
7735    '$ %MapPrototype%': typeof Map === 'undefined' ? undefined$1 : Map.prototype,
7736    '$ %Math%': Math,
7737    '$ %Number%': Number,
7738    '$ %NumberPrototype%': Number.prototype,
7739    '$ %Object%': Object,
7740    '$ %ObjectPrototype%': Object.prototype,
7741    '$ %ObjProto_toString%': Object.prototype.toString,
7742    '$ %ObjProto_valueOf%': Object.prototype.valueOf,
7743    '$ %parseFloat%': parseFloat,
7744    '$ %parseInt%': parseInt,
7745    '$ %Promise%': typeof Promise === 'undefined' ? undefined$1 : Promise,
7746    '$ %PromisePrototype%': typeof Promise === 'undefined' ? undefined$1 : Promise.prototype,
7747    '$ %PromiseProto_then%': typeof Promise === 'undefined' ? undefined$1 : Promise.prototype.then,
7748    '$ %Promise_all%': typeof Promise === 'undefined' ? undefined$1 : Promise.all,
7749    '$ %Promise_reject%': typeof Promise === 'undefined' ? undefined$1 : Promise.reject,
7750    '$ %Promise_resolve%': typeof Promise === 'undefined' ? undefined$1 : Promise.resolve,
7751    '$ %Proxy%': typeof Proxy === 'undefined' ? undefined$1 : Proxy,
7752    '$ %RangeError%': RangeError,
7753    '$ %RangeErrorPrototype%': RangeError.prototype,
7754    '$ %ReferenceError%': ReferenceError,
7755    '$ %ReferenceErrorPrototype%': ReferenceError.prototype,
7756    '$ %Reflect%': typeof Reflect === 'undefined' ? undefined$1 : Reflect,
7757    '$ %RegExp%': RegExp,
7758    '$ %RegExpPrototype%': RegExp.prototype,
7759    '$ %Set%': typeof Set === 'undefined' ? undefined$1 : Set,
7760    '$ %SetIteratorPrototype%': typeof Set === 'undefined' || !hasSymbols$1 ? undefined$1 : getProto(new Set()[Symbol.iterator]()),
7761    '$ %SetPrototype%': typeof Set === 'undefined' ? undefined$1 : Set.prototype,
7762    '$ %SharedArrayBuffer%': typeof SharedArrayBuffer === 'undefined' ? undefined$1 : SharedArrayBuffer,
7763    '$ %SharedArrayBufferPrototype%': typeof SharedArrayBuffer === 'undefined' ? undefined$1 : SharedArrayBuffer.prototype,
7764    '$ %String%': String,
7765    '$ %StringIteratorPrototype%': hasSymbols$1 ? getProto(''[Symbol.iterator]()) : undefined$1,
7766    '$ %StringPrototype%': String.prototype,
7767    '$ %Symbol%': hasSymbols$1 ? Symbol : undefined$1,
7768    '$ %SymbolPrototype%': hasSymbols$1 ? Symbol.prototype : undefined$1,
7769    '$ %SyntaxError%': SyntaxError,
7770    '$ %SyntaxErrorPrototype%': SyntaxError.prototype,
7771    '$ %ThrowTypeError%': ThrowTypeError,
7772    '$ %TypedArray%': TypedArray,
7773    '$ %TypedArrayPrototype%': TypedArray ? TypedArray.prototype : undefined$1,
7774    '$ %TypeError%': TypeError,
7775    '$ %TypeErrorPrototype%': TypeError.prototype,
7776    '$ %Uint8Array%': typeof Uint8Array === 'undefined' ? undefined$1 : Uint8Array,
7777    '$ %Uint8ArrayPrototype%': typeof Uint8Array === 'undefined' ? undefined$1 : Uint8Array.prototype,
7778    '$ %Uint8ClampedArray%': typeof Uint8ClampedArray === 'undefined' ? undefined$1 : Uint8ClampedArray,
7779    '$ %Uint8ClampedArrayPrototype%': typeof Uint8ClampedArray === 'undefined' ? undefined$1 : Uint8ClampedArray.prototype,
7780    '$ %Uint16Array%': typeof Uint16Array === 'undefined' ? undefined$1 : Uint16Array,
7781    '$ %Uint16ArrayPrototype%': typeof Uint16Array === 'undefined' ? undefined$1 : Uint16Array.prototype,
7782    '$ %Uint32Array%': typeof Uint32Array === 'undefined' ? undefined$1 : Uint32Array,
7783    '$ %Uint32ArrayPrototype%': typeof Uint32Array === 'undefined' ? undefined$1 : Uint32Array.prototype,
7784    '$ %URIError%': URIError,
7785    '$ %URIErrorPrototype%': URIError.prototype,
7786    '$ %WeakMap%': typeof WeakMap === 'undefined' ? undefined$1 : WeakMap,
7787    '$ %WeakMapPrototype%': typeof WeakMap === 'undefined' ? undefined$1 : WeakMap.prototype,
7788    '$ %WeakSet%': typeof WeakSet === 'undefined' ? undefined$1 : WeakSet,
7789    '$ %WeakSetPrototype%': typeof WeakSet === 'undefined' ? undefined$1 : WeakSet.prototype
7790  };
7791
7792  var GetIntrinsic = function GetIntrinsic(name, allowMissing) {
7793    if (arguments.length > 1 && typeof allowMissing !== 'boolean') {
7794      throw new TypeError('"allowMissing" argument must be a boolean');
7795    }
7796
7797    var key = '$ ' + name;
7798
7799    if (!(key in INTRINSICS)) {
7800      throw new SyntaxError('intrinsic ' + name + ' does not exist!');
7801    } // istanbul ignore if // hopefully this is impossible to test :-)
7802
7803
7804    if (typeof INTRINSICS[key] === 'undefined' && !allowMissing) {
7805      throw new TypeError('intrinsic ' + name + ' exists, but is not available. Please file an issue!');
7806    }
7807
7808    return INTRINSICS[key];
7809  };
7810
7811  var src = functionBind.call(Function.call, Object.prototype.hasOwnProperty);
7812
7813  var $TypeError = GetIntrinsic('%TypeError%');
7814  var $SyntaxError = GetIntrinsic('%SyntaxError%');
7815  var predicates = {
7816    // https://ecma-international.org/ecma-262/6.0/#sec-property-descriptor-specification-type
7817    'Property Descriptor': function isPropertyDescriptor(ES, Desc) {
7818      if (ES.Type(Desc) !== 'Object') {
7819        return false;
7820      }
7821
7822      var allowed = {
7823        '[[Configurable]]': true,
7824        '[[Enumerable]]': true,
7825        '[[Get]]': true,
7826        '[[Set]]': true,
7827        '[[Value]]': true,
7828        '[[Writable]]': true
7829      };
7830
7831      for (var key in Desc) {
7832        // eslint-disable-line
7833        if (src(Desc, key) && !allowed[key]) {
7834          return false;
7835        }
7836      }
7837
7838      var isData = src(Desc, '[[Value]]');
7839      var IsAccessor = src(Desc, '[[Get]]') || src(Desc, '[[Set]]');
7840
7841      if (isData && IsAccessor) {
7842        throw new $TypeError('Property Descriptors may not be both accessor and data descriptors');
7843      }
7844
7845      return true;
7846    }
7847  };
7848
7849  var assertRecord = function assertRecord(ES, recordType, argumentName, value) {
7850    var predicate = predicates[recordType];
7851
7852    if (typeof predicate !== 'function') {
7853      throw new $SyntaxError('unknown record type: ' + recordType);
7854    }
7855
7856    if (!predicate(ES, value)) {
7857      throw new $TypeError(argumentName + ' must be a ' + recordType);
7858    }
7859
7860    console.log(predicate(ES, value), value);
7861  };
7862
7863  var _isNaN = Number.isNaN || function isNaN(a) {
7864    return a !== a;
7865  };
7866
7867  var $isNaN = Number.isNaN || function (a) {
7868    return a !== a;
7869  };
7870
7871  var _isFinite = Number.isFinite || function (x) {
7872    return typeof x === 'number' && !$isNaN(x) && x !== Infinity && x !== -Infinity;
7873  };
7874
7875  var sign = function sign(number) {
7876    return number >= 0 ? 1 : -1;
7877  };
7878
7879  var mod = function mod(number, modulo) {
7880    var remain = number % modulo;
7881    return Math.floor(remain >= 0 ? remain : remain + modulo);
7882  };
7883
7884  var fnToStr = Function.prototype.toString;
7885  var constructorRegex = /^\s*class\b/;
7886
7887  var isES6ClassFn = function isES6ClassFunction(value) {
7888    try {
7889      var fnStr = fnToStr.call(value);
7890      return constructorRegex.test(fnStr);
7891    } catch (e) {
7892      return false; // not a function
7893    }
7894  };
7895
7896  var tryFunctionObject = function tryFunctionToStr(value) {
7897    try {
7898      if (isES6ClassFn(value)) {
7899        return false;
7900      }
7901
7902      fnToStr.call(value);
7903      return true;
7904    } catch (e) {
7905      return false;
7906    }
7907  };
7908
7909  var toStr$4 = Object.prototype.toString;
7910  var fnClass = '[object Function]';
7911  var genClass = '[object GeneratorFunction]';
7912  var hasToStringTag = typeof Symbol === 'function' && typeof Symbol.toStringTag === 'symbol';
7913
7914  var isCallable = function isCallable(value) {
7915    if (!value) {
7916      return false;
7917    }
7918
7919    if (typeof value !== 'function' && typeof value !== 'object') {
7920      return false;
7921    }
7922
7923    if (typeof value === 'function' && !value.prototype) {
7924      return true;
7925    }
7926
7927    if (hasToStringTag) {
7928      return tryFunctionObject(value);
7929    }
7930
7931    if (isES6ClassFn(value)) {
7932      return false;
7933    }
7934
7935    var strClass = toStr$4.call(value);
7936    return strClass === fnClass || strClass === genClass;
7937  };
7938
7939  var isPrimitive = function isPrimitive(value) {
7940    return value === null || typeof value !== 'function' && typeof value !== 'object';
7941  };
7942
7943  var toStr$5 = Object.prototype.toString; // http://ecma-international.org/ecma-262/5.1/#sec-8.12.8
7944
7945  var ES5internalSlots = {
7946    '[[DefaultValue]]': function DefaultValue(O) {
7947      var actualHint;
7948
7949      if (arguments.length > 1) {
7950        actualHint = arguments[1];
7951      } else {
7952        actualHint = toStr$5.call(O) === '[object Date]' ? String : Number;
7953      }
7954
7955      if (actualHint === String || actualHint === Number) {
7956        var methods = actualHint === String ? ['toString', 'valueOf'] : ['valueOf', 'toString'];
7957        var value, i;
7958
7959        for (i = 0; i < methods.length; ++i) {
7960          if (isCallable(O[methods[i]])) {
7961            value = O[methods[i]]();
7962
7963            if (isPrimitive(value)) {
7964              return value;
7965            }
7966          }
7967        }
7968
7969        throw new TypeError('No default value');
7970      }
7971
7972      throw new TypeError('invalid [[DefaultValue]] hint supplied');
7973    }
7974  }; // http://ecma-international.org/ecma-262/5.1/#sec-9.1
7975
7976  var es5 = function ToPrimitive(input) {
7977    if (isPrimitive(input)) {
7978      return input;
7979    }
7980
7981    if (arguments.length > 1) {
7982      return ES5internalSlots['[[DefaultValue]]'](input, arguments[1]);
7983    }
7984
7985    return ES5internalSlots['[[DefaultValue]]'](input);
7986  };
7987
7988  var $Object = GetIntrinsic('%Object%');
7989  var $TypeError$1 = GetIntrinsic('%TypeError%');
7990  var $String = GetIntrinsic('%String%'); // https://es5.github.io/#x9
7991
7992  var ES5 = {
7993    ToPrimitive: es5,
7994    ToBoolean: function ToBoolean(value) {
7995      return !!value;
7996    },
7997    ToNumber: function ToNumber(value) {
7998      return +value; // eslint-disable-line no-implicit-coercion
7999    },
8000    ToInteger: function ToInteger(value) {
8001      var number = this.ToNumber(value);
8002
8003      if (_isNaN(number)) {
8004        return 0;
8005      }
8006
8007      if (number === 0 || !_isFinite(number)) {
8008        return number;
8009      }
8010
8011      return sign(number) * Math.floor(Math.abs(number));
8012    },
8013    ToInt32: function ToInt32(x) {
8014      return this.ToNumber(x) >> 0;
8015    },
8016    ToUint32: function ToUint32(x) {
8017      return this.ToNumber(x) >>> 0;
8018    },
8019    ToUint16: function ToUint16(value) {
8020      var number = this.ToNumber(value);
8021
8022      if (_isNaN(number) || number === 0 || !_isFinite(number)) {
8023        return 0;
8024      }
8025
8026      var posInt = sign(number) * Math.floor(Math.abs(number));
8027      return mod(posInt, 0x10000);
8028    },
8029    ToString: function ToString(value) {
8030      return $String(value);
8031    },
8032    ToObject: function ToObject(value) {
8033      this.CheckObjectCoercible(value);
8034      return $Object(value);
8035    },
8036    CheckObjectCoercible: function CheckObjectCoercible(value, optMessage) {
8037      /* jshint eqnull:true */
8038      if (value == null) {
8039        throw new $TypeError$1(optMessage || 'Cannot call method on ' + value);
8040      }
8041
8042      return value;
8043    },
8044    IsCallable: isCallable,
8045    SameValue: function SameValue(x, y) {
8046      if (x === y) {
8047        // 0 === -0, but they are not identical.
8048        if (x === 0) {
8049          return 1 / x === 1 / y;
8050        }
8051
8052        return true;
8053      }
8054
8055      return _isNaN(x) && _isNaN(y);
8056    },
8057    // https://www.ecma-international.org/ecma-262/5.1/#sec-8
8058    Type: function Type(x) {
8059      if (x === null) {
8060        return 'Null';
8061      }
8062
8063      if (typeof x === 'undefined') {
8064        return 'Undefined';
8065      }
8066
8067      if (typeof x === 'function' || typeof x === 'object') {
8068        return 'Object';
8069      }
8070
8071      if (typeof x === 'number') {
8072        return 'Number';
8073      }
8074
8075      if (typeof x === 'boolean') {
8076        return 'Boolean';
8077      }
8078
8079      if (typeof x === 'string') {
8080        return 'String';
8081      }
8082    },
8083    // https://ecma-international.org/ecma-262/6.0/#sec-property-descriptor-specification-type
8084    IsPropertyDescriptor: function IsPropertyDescriptor(Desc) {
8085      if (this.Type(Desc) !== 'Object') {
8086        return false;
8087      }
8088
8089      var allowed = {
8090        '[[Configurable]]': true,
8091        '[[Enumerable]]': true,
8092        '[[Get]]': true,
8093        '[[Set]]': true,
8094        '[[Value]]': true,
8095        '[[Writable]]': true
8096      };
8097
8098      for (var key in Desc) {
8099        // eslint-disable-line
8100        if (src(Desc, key) && !allowed[key]) {
8101          return false;
8102        }
8103      }
8104
8105      var isData = src(Desc, '[[Value]]');
8106      var IsAccessor = src(Desc, '[[Get]]') || src(Desc, '[[Set]]');
8107
8108      if (isData && IsAccessor) {
8109        throw new $TypeError$1('Property Descriptors may not be both accessor and data descriptors');
8110      }
8111
8112      return true;
8113    },
8114    // https://ecma-international.org/ecma-262/5.1/#sec-8.10.1
8115    IsAccessorDescriptor: function IsAccessorDescriptor(Desc) {
8116      if (typeof Desc === 'undefined') {
8117        return false;
8118      }
8119
8120      assertRecord(this, 'Property Descriptor', 'Desc', Desc);
8121
8122      if (!src(Desc, '[[Get]]') && !src(Desc, '[[Set]]')) {
8123        return false;
8124      }
8125
8126      return true;
8127    },
8128    // https://ecma-international.org/ecma-262/5.1/#sec-8.10.2
8129    IsDataDescriptor: function IsDataDescriptor(Desc) {
8130      if (typeof Desc === 'undefined') {
8131        return false;
8132      }
8133
8134      assertRecord(this, 'Property Descriptor', 'Desc', Desc);
8135
8136      if (!src(Desc, '[[Value]]') && !src(Desc, '[[Writable]]')) {
8137        return false;
8138      }
8139
8140      return true;
8141    },
8142    // https://ecma-international.org/ecma-262/5.1/#sec-8.10.3
8143    IsGenericDescriptor: function IsGenericDescriptor(Desc) {
8144      if (typeof Desc === 'undefined') {
8145        return false;
8146      }
8147
8148      assertRecord(this, 'Property Descriptor', 'Desc', Desc);
8149
8150      if (!this.IsAccessorDescriptor(Desc) && !this.IsDataDescriptor(Desc)) {
8151        return true;
8152      }
8153
8154      return false;
8155    },
8156    // https://ecma-international.org/ecma-262/5.1/#sec-8.10.4
8157    FromPropertyDescriptor: function FromPropertyDescriptor(Desc) {
8158      if (typeof Desc === 'undefined') {
8159        return Desc;
8160      }
8161
8162      assertRecord(this, 'Property Descriptor', 'Desc', Desc);
8163
8164      if (this.IsDataDescriptor(Desc)) {
8165        return {
8166          value: Desc['[[Value]]'],
8167          writable: !!Desc['[[Writable]]'],
8168          enumerable: !!Desc['[[Enumerable]]'],
8169          configurable: !!Desc['[[Configurable]]']
8170        };
8171      } else if (this.IsAccessorDescriptor(Desc)) {
8172        return {
8173          get: Desc['[[Get]]'],
8174          set: Desc['[[Set]]'],
8175          enumerable: !!Desc['[[Enumerable]]'],
8176          configurable: !!Desc['[[Configurable]]']
8177        };
8178      } else {
8179        throw new $TypeError$1('FromPropertyDescriptor must be called with a fully populated Property Descriptor');
8180      }
8181    },
8182    // https://ecma-international.org/ecma-262/5.1/#sec-8.10.5
8183    ToPropertyDescriptor: function ToPropertyDescriptor(Obj) {
8184      if (this.Type(Obj) !== 'Object') {
8185        throw new $TypeError$1('ToPropertyDescriptor requires an object');
8186      }
8187
8188      var desc = {};
8189
8190      if (src(Obj, 'enumerable')) {
8191        desc['[[Enumerable]]'] = this.ToBoolean(Obj.enumerable);
8192      }
8193
8194      if (src(Obj, 'configurable')) {
8195        desc['[[Configurable]]'] = this.ToBoolean(Obj.configurable);
8196      }
8197
8198      if (src(Obj, 'value')) {
8199        desc['[[Value]]'] = Obj.value;
8200      }
8201
8202      if (src(Obj, 'writable')) {
8203        desc['[[Writable]]'] = this.ToBoolean(Obj.writable);
8204      }
8205
8206      if (src(Obj, 'get')) {
8207        var getter = Obj.get;
8208
8209        if (typeof getter !== 'undefined' && !this.IsCallable(getter)) {
8210          throw new TypeError('getter must be a function');
8211        }
8212
8213        desc['[[Get]]'] = getter;
8214      }
8215
8216      if (src(Obj, 'set')) {
8217        var setter = Obj.set;
8218
8219        if (typeof setter !== 'undefined' && !this.IsCallable(setter)) {
8220          throw new $TypeError$1('setter must be a function');
8221        }
8222
8223        desc['[[Set]]'] = setter;
8224      }
8225
8226      if ((src(desc, '[[Get]]') || src(desc, '[[Set]]')) && (src(desc, '[[Value]]') || src(desc, '[[Writable]]'))) {
8227        throw new $TypeError$1('Invalid property descriptor. Cannot both specify accessors and a value or writable attribute');
8228      }
8229
8230      return desc;
8231    }
8232  };
8233  var es5$1 = ES5;
8234
8235  var replace = functionBind.call(Function.call, String.prototype.replace);
8236  /* eslint-disable no-control-regex */
8237
8238  var leftWhitespace = /^[\x09\x0A\x0B\x0C\x0D\x20\xA0\u1680\u180E\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200A\u202F\u205F\u3000\u2028\u2029\uFEFF]+/;
8239  var rightWhitespace = /[\x09\x0A\x0B\x0C\x0D\x20\xA0\u1680\u180E\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200A\u202F\u205F\u3000\u2028\u2029\uFEFF]+$/;
8240  /* eslint-enable no-control-regex */
8241
8242  var implementation$2 = function trim() {
8243    var S = es5$1.ToString(es5$1.CheckObjectCoercible(this));
8244    return replace(replace(S, leftWhitespace, ''), rightWhitespace, '');
8245  };
8246
8247  var zeroWidthSpace = "\u200B";
8248
8249  var polyfill = function getPolyfill() {
8250    if (String.prototype.trim && zeroWidthSpace.trim() === zeroWidthSpace) {
8251      return String.prototype.trim;
8252    }
8253
8254    return implementation$2;
8255  };
8256
8257  var shim = function shimStringTrim() {
8258    var polyfill$1 = polyfill();
8259    defineProperties_1(String.prototype, {
8260      trim: polyfill$1
8261    }, {
8262      trim: function testTrim() {
8263        return String.prototype.trim !== polyfill$1;
8264      }
8265    });
8266    return polyfill$1;
8267  };
8268
8269  var boundTrim = functionBind.call(Function.call, polyfill());
8270  defineProperties_1(boundTrim, {
8271    getPolyfill: polyfill,
8272    implementation: implementation$2,
8273    shim: shim
8274  });
8275  var string_prototype_trim = boundTrim;
8276
8277  var toStr$6 = Object.prototype.toString;
8278  var hasOwnProperty = Object.prototype.hasOwnProperty;
8279
8280  var forEachArray = function forEachArray(array, iterator, receiver) {
8281    for (var i = 0, len = array.length; i < len; i++) {
8282      if (hasOwnProperty.call(array, i)) {
8283        if (receiver == null) {
8284          iterator(array[i], i, array);
8285        } else {
8286          iterator.call(receiver, array[i], i, array);
8287        }
8288      }
8289    }
8290  };
8291
8292  var forEachString = function forEachString(string, iterator, receiver) {
8293    for (var i = 0, len = string.length; i < len; i++) {
8294      // no such thing as a sparse string.
8295      if (receiver == null) {
8296        iterator(string.charAt(i), i, string);
8297      } else {
8298        iterator.call(receiver, string.charAt(i), i, string);
8299      }
8300    }
8301  };
8302
8303  var forEachObject = function forEachObject(object, iterator, receiver) {
8304    for (var k in object) {
8305      if (hasOwnProperty.call(object, k)) {
8306        if (receiver == null) {
8307          iterator(object[k], k, object);
8308        } else {
8309          iterator.call(receiver, object[k], k, object);
8310        }
8311      }
8312    }
8313  };
8314
8315  var forEach = function forEach(list, iterator, thisArg) {
8316    if (!isCallable(iterator)) {
8317      throw new TypeError('iterator must be a function');
8318    }
8319
8320    var receiver;
8321
8322    if (arguments.length >= 3) {
8323      receiver = thisArg;
8324    }
8325
8326    if (toStr$6.call(list) === '[object Array]') {
8327      forEachArray(list, iterator, receiver);
8328    } else if (typeof list === 'string') {
8329      forEachString(list, iterator, receiver);
8330    } else {
8331      forEachObject(list, iterator, receiver);
8332    }
8333  };
8334
8335  var forEach_1 = forEach;
8336
8337  var isArray = function isArray(arg) {
8338    return Object.prototype.toString.call(arg) === '[object Array]';
8339  };
8340
8341  var parseHeaders = function parseHeaders(headers) {
8342    if (!headers) return {};
8343    var result = {};
8344    forEach_1(string_prototype_trim(headers).split('\n'), function (row) {
8345      var index = row.indexOf(':'),
8346          key = string_prototype_trim(row.slice(0, index)).toLowerCase(),
8347          value = string_prototype_trim(row.slice(index + 1));
8348
8349      if (typeof result[key] === 'undefined') {
8350        result[key] = value;
8351      } else if (isArray(result[key])) {
8352        result[key].push(value);
8353      } else {
8354        result[key] = [result[key], value];
8355      }
8356    });
vendor: 4,672 bytes, lines 8357-8540
8357    return result;
8358  };
8359
8360  var immutable = extend;
8361  var hasOwnProperty$1 = Object.prototype.hasOwnProperty;
8362
8363  function extend() {
8364    var target = {};
8365
8366    for (var i = 0; i < arguments.length; i++) {
8367      var source = arguments[i];
8368
8369      for (var key in source) {
8370        if (hasOwnProperty$1.call(source, key)) {
8371          target[key] = source[key];
8372        }
8373      }
8374    }
8375
8376    return target;
8377  }
8378
8379  var xhr = createXHR;
8380  createXHR.XMLHttpRequest = window$1.XMLHttpRequest || noop;
8381  createXHR.XDomainRequest = "withCredentials" in new createXHR.XMLHttpRequest() ? createXHR.XMLHttpRequest : window$1.XDomainRequest;
8382  forEachArray$1(["get", "put", "post", "patch", "head", "delete"], function (method) {
8383    createXHR[method === "delete" ? "del" : method] = function (uri, options, callback) {
8384      options = initParams(uri, options, callback);
8385      options.method = method.toUpperCase();
8386      return _createXHR(options);
8387    };
8388  });
8389
8390  function forEachArray$1(array, iterator) {
8391    for (var i = 0; i < array.length; i++) {
8392      iterator(array[i]);
8393    }
8394  }
8395
8396  function isEmpty(obj) {
8397    for (var i in obj) {
8398      if (obj.hasOwnProperty(i)) return false;
8399    }
8400
8401    return true;
8402  }
8403
8404  function initParams(uri, options, callback) {
8405    var params = uri;
8406
8407    if (isFunction_1(options)) {
8408      callback = options;
8409
8410      if (typeof uri === "string") {
8411        params = {
8412          uri: uri
8413        };
8414      }
8415    } else {
8416      params = immutable(options, {
8417        uri: uri
8418      });
8419    }
8420
8421    params.callback = callback;
8422    return params;
8423  }
8424
8425  function createXHR(uri, options, callback) {
8426    options = initParams(uri, options, callback);
8427    return _createXHR(options);
8428  }
8429
8430  function _createXHR(options) {
8431    if (typeof options.callback === "undefined") {
8432      throw new Error("callback argument missing");
8433    }
8434
8435    var called = false;
8436
8437    var callback = function cbOnce(err, response, body) {
8438      if (!called) {
8439        called = true;
8440        options.callback(err, response, body);
8441      }
8442    };
8443
8444    function readystatechange() {
8445      if (xhr.readyState === 4) {
8446        setTimeout(loadFunc, 0);
8447      }
8448    }
8449
8450    function getBody() {
8451      // Chrome with requestType=blob throws errors arround when even testing access to responseText
8452      var body = undefined;
8453
8454      if (xhr.response) {
8455        body = xhr.response;
8456      } else {
8457        body = xhr.responseText || getXml(xhr);
8458      }
8459
8460      if (isJson) {
8461        try {
8462          body = JSON.parse(body);
8463        } catch (e) {}
8464      }
8465
8466      return body;
8467    }
8468
8469    function errorFunc(evt) {
8470      clearTimeout(timeoutTimer);
8471
8472      if (!(evt instanceof Error)) {
8473        evt = new Error("" + (evt || "Unknown XMLHttpRequest Error"));
8474      }
8475
8476      evt.statusCode = 0;
8477      return callback(evt, failureResponse);
8478    } // will load the data & process the response in a special response object
8479
8480
8481    function loadFunc() {
8482      if (aborted) return;
8483      var status;
8484      clearTimeout(timeoutTimer);
8485
8486      if (options.useXDR && xhr.status === undefined) {
8487        //IE8 CORS GET successful response doesn't have a status field, but body is fine
8488        status = 200;
8489      } else {
8490        status = xhr.status === 1223 ? 204 : xhr.status;
8491      }
8492
8493      var response = failureResponse;
8494      var err = null;
8495
8496      if (status !== 0) {
8497        response = {
8498          body: getBody(),
8499          statusCode: status,
8500          method: method,
8501          headers: {},
8502          url: uri,
8503          rawRequest: xhr
8504        };
8505
8506        if (xhr.getAllResponseHeaders) {
8507          //remember xhr can in fact be XDR for CORS in IE
8508          response.headers = parseHeaders(xhr.getAllResponseHeaders());
8509        }
8510      } else {
8511        err = new Error("Internal XMLHttpRequest Error");
8512      }
8513
8514      return callback(err, response, response.body);
8515    }
8516
8517    var xhr = options.xhr || null;
8518
8519    if (!xhr) {
8520      if (options.cors || options.useXDR) {
8521        xhr = new createXHR.XDomainRequest();
8522      } else {
8523        xhr = new createXHR.XMLHttpRequest();
8524      }
8525    }
8526
8527    var key;
8528    var aborted;
8529    var uri = xhr.url = options.uri || options.url;
8530    var method = xhr.method = options.method || "GET";
8531    var body = options.body || options.data;
8532    var headers = xhr.headers = options.headers || {};
8533    var sync = !!options.sync;
8534    var isJson = false;
8535    var timeoutTimer;
8536    var failureResponse = {
8537      body: undefined,
8538      headers: {},
8539      statusCode: 0,
8540      method: method,
vendor: 25,026 bytes, lines 8541-9396
8541      url: uri,
8542      rawRequest: xhr
8543    };
8544
8545    if ("json" in options && options.json !== false) {
8546      isJson = true;
8547      headers["accept"] || headers["Accept"] || (headers["Accept"] = "application/json"); //Don't override existing accept header declared by user
8548
8549      if (method !== "GET" && method !== "HEAD") {
8550        headers["content-type"] || headers["Content-Type"] || (headers["Content-Type"] = "application/json"); //Don't override existing accept header declared by user
8551
8552        body = JSON.stringify(options.json === true ? body : options.json);
8553      }
8554    }
8555
8556    xhr.onreadystatechange = readystatechange;
8557    xhr.onload = loadFunc;
8558    xhr.onerror = errorFunc; // IE9 must have onprogress be set to a unique function.
8559
8560    xhr.onprogress = function () {// IE must die
8561    };
8562
8563    xhr.onabort = function () {
8564      aborted = true;
8565    };
8566
8567    xhr.ontimeout = errorFunc;
8568    xhr.open(method, uri, !sync, options.username, options.password); //has to be after open
8569
8570    if (!sync) {
8571      xhr.withCredentials = !!options.withCredentials;
8572    } // Cannot set timeout with sync request
8573    // not setting timeout on the xhr object, because of old webkits etc. not handling that correctly
8574    // both npm's request and jquery 1.x use this kind of timeout, so this is being consistent
8575
8576
8577    if (!sync && options.timeout > 0) {
8578      timeoutTimer = setTimeout(function () {
8579        if (aborted) return;
8580        aborted = true; //IE9 may still call readystatechange
8581
8582        xhr.abort("timeout");
8583        var e = new Error("XMLHttpRequest timeout");
8584        e.code = "ETIMEDOUT";
8585        errorFunc(e);
8586      }, options.timeout);
8587    }
8588
8589    if (xhr.setRequestHeader) {
8590      for (key in headers) {
8591        if (headers.hasOwnProperty(key)) {
8592          xhr.setRequestHeader(key, headers[key]);
8593        }
8594      }
8595    } else if (options.headers && !isEmpty(options.headers)) {
8596      throw new Error("Headers cannot be set on an XDomainRequest object");
8597    }
8598
8599    if ("responseType" in options) {
8600      xhr.responseType = options.responseType;
8601    }
8602
8603    if ("beforeSend" in options && typeof options.beforeSend === "function") {
8604      options.beforeSend(xhr);
8605    } // Microsoft Edge browser sends "undefined" when send is called with undefined value.
8606    // XMLHttpRequest spec says to pass null as body to indicate no body
8607    // See https://github.com/naugtur/xhr/issues/100.
8608
8609
8610    xhr.send(body || null);
8611    return xhr;
8612  }
8613
8614  function getXml(xhr) {
8615    if (xhr.responseType === "document") {
8616      return xhr.responseXML;
8617    }
8618
8619    var firefoxBugTakenEffect = xhr.responseXML && xhr.responseXML.documentElement.nodeName === "parsererror";
8620
8621    if (xhr.responseType === "" && !firefoxBugTakenEffect) {
8622      return xhr.responseXML;
8623    }
8624
8625    return null;
8626  }
8627
8628  function noop() {}
8629
8630  /**
8631   * Takes a webvtt file contents and parses it into cues
8632   *
8633   * @param {string} srcContent
8634   *        webVTT file contents
8635   *
8636   * @param {TextTrack} track
8637   *        TextTrack to add cues to. Cues come from the srcContent.
8638   *
8639   * @private
8640   */
8641
8642  var parseCues = function parseCues(srcContent, track) {
8643    var parser = new window$1.WebVTT.Parser(window$1, window$1.vttjs, window$1.WebVTT.StringDecoder());
8644    var errors = [];
8645
8646    parser.oncue = function (cue) {
8647      track.addCue(cue);
8648    };
8649
8650    parser.onparsingerror = function (error) {
8651      errors.push(error);
8652    };
8653
8654    parser.onflush = function () {
8655      track.trigger({
8656        type: 'loadeddata',
8657        target: track
8658      });
8659    };
8660
8661    parser.parse(srcContent);
8662
8663    if (errors.length > 0) {
8664      if (window$1.console && window$1.console.groupCollapsed) {
8665        window$1.console.groupCollapsed("Text Track parsing errors for " + track.src);
8666      }
8667
8668      errors.forEach(function (error) {
8669        return log.error(error);
8670      });
8671
8672      if (window$1.console && window$1.console.groupEnd) {
8673        window$1.console.groupEnd();
8674      }
8675    }
8676
8677    parser.flush();
8678  };
8679  /**
8680   * Load a `TextTrack` from a specified url.
8681   *
8682   * @param {string} src
8683   *        Url to load track from.
8684   *
8685   * @param {TextTrack} track
8686   *        Track to add cues to. Comes from the content at the end of `url`.
8687   *
8688   * @private
8689   */
8690
8691
8692  var loadTrack = function loadTrack(src, track) {
8693    var opts = {
8694      uri: src
8695    };
8696    var crossOrigin = isCrossOrigin(src);
8697
8698    if (crossOrigin) {
8699      opts.cors = crossOrigin;
8700    }
8701
8702    xhr(opts, bind(this, function (err, response, responseBody) {
8703      if (err) {
8704        return log.error(err, response);
8705      }
8706
8707      track.loaded_ = true; // Make sure that vttjs has loaded, otherwise, wait till it finished loading
8708      // NOTE: this is only used for the alt/video.novtt.js build
8709
8710      if (typeof window$1.WebVTT !== 'function') {
8711        if (track.tech_) {
8712          // to prevent use before define eslint error, we define loadHandler
8713          // as a let here
8714          track.tech_.any(['vttjsloaded', 'vttjserror'], function (event) {
8715            if (event.type === 'vttjserror') {
8716              log.error("vttjs failed to load, stopping trying to process " + track.src);
8717              return;
8718            }
8719
8720            return parseCues(responseBody, track);
8721          });
8722        }
8723      } else {
8724        parseCues(responseBody, track);
8725      }
8726    }));
8727  };
8728  /**
8729   * A representation of a single `TextTrack`.
8730   *
8731   * @see [Spec]{@link https://html.spec.whatwg.org/multipage/embedded-content.html#texttrack}
8732   * @extends Track
8733   */
8734
8735
8736  var TextTrack =
8737  /*#__PURE__*/
8738  function (_Track) {
8739    _inheritsLoose(TextTrack, _Track);
8740
8741    /**
8742     * Create an instance of this class.
8743     *
8744     * @param {Object} options={}
8745     *        Object of option names and values
8746     *
8747     * @param {Tech} options.tech
8748     *        A reference to the tech that owns this TextTrack.
8749     *
8750     * @param {TextTrack~Kind} [options.kind='subtitles']
8751     *        A valid text track kind.
8752     *
8753     * @param {TextTrack~Mode} [options.mode='disabled']
8754     *        A valid text track mode.
8755     *
8756     * @param {string} [options.id='vjs_track_' + Guid.newGUID()]
8757     *        A unique id for this TextTrack.
8758     *
8759     * @param {string} [options.label='']
8760     *        The menu label for this track.
8761     *
8762     * @param {string} [options.language='']
8763     *        A valid two character language code.
8764     *
8765     * @param {string} [options.srclang='']
8766     *        A valid two character language code. An alternative, but deprioritized
8767     *        version of `options.language`
8768     *
8769     * @param {string} [options.src]
8770     *        A url to TextTrack cues.
8771     *
8772     * @param {boolean} [options.default]
8773     *        If this track should default to on or off.
8774     */
8775    function TextTrack(options) {
8776      var _this;
8777
8778      if (options === void 0) {
8779        options = {};
8780      }
8781
8782      if (!options.tech) {
8783        throw new Error('A tech was not provided.');
8784      }
8785
8786      var settings = mergeOptions(options, {
8787        kind: TextTrackKind[options.kind] || 'subtitles',
8788        language: options.language || options.srclang || ''
8789      });
8790      var mode = TextTrackMode[settings.mode] || 'disabled';
8791      var default_ = settings["default"];
8792
8793      if (settings.kind === 'metadata' || settings.kind === 'chapters') {
8794        mode = 'hidden';
8795      }
8796
8797      _this = _Track.call(this, settings) || this;
8798      _this.tech_ = settings.tech;
8799      _this.cues_ = [];
8800      _this.activeCues_ = [];
8801      var cues = new TextTrackCueList(_this.cues_);
8802      var activeCues = new TextTrackCueList(_this.activeCues_);
8803      var changed = false;
8804      var timeupdateHandler = bind(_assertThisInitialized(_this), function () {
8805        // Accessing this.activeCues for the side-effects of updating itself
8806        // due to its nature as a getter function. Do not remove or cues will
8807        // stop updating!
8808        // Use the setter to prevent deletion from uglify (pure_getters rule)
8809        this.activeCues = this.activeCues;
8810
8811        if (changed) {
8812          this.trigger('cuechange');
8813          changed = false;
8814        }
8815      });
8816
8817      if (mode !== 'disabled') {
8818        _this.tech_.ready(function () {
8819          _this.tech_.on('timeupdate', timeupdateHandler);
8820        }, true);
8821      }
8822
8823      Object.defineProperties(_assertThisInitialized(_this), {
8824        /**
8825         * @memberof TextTrack
8826         * @member {boolean} default
8827         *         If this track was set to be on or off by default. Cannot be changed after
8828         *         creation.
8829         * @instance
8830         *
8831         * @readonly
8832         */
8833        "default": {
8834          get: function get() {
8835            return default_;
8836          },
8837          set: function set() {}
8838        },
8839
8840        /**
8841         * @memberof TextTrack
8842         * @member {string} mode
8843         *         Set the mode of this TextTrack to a valid {@link TextTrack~Mode}. Will
8844         *         not be set if setting to an invalid mode.
8845         * @instance
8846         *
8847         * @fires TextTrack#modechange
8848         */
8849        mode: {
8850          get: function get() {
8851            return mode;
8852          },
8853          set: function set(newMode) {
8854            var _this2 = this;
8855
8856            if (!TextTrackMode[newMode]) {
8857              return;
8858            }
8859
8860            mode = newMode;
8861
8862            if (mode !== 'disabled') {
8863              this.tech_.ready(function () {
8864                _this2.tech_.on('timeupdate', timeupdateHandler);
8865              }, true);
8866            } else {
8867              this.tech_.off('timeupdate', timeupdateHandler);
8868            }
8869            /**
8870             * An event that fires when mode changes on this track. This allows
8871             * the TextTrackList that holds this track to act accordingly.
8872             *
8873             * > Note: This is not part of the spec!
8874             *
8875             * @event TextTrack#modechange
8876             * @type {EventTarget~Event}
8877             */
8878
8879
8880            this.trigger('modechange');
8881          }
8882        },
8883
8884        /**
8885         * @memberof TextTrack
8886         * @member {TextTrackCueList} cues
8887         *         The text track cue list for this TextTrack.
8888         * @instance
8889         */
8890        cues: {
8891          get: function get() {
8892            if (!this.loaded_) {
8893              return null;
8894            }
8895
8896            return cues;
8897          },
8898          set: function set() {}
8899        },
8900
8901        /**
8902         * @memberof TextTrack
8903         * @member {TextTrackCueList} activeCues
8904         *         The list text track cues that are currently active for this TextTrack.
8905         * @instance
8906         */
8907        activeCues: {
8908          get: function get() {
8909            if (!this.loaded_) {
8910              return null;
8911            } // nothing to do
8912
8913
8914            if (this.cues.length === 0) {
8915              return activeCues;
8916            }
8917
8918            var ct = this.tech_.currentTime();
8919            var active = [];
8920
8921            for (var i = 0, l = this.cues.length; i < l; i++) {
8922              var cue = this.cues[i];
8923
8924              if (cue.startTime <= ct && cue.endTime >= ct) {
8925                active.push(cue);
8926              } else if (cue.startTime === cue.endTime && cue.startTime <= ct && cue.startTime + 0.5 >= ct) {
8927                active.push(cue);
8928              }
8929            }
8930
8931            changed = false;
8932
8933            if (active.length !== this.activeCues_.length) {
8934              changed = true;
8935            } else {
8936              for (var _i = 0; _i < active.length; _i++) {
8937                if (this.activeCues_.indexOf(active[_i]) === -1) {
8938                  changed = true;
8939                }
8940              }
8941            }
8942
8943            this.activeCues_ = active;
8944            activeCues.setCues_(this.activeCues_);
8945            return activeCues;
8946          },
8947          // /!\ Keep this setter empty (see the timeupdate handler above)
8948          set: function set() {}
8949        }
8950      });
8951
8952      if (settings.src) {
8953        _this.src = settings.src;
8954        loadTrack(settings.src, _assertThisInitialized(_this));
8955      } else {
8956        _this.loaded_ = true;
8957      }
8958
8959      return _this;
8960    }
8961    /**
8962     * Add a cue to the internal list of cues.
8963     *
8964     * @param {TextTrack~Cue} cue
8965     *        The cue to add to our internal list
8966     */
8967
8968
8969    var _proto = TextTrack.prototype;
8970
8971    _proto.addCue = function addCue(originalCue) {
8972      var cue = originalCue;
8973
8974      if (window$1.vttjs && !(originalCue instanceof window$1.vttjs.VTTCue)) {
8975        cue = new window$1.vttjs.VTTCue(originalCue.startTime, originalCue.endTime, originalCue.text);
8976
8977        for (var prop in originalCue) {
8978          if (!(prop in cue)) {
8979            cue[prop] = originalCue[prop];
8980          }
8981        } // make sure that `id` is copied over
8982
8983
8984        cue.id = originalCue.id;
8985        cue.originalCue_ = originalCue;
8986      }
8987
8988      var tracks = this.tech_.textTracks();
8989
8990      for (var i = 0; i < tracks.length; i++) {
8991        if (tracks[i] !== this) {
8992          tracks[i].removeCue(cue);
8993        }
8994      }
8995
8996      this.cues_.push(cue);
8997      this.cues.setCues_(this.cues_);
8998    }
8999    /**
9000     * Remove a cue from our internal list
9001     *
9002     * @param {TextTrack~Cue} removeCue
9003     *        The cue to remove from our internal list
9004     */
9005    ;
9006
9007    _proto.removeCue = function removeCue(_removeCue) {
9008      var i = this.cues_.length;
9009
9010      while (i--) {
9011        var cue = this.cues_[i];
9012
9013        if (cue === _removeCue || cue.originalCue_ && cue.originalCue_ === _removeCue) {
9014          this.cues_.splice(i, 1);
9015          this.cues.setCues_(this.cues_);
9016          break;
9017        }
9018      }
9019    };
9020
9021    return TextTrack;
9022  }(Track);
9023  /**
9024   * cuechange - One or more cues in the track have become active or stopped being active.
9025   */
9026
9027
9028  TextTrack.prototype.allowedEvents_ = {
9029    cuechange: 'cuechange'
9030  };
9031
9032  /**
9033   * A representation of a single `AudioTrack`. If it is part of an {@link AudioTrackList}
9034   * only one `AudioTrack` in the list will be enabled at a time.
9035   *
9036   * @see [Spec]{@link https://html.spec.whatwg.org/multipage/embedded-content.html#audiotrack}
9037   * @extends Track
9038   */
9039
9040  var AudioTrack =
9041  /*#__PURE__*/
9042  function (_Track) {
9043    _inheritsLoose(AudioTrack, _Track);
9044
9045    /**
9046     * Create an instance of this class.
9047     *
9048     * @param {Object} [options={}]
9049     *        Object of option names and values
9050     *
9051     * @param {AudioTrack~Kind} [options.kind='']
9052     *        A valid audio track kind
9053     *
9054     * @param {string} [options.id='vjs_track_' + Guid.newGUID()]
9055     *        A unique id for this AudioTrack.
9056     *
9057     * @param {string} [options.label='']
9058     *        The menu label for this track.
9059     *
9060     * @param {string} [options.language='']
9061     *        A valid two character language code.
9062     *
9063     * @param {boolean} [options.enabled]
9064     *        If this track is the one that is currently playing. If this track is part of
9065     *        an {@link AudioTrackList}, only one {@link AudioTrack} will be enabled.
9066     */
9067    function AudioTrack(options) {
9068      var _this;
9069
9070      if (options === void 0) {
9071        options = {};
9072      }
9073
9074      var settings = mergeOptions(options, {
9075        kind: AudioTrackKind[options.kind] || ''
9076      });
9077      _this = _Track.call(this, settings) || this;
9078      var enabled = false;
9079      /**
9080       * @memberof AudioTrack
9081       * @member {boolean} enabled
9082       *         If this `AudioTrack` is enabled or not. When setting this will
9083       *         fire {@link AudioTrack#enabledchange} if the state of enabled is changed.
9084       * @instance
9085       *
9086       * @fires VideoTrack#selectedchange
9087       */
9088
9089      Object.defineProperty(_assertThisInitialized(_this), 'enabled', {
9090        get: function get() {
9091          return enabled;
9092        },
9093        set: function set(newEnabled) {
9094          // an invalid or unchanged value
9095          if (typeof newEnabled !== 'boolean' || newEnabled === enabled) {
9096            return;
9097          }
9098
9099          enabled = newEnabled;
9100          /**
9101           * An event that fires when enabled changes on this track. This allows
9102           * the AudioTrackList that holds this track to act accordingly.
9103           *
9104           * > Note: This is not part of the spec! Native tracks will do
9105           *         this internally without an event.
9106           *
9107           * @event AudioTrack#enabledchange
9108           * @type {EventTarget~Event}
9109           */
9110
9111          this.trigger('enabledchange');
9112        }
9113      }); // if the user sets this track to selected then
9114      // set selected to that true value otherwise
9115      // we keep it false
9116
9117      if (settings.enabled) {
9118        _this.enabled = settings.enabled;
9119      }
9120
9121      _this.loaded_ = true;
9122      return _this;
9123    }
9124
9125    return AudioTrack;
9126  }(Track);
9127
9128  /**
9129   * A representation of a single `VideoTrack`.
9130   *
9131   * @see [Spec]{@link https://html.spec.whatwg.org/multipage/embedded-content.html#videotrack}
9132   * @extends Track
9133   */
9134
9135  var VideoTrack =
9136  /*#__PURE__*/
9137  function (_Track) {
9138    _inheritsLoose(VideoTrack, _Track);
9139
9140    /**
9141     * Create an instance of this class.
9142     *
9143     * @param {Object} [options={}]
9144     *        Object of option names and values
9145     *
9146     * @param {string} [options.kind='']
9147     *        A valid {@link VideoTrack~Kind}
9148     *
9149     * @param {string} [options.id='vjs_track_' + Guid.newGUID()]
9150     *        A unique id for this AudioTrack.
9151     *
9152     * @param {string} [options.label='']
9153     *        The menu label for this track.
9154     *
9155     * @param {string} [options.language='']
9156     *        A valid two character language code.
9157     *
9158     * @param {boolean} [options.selected]
9159     *        If this track is the one that is currently playing.
9160     */
9161    function VideoTrack(options) {
9162      var _this;
9163
9164      if (options === void 0) {
9165        options = {};
9166      }
9167
9168      var settings = mergeOptions(options, {
9169        kind: VideoTrackKind[options.kind] || ''
9170      });
9171      _this = _Track.call(this, settings) || this;
9172      var selected = false;
9173      /**
9174       * @memberof VideoTrack
9175       * @member {boolean} selected
9176       *         If this `VideoTrack` is selected or not. When setting this will
9177       *         fire {@link VideoTrack#selectedchange} if the state of selected changed.
9178       * @instance
9179       *
9180       * @fires VideoTrack#selectedchange
9181       */
9182
9183      Object.defineProperty(_assertThisInitialized(_this), 'selected', {
9184        get: function get() {
9185          return selected;
9186        },
9187        set: function set(newSelected) {
9188          // an invalid or unchanged value
9189          if (typeof newSelected !== 'boolean' || newSelected === selected) {
9190            return;
9191          }
9192
9193          selected = newSelected;
9194          /**
9195           * An event that fires when selected changes on this track. This allows
9196           * the VideoTrackList that holds this track to act accordingly.
9197           *
9198           * > Note: This is not part of the spec! Native tracks will do
9199           *         this internally without an event.
9200           *
9201           * @event VideoTrack#selectedchange
9202           * @type {EventTarget~Event}
9203           */
9204
9205          this.trigger('selectedchange');
9206        }
9207      }); // if the user sets this track to selected then
9208      // set selected to that true value otherwise
9209      // we keep it false
9210
9211      if (settings.selected) {
9212        _this.selected = settings.selected;
9213      }
9214
9215      return _this;
9216    }
9217
9218    return VideoTrack;
9219  }(Track);
9220
9221  /**
9222   * @memberof HTMLTrackElement
9223   * @typedef {HTMLTrackElement~ReadyState}
9224   * @enum {number}
9225   */
9226
9227  var NONE = 0;
9228  var LOADING = 1;
9229  var LOADED = 2;
9230  var ERROR = 3;
9231  /**
9232   * A single track represented in the DOM.
9233   *
9234   * @see [Spec]{@link https://html.spec.whatwg.org/multipage/embedded-content.html#htmltrackelement}
9235   * @extends EventTarget
9236   */
9237
9238  var HTMLTrackElement =
9239  /*#__PURE__*/
9240  function (_EventTarget) {
9241    _inheritsLoose(HTMLTrackElement, _EventTarget);
9242
9243    /**
9244     * Create an instance of this class.
9245     *
9246     * @param {Object} options={}
9247     *        Object of option names and values
9248     *
9249     * @param {Tech} options.tech
9250     *        A reference to the tech that owns this HTMLTrackElement.
9251     *
9252     * @param {TextTrack~Kind} [options.kind='subtitles']
9253     *        A valid text track kind.
9254     *
9255     * @param {TextTrack~Mode} [options.mode='disabled']
9256     *        A valid text track mode.
9257     *
9258     * @param {string} [options.id='vjs_track_' + Guid.newGUID()]
9259     *        A unique id for this TextTrack.
9260     *
9261     * @param {string} [options.label='']
9262     *        The menu label for this track.
9263     *
9264     * @param {string} [options.language='']
9265     *        A valid two character language code.
9266     *
9267     * @param {string} [options.srclang='']
9268     *        A valid two character language code. An alternative, but deprioritized
9269     *        vesion of `options.language`
9270     *
9271     * @param {string} [options.src]
9272     *        A url to TextTrack cues.
9273     *
9274     * @param {boolean} [options.default]
9275     *        If this track should default to on or off.
9276     */
9277    function HTMLTrackElement(options) {
9278      var _this;
9279
9280      if (options === void 0) {
9281        options = {};
9282      }
9283
9284      _this = _EventTarget.call(this) || this;
9285      var readyState;
9286      var track = new TextTrack(options);
9287      _this.kind = track.kind;
9288      _this.src = track.src;
9289      _this.srclang = track.language;
9290      _this.label = track.label;
9291      _this["default"] = track["default"];
9292      Object.defineProperties(_assertThisInitialized(_this), {
9293        /**
9294         * @memberof HTMLTrackElement
9295         * @member {HTMLTrackElement~ReadyState} readyState
9296         *         The current ready state of the track element.
9297         * @instance
9298         */
9299        readyState: {
9300          get: function get() {
9301            return readyState;
9302          }
9303        },
9304
9305        /**
9306         * @memberof HTMLTrackElement
9307         * @member {TextTrack} track
9308         *         The underlying TextTrack object.
9309         * @instance
9310         *
9311         */
9312        track: {
9313          get: function get() {
9314            return track;
9315          }
9316        }
9317      });
9318      readyState = NONE;
9319      /**
9320       * @listens TextTrack#loadeddata
9321       * @fires HTMLTrackElement#load
9322       */
9323
9324      track.addEventListener('loadeddata', function () {
9325        readyState = LOADED;
9326
9327        _this.trigger({
9328          type: 'load',
9329          target: _assertThisInitialized(_this)
9330        });
9331      });
9332      return _this;
9333    }
9334
9335    return HTMLTrackElement;
9336  }(EventTarget);
9337
9338  HTMLTrackElement.prototype.allowedEvents_ = {
9339    load: 'load'
9340  };
9341  HTMLTrackElement.NONE = NONE;
9342  HTMLTrackElement.LOADING = LOADING;
9343  HTMLTrackElement.LOADED = LOADED;
9344  HTMLTrackElement.ERROR = ERROR;
9345
9346  /*
9347   * This file contains all track properties that are used in
9348   * player.js, tech.js, html5.js and possibly other techs in the future.
9349   */
9350
9351  var NORMAL = {
9352    audio: {
9353      ListClass: AudioTrackList,
9354      TrackClass: AudioTrack,
9355      capitalName: 'Audio'
9356    },
9357    video: {
9358      ListClass: VideoTrackList,
9359      TrackClass: VideoTrack,
9360      capitalName: 'Video'
9361    },
9362    text: {
9363      ListClass: TextTrackList,
9364      TrackClass: TextTrack,
9365      capitalName: 'Text'
9366    }
9367  };
9368  Object.keys(NORMAL).forEach(function (type) {
9369    NORMAL[type].getterName = type + "Tracks";
9370    NORMAL[type].privateName = type + "Tracks_";
9371  });
9372  var REMOTE = {
9373    remoteText: {
9374      ListClass: TextTrackList,
9375      TrackClass: TextTrack,
9376      capitalName: 'RemoteText',
9377      getterName: 'remoteTextTracks',
9378      privateName: 'remoteTextTracks_'
9379    },
9380    remoteTextEl: {
9381      ListClass: HtmlTrackElementList,
9382      TrackClass: HTMLTrackElement,
9383      capitalName: 'RemoteTextTrackEls',
9384      getterName: 'remoteTextTrackEls',
9385      privateName: 'remoteTextTrackEls_'
9386    }
9387  };
9388  var ALL = mergeOptions(NORMAL, REMOTE);
9389  REMOTE.names = Object.keys(REMOTE);
9390  NORMAL.names = Object.keys(NORMAL);
9391  ALL.names = [].concat(REMOTE.names).concat(NORMAL.names);
9392
9393  /**
9394   * Copyright 2013 vtt.js Contributors
9395   *
9396   * Licensed under the Apache License, Version 2.0 (the "License");
vendor: 12,911 bytes, lines 9397-9825
9397   * you may not use this file except in compliance with the License.
9398   * You may obtain a copy of the License at
9399   *
9400   *   http://www.apache.org/licenses/LICENSE-2.0
9401   *
9402   * Unless required by applicable law or agreed to in writing, software
9403   * distributed under the License is distributed on an "AS IS" BASIS,
9404   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
9405   * See the License for the specific language governing permissions and
9406   * limitations under the License.
9407   */
9408
9409  /* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
9410
9411  /* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */
9412  var _objCreate = Object.create || function () {
9413    function F() {}
9414
9415    return function (o) {
9416      if (arguments.length !== 1) {
9417        throw new Error('Object.create shim only accepts one parameter.');
9418      }
9419
9420      F.prototype = o;
9421      return new F();
9422    };
9423  }(); // Creates a new ParserError object from an errorData object. The errorData
9424  // object should have default code and message properties. The default message
9425  // property can be overriden by passing in a message parameter.
9426  // See ParsingError.Errors below for acceptable errors.
9427
9428
9429  function ParsingError(errorData, message) {
9430    this.name = "ParsingError";
9431    this.code = errorData.code;
9432    this.message = message || errorData.message;
9433  }
9434
9435  ParsingError.prototype = _objCreate(Error.prototype);
9436  ParsingError.prototype.constructor = ParsingError; // ParsingError metadata for acceptable ParsingErrors.
9437
9438  ParsingError.Errors = {
9439    BadSignature: {
9440      code: 0,
9441      message: "Malformed WebVTT signature."
9442    },
9443    BadTimeStamp: {
9444      code: 1,
9445      message: "Malformed time stamp."
9446    }
9447  }; // Try to parse input as a time stamp.
9448
9449  function parseTimeStamp(input) {
9450    function computeSeconds(h, m, s, f) {
9451      return (h | 0) * 3600 + (m | 0) * 60 + (s | 0) + (f | 0) / 1000;
9452    }
9453
9454    var m = input.match(/^(\d+):(\d{2})(:\d{2})?\.(\d{3})/);
9455
9456    if (!m) {
9457      return null;
9458    }
9459
9460    if (m[3]) {
9461      // Timestamp takes the form of [hours]:[minutes]:[seconds].[milliseconds]
9462      return computeSeconds(m[1], m[2], m[3].replace(":", ""), m[4]);
9463    } else if (m[1] > 59) {
9464      // Timestamp takes the form of [hours]:[minutes].[milliseconds]
9465      // First position is hours as it's over 59.
9466      return computeSeconds(m[1], m[2], 0, m[4]);
9467    } else {
9468      // Timestamp takes the form of [minutes]:[seconds].[milliseconds]
9469      return computeSeconds(0, m[1], m[2], m[4]);
9470    }
9471  } // A settings object holds key/value pairs and will ignore anything but the first
9472  // assignment to a specific key.
9473
9474
9475  function Settings() {
9476    this.values = _objCreate(null);
9477  }
9478
9479  Settings.prototype = {
9480    // Only accept the first assignment to any key.
9481    set: function set(k, v) {
9482      if (!this.get(k) && v !== "") {
9483        this.values[k] = v;
9484      }
9485    },
9486    // Return the value for a key, or a default value.
9487    // If 'defaultKey' is passed then 'dflt' is assumed to be an object with
9488    // a number of possible default values as properties where 'defaultKey' is
9489    // the key of the property that will be chosen; otherwise it's assumed to be
9490    // a single value.
9491    get: function get(k, dflt, defaultKey) {
9492      if (defaultKey) {
9493        return this.has(k) ? this.values[k] : dflt[defaultKey];
9494      }
9495
9496      return this.has(k) ? this.values[k] : dflt;
9497    },
9498    // Check whether we have a value for a key.
9499    has: function has(k) {
9500      return k in this.values;
9501    },
9502    // Accept a setting if its one of the given alternatives.
9503    alt: function alt(k, v, a) {
9504      for (var n = 0; n < a.length; ++n) {
9505        if (v === a[n]) {
9506          this.set(k, v);
9507          break;
9508        }
9509      }
9510    },
9511    // Accept a setting if its a valid (signed) integer.
9512    integer: function integer(k, v) {
9513      if (/^-?\d+$/.test(v)) {
9514        // integer
9515        this.set(k, parseInt(v, 10));
9516      }
9517    },
9518    // Accept a setting if its a valid percentage.
9519    percent: function percent(k, v) {
9520      var m;
9521
9522      if (m = v.match(/^([\d]{1,3})(\.[\d]*)?%$/)) {
9523        v = parseFloat(v);
9524
9525        if (v >= 0 && v <= 100) {
9526          this.set(k, v);
9527          return true;
9528        }
9529      }
9530
9531      return false;
9532    }
9533  }; // Helper function to parse input into groups separated by 'groupDelim', and
9534  // interprete each group as a key/value pair separated by 'keyValueDelim'.
9535
9536  function parseOptions(input, callback, keyValueDelim, groupDelim) {
9537    var groups = groupDelim ? input.split(groupDelim) : [input];
9538
9539    for (var i in groups) {
9540      if (typeof groups[i] !== "string") {
9541        continue;
9542      }
9543
9544      var kv = groups[i].split(keyValueDelim);
9545
9546      if (kv.length !== 2) {
9547        continue;
9548      }
9549
9550      var k = kv[0];
9551      var v = kv[1];
9552      callback(k, v);
9553    }
9554  }
9555
9556  function parseCue(input, cue, regionList) {
9557    // Remember the original input if we need to throw an error.
9558    var oInput = input; // 4.1 WebVTT timestamp
9559
9560    function consumeTimeStamp() {
9561      var ts = parseTimeStamp(input);
9562
9563      if (ts === null) {
9564        throw new ParsingError(ParsingError.Errors.BadTimeStamp, "Malformed timestamp: " + oInput);
9565      } // Remove time stamp from input.
9566
9567
9568      input = input.replace(/^[^\sa-zA-Z-]+/, "");
9569      return ts;
9570    } // 4.4.2 WebVTT cue settings
9571
9572
9573    function consumeCueSettings(input, cue) {
9574      var settings = new Settings();
9575      parseOptions(input, function (k, v) {
9576        switch (k) {
9577          case "region":
9578            // Find the last region we parsed with the same region id.
9579            for (var i = regionList.length - 1; i >= 0; i--) {
9580              if (regionList[i].id === v) {
9581                settings.set(k, regionList[i].region);
9582                break;
9583              }
9584            }
9585
9586            break;
9587
9588          case "vertical":
9589            settings.alt(k, v, ["rl", "lr"]);
9590            break;
9591
9592          case "line":
9593            var vals = v.split(","),
9594                vals0 = vals[0];
9595            settings.integer(k, vals0);
9596            settings.percent(k, vals0) ? settings.set("snapToLines", false) : null;
9597            settings.alt(k, vals0, ["auto"]);
9598
9599            if (vals.length === 2) {
9600              settings.alt("lineAlign", vals[1], ["start", "middle", "end"]);
9601            }
9602
9603            break;
9604
9605          case "position":
9606            vals = v.split(",");
9607            settings.percent(k, vals[0]);
9608
9609            if (vals.length === 2) {
9610              settings.alt("positionAlign", vals[1], ["start", "middle", "end"]);
9611            }
9612
9613            break;
9614
9615          case "size":
9616            settings.percent(k, v);
9617            break;
9618
9619          case "align":
9620            settings.alt(k, v, ["start", "middle", "end", "left", "right"]);
9621            break;
9622        }
9623      }, /:/, /\s/); // Apply default values for any missing fields.
9624
9625      cue.region = settings.get("region", null);
9626      cue.vertical = settings.get("vertical", "");
9627      cue.line = settings.get("line", "auto");
9628      cue.lineAlign = settings.get("lineAlign", "start");
9629      cue.snapToLines = settings.get("snapToLines", true);
9630      cue.size = settings.get("size", 100);
9631      cue.align = settings.get("align", "middle");
9632      cue.position = settings.get("position", {
9633        start: 0,
9634        left: 0,
9635        middle: 50,
9636        end: 100,
9637        right: 100
9638      }, cue.align);
9639      cue.positionAlign = settings.get("positionAlign", {
9640        start: "start",
9641        left: "start",
9642        middle: "middle",
9643        end: "end",
9644        right: "end"
9645      }, cue.align);
9646    }
9647
9648    function skipWhitespace() {
9649      input = input.replace(/^\s+/, "");
9650    } // 4.1 WebVTT cue timings.
9651
9652
9653    skipWhitespace();
9654    cue.startTime = consumeTimeStamp(); // (1) collect cue start time
9655
9656    skipWhitespace();
9657
9658    if (input.substr(0, 3) !== "-->") {
9659      // (3) next characters must match "-->"
9660      throw new ParsingError(ParsingError.Errors.BadTimeStamp, "Malformed time stamp (time stamps must be separated by '-->'): " + oInput);
9661    }
9662
9663    input = input.substr(3);
9664    skipWhitespace();
9665    cue.endTime = consumeTimeStamp(); // (5) collect cue end time
9666    // 4.1 WebVTT cue settings list.
9667
9668    skipWhitespace();
9669    consumeCueSettings(input, cue);
9670  }
9671
9672  var ESCAPE = {
9673    "&amp;": "&",
9674    "&lt;": "<",
9675    "&gt;": ">",
9676    "&lrm;": "\u200E",
9677    "&rlm;": "\u200F",
9678    "&nbsp;": "\xA0"
9679  };
9680  var TAG_NAME = {
9681    c: "span",
9682    i: "i",
9683    b: "b",
9684    u: "u",
9685    ruby: "ruby",
9686    rt: "rt",
9687    v: "span",
9688    lang: "span"
9689  };
9690  var TAG_ANNOTATION = {
9691    v: "title",
9692    lang: "lang"
9693  };
9694  var NEEDS_PARENT = {
9695    rt: "ruby"
9696  }; // Parse content into a document fragment.
9697
9698  function parseContent(window, input) {
9699    function nextToken() {
9700      // Check for end-of-string.
9701      if (!input) {
9702        return null;
9703      } // Consume 'n' characters from the input.
9704
9705
9706      function consume(result) {
9707        input = input.substr(result.length);
9708        return result;
9709      }
9710
9711      var m = input.match(/^([^<]*)(<[^>]*>?)?/); // If there is some text before the next tag, return it, otherwise return
9712      // the tag.
9713
9714      return consume(m[1] ? m[1] : m[2]);
9715    } // Unescape a string 's'.
9716
9717
9718    function unescape1(e) {
9719      return ESCAPE[e];
9720    }
9721
9722    function unescape(s) {
9723      while (m = s.match(/&(amp|lt|gt|lrm|rlm|nbsp);/)) {
9724        s = s.replace(m[0], unescape1);
9725      }
9726
9727      return s;
9728    }
9729
9730    function shouldAdd(current, element) {
9731      return !NEEDS_PARENT[element.localName] || NEEDS_PARENT[element.localName] === current.localName;
9732    } // Create an element for this tag.
9733
9734
9735    function createElement(type, annotation) {
9736      var tagName = TAG_NAME[type];
9737
9738      if (!tagName) {
9739        return null;
9740      }
9741
9742      var element = window.document.createElement(tagName);
9743      element.localName = tagName;
9744      var name = TAG_ANNOTATION[type];
9745
9746      if (name && annotation) {
9747        element[name] = annotation.trim();
9748      }
9749
9750      return element;
9751    }
9752
9753    var rootDiv = window.document.createElement("div"),
9754        current = rootDiv,
9755        t,
9756        tagStack = [];
9757
9758    while ((t = nextToken()) !== null) {
9759      if (t[0] === '<') {
9760        if (t[1] === "/") {
9761          // If the closing tag matches, move back up to the parent node.
9762          if (tagStack.length && tagStack[tagStack.length - 1] === t.substr(2).replace(">", "")) {
9763            tagStack.pop();
9764            current = current.parentNode;
9765          } // Otherwise just ignore the end tag.
9766
9767
9768          continue;
9769        }
9770
9771        var ts = parseTimeStamp(t.substr(1, t.length - 2));
9772        var node;
9773
9774        if (ts) {
9775          // Timestamps are lead nodes as well.
9776          node = window.document.createProcessingInstruction("timestamp", ts);
9777          current.appendChild(node);
9778          continue;
9779        }
9780
9781        var m = t.match(/^<([^.\s/0-9>]+)(\.[^\s\\>]+)?([^>\\]+)?(\\?)>?$/); // If we can't parse the tag, skip to the next tag.
9782
9783        if (!m) {
9784          continue;
9785        } // Try to construct an element, and ignore the tag if we couldn't.
9786
9787
9788        node = createElement(m[1], m[3]);
9789
9790        if (!node) {
9791          continue;
9792        } // Determine if the tag should be added based on the context of where it
9793        // is placed in the cuetext.
9794
9795
9796        if (!shouldAdd(current, node)) {
9797          continue;
9798        } // Set the class list (as a list of classes, separated by space).
9799
9800
9801        if (m[2]) {
9802          node.className = m[2].substr(1).replace('.', ' ');
9803        } // Append the node to the current node, and enter the scope of the new
9804        // node.
9805
9806
9807        tagStack.push(m[1]);
9808        current.appendChild(node);
9809        current = node;
9810        continue;
9811      } // Text nodes are leaf nodes.
9812
9813
9814      current.appendChild(window.document.createTextNode(unescape(t)));
9815    }
9816
9817    return rootDiv;
9818  } // This is a list of all the Unicode characters that have a strong
9819  // right-to-left category. What this means is that these characters are
9820  // written right-to-left for sure. It was generated by pulling all the strong
9821  // right-to-left characters out of the Unicode data table. That table can
9822  // found at: http://www.unicode.org/Public/UNIDATA/UnicodeData.txt
9823
9824
9825  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], [0x6
vendor: 17,874 bytes, lines 9825-10300
982571, 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]];
9826
9827  function isStrongRTLChar(charCode) {
9828    for (var i = 0; i < strongRTLRanges.length; i++) {
9829      var currentRange = strongRTLRanges[i];
9830
9831      if (charCode >= currentRange[0] && charCode <= currentRange[1]) {
9832        return true;
9833      }
9834    }
9835
9836    return false;
9837  }
9838
9839  function determineBidi(cueDiv) {
9840    var nodeStack = [],
9841        text = "",
9842        charCode;
9843
9844    if (!cueDiv || !cueDiv.childNodes) {
9845      return "ltr";
9846    }
9847
9848    function pushNodes(nodeStack, node) {
9849      for (var i = node.childNodes.length - 1; i >= 0; i--) {
9850        nodeStack.push(node.childNodes[i]);
9851      }
9852    }
9853
9854    function nextTextNode(nodeStack) {
9855      if (!nodeStack || !nodeStack.length) {
9856        return null;
9857      }
9858
9859      var node = nodeStack.pop(),
9860          text = node.textContent || node.innerText;
9861
9862      if (text) {
9863        // TODO: This should match all unicode type B characters (paragraph
9864        // separator characters). See issue #115.
9865        var m = text.match(/^.*(\n|\r)/);
9866
9867        if (m) {
9868          nodeStack.length = 0;
9869          return m[0];
9870        }
9871
9872        return text;
9873      }
9874
9875      if (node.tagName === "ruby") {
9876        return nextTextNode(nodeStack);
9877      }
9878
9879      if (node.childNodes) {
9880        pushNodes(nodeStack, node);
9881        return nextTextNode(nodeStack);
9882      }
9883    }
9884
9885    pushNodes(nodeStack, cueDiv);
9886
9887    while (text = nextTextNode(nodeStack)) {
9888      for (var i = 0; i < text.length; i++) {
9889        charCode = text.charCodeAt(i);
9890
9891        if (isStrongRTLChar(charCode)) {
9892          return "rtl";
9893        }
9894      }
9895    }
9896
9897    return "ltr";
9898  }
9899
9900  function computeLinePos(cue) {
9901    if (typeof cue.line === "number" && (cue.snapToLines || cue.line >= 0 && cue.line <= 100)) {
9902      return cue.line;
9903    }
9904
9905    if (!cue.track || !cue.track.textTrackList || !cue.track.textTrackList.mediaElement) {
9906      return -1;
9907    }
9908
9909    var track = cue.track,
9910        trackList = track.textTrackList,
9911        count = 0;
9912
9913    for (var i = 0; i < trackList.length && trackList[i] !== track; i++) {
9914      if (trackList[i].mode === "showing") {
9915        count++;
9916      }
9917    }
9918
9919    return ++count * -1;
9920  }
9921
9922  function StyleBox() {} // Apply styles to a div. If there is no div passed then it defaults to the
9923  // div on 'this'.
9924
9925
9926  StyleBox.prototype.applyStyles = function (styles, div) {
9927    div = div || this.div;
9928
9929    for (var prop in styles) {
9930      if (styles.hasOwnProperty(prop)) {
9931        div.style[prop] = styles[prop];
9932      }
9933    }
9934  };
9935
9936  StyleBox.prototype.formatStyle = function (val, unit) {
9937    return val === 0 ? 0 : val + unit;
9938  }; // Constructs the computed display state of the cue (a div). Places the div
9939  // into the overlay which should be a block level element (usually a div).
9940
9941
9942  function CueStyleBox(window, cue, styleOptions) {
9943    StyleBox.call(this);
9944    this.cue = cue; // Parse our cue's text into a DOM tree rooted at 'cueDiv'. This div will
9945    // have inline positioning and will function as the cue background box.
9946
9947    this.cueDiv = parseContent(window, cue.text);
9948    var styles = {
9949      color: "rgba(255, 255, 255, 1)",
9950      backgroundColor: "rgba(0, 0, 0, 0.8)",
9951      position: "relative",
9952      left: 0,
9953      right: 0,
9954      top: 0,
9955      bottom: 0,
9956      display: "inline",
9957      writingMode: cue.vertical === "" ? "horizontal-tb" : cue.vertical === "lr" ? "vertical-lr" : "vertical-rl",
9958      unicodeBidi: "plaintext"
9959    };
9960    this.applyStyles(styles, this.cueDiv); // Create an absolutely positioned div that will be used to position the cue
9961    // div. Note, all WebVTT cue-setting alignments are equivalent to the CSS
9962    // mirrors of them except "middle" which is "center" in CSS.
9963
9964    this.div = window.document.createElement("div");
9965    styles = {
9966      direction: determineBidi(this.cueDiv),
9967      writingMode: cue.vertical === "" ? "horizontal-tb" : cue.vertical === "lr" ? "vertical-lr" : "vertical-rl",
9968      unicodeBidi: "plaintext",
9969      textAlign: cue.align === "middle" ? "center" : cue.align,
9970      font: styleOptions.font,
9971      whiteSpace: "pre-line",
9972      position: "absolute"
9973    };
9974    this.applyStyles(styles);
9975    this.div.appendChild(this.cueDiv); // Calculate the distance from the reference edge of the viewport to the text
9976    // position of the cue box. The reference edge will be resolved later when
9977    // the box orientation styles are applied.
9978
9979    var textPos = 0;
9980
9981    switch (cue.positionAlign) {
9982      case "start":
9983        textPos = cue.position;
9984        break;
9985
9986      case "middle":
9987        textPos = cue.position - cue.size / 2;
9988        break;
9989
9990      case "end":
9991        textPos = cue.position - cue.size;
9992        break;
9993    } // Horizontal box orientation; textPos is the distance from the left edge of the
9994    // area to the left edge of the box and cue.size is the distance extending to
9995    // the right from there.
9996
9997
9998    if (cue.vertical === "") {
9999      this.applyStyles({
10000        left: this.formatStyle(textPos, "%"),
10001        width: this.formatStyle(cue.size, "%")
10002      }); // Vertical box orientation; textPos is the distance from the top edge of the
10003      // area to the top edge of the box and cue.size is the height extending
10004      // downwards from there.
10005    } else {
10006      this.applyStyles({
10007        top: this.formatStyle(textPos, "%"),
10008        height: this.formatStyle(cue.size, "%")
10009      });
10010    }
10011
10012    this.move = function (box) {
10013      this.applyStyles({
10014        top: this.formatStyle(box.top, "px"),
10015        bottom: this.formatStyle(box.bottom, "px"),
10016        left: this.formatStyle(box.left, "px"),
10017        right: this.formatStyle(box.right, "px"),
10018        height: this.formatStyle(box.height, "px"),
10019        width: this.formatStyle(box.width, "px")
10020      });
10021    };
10022  }
10023
10024  CueStyleBox.prototype = _objCreate(StyleBox.prototype);
10025  CueStyleBox.prototype.constructor = CueStyleBox; // Represents the co-ordinates of an Element in a way that we can easily
10026  // compute things with such as if it overlaps or intersects with another Element.
10027  // Can initialize it with either a StyleBox or another BoxPosition.
10028
10029  function BoxPosition(obj) {
10030    // Either a BoxPosition was passed in and we need to copy it, or a StyleBox
10031    // was passed in and we need to copy the results of 'getBoundingClientRect'
10032    // as the object returned is readonly. All co-ordinate values are in reference
10033    // to the viewport origin (top left).
10034    var lh, height, width, top;
10035
10036    if (obj.div) {
10037      height = obj.div.offsetHeight;
10038      width = obj.div.offsetWidth;
10039      top = obj.div.offsetTop;
10040      var rects = (rects = obj.div.childNodes) && (rects = rects[0]) && rects.getClientRects && rects.getClientRects();
10041      obj = obj.div.getBoundingClientRect(); // In certain cases the outter div will be slightly larger then the sum of
10042      // the inner div's lines. This could be due to bold text, etc, on some platforms.
10043      // In this case we should get the average line height and use that. This will
10044      // result in the desired behaviour.
10045
10046      lh = rects ? Math.max(rects[0] && rects[0].height || 0, obj.height / rects.length) : 0;
10047    }
10048
10049    this.left = obj.left;
10050    this.right = obj.right;
10051    this.top = obj.top || top;
10052    this.height = obj.height || height;
10053    this.bottom = obj.bottom || top + (obj.height || height);
10054    this.width = obj.width || width;
10055    this.lineHeight = lh !== undefined ? lh : obj.lineHeight;
10056  } // Move the box along a particular axis. Optionally pass in an amount to move
10057  // the box. If no amount is passed then the default is the line height of the
10058  // box.
10059
10060
10061  BoxPosition.prototype.move = function (axis, toMove) {
10062    toMove = toMove !== undefined ? toMove : this.lineHeight;
10063
10064    switch (axis) {
10065      case "+x":
10066        this.left += toMove;
10067        this.right += toMove;
10068        break;
10069
10070      case "-x":
10071        this.left -= toMove;
10072        this.right -= toMove;
10073        break;
10074
10075      case "+y":
10076        this.top += toMove;
10077        this.bottom += toMove;
10078        break;
10079
10080      case "-y":
10081        this.top -= toMove;
10082        this.bottom -= toMove;
10083        break;
10084    }
10085  }; // Check if this box overlaps another box, b2.
10086
10087
10088  BoxPosition.prototype.overlaps = function (b2) {
10089    return this.left < b2.right && this.right > b2.left && this.top < b2.bottom && this.bottom > b2.top;
10090  }; // Check if this box overlaps any other boxes in boxes.
10091
10092
10093  BoxPosition.prototype.overlapsAny = function (boxes) {
10094    for (var i = 0; i < boxes.length; i++) {
10095      if (this.overlaps(boxes[i])) {
10096        return true;
10097      }
10098    }
10099
10100    return false;
10101  }; // Check if this box is within another box.
10102
10103
10104  BoxPosition.prototype.within = function (container) {
10105    return this.top >= container.top && this.bottom <= container.bottom && this.left >= container.left && this.right <= container.right;
10106  }; // Check if this box is entirely within the container or it is overlapping
10107  // on the edge opposite of the axis direction passed. For example, if "+x" is
10108  // passed and the box is overlapping on the left edge of the container, then
10109  // return true.
10110
10111
10112  BoxPosition.prototype.overlapsOppositeAxis = function (container, axis) {
10113    switch (axis) {
10114      case "+x":
10115        return this.left < container.left;
10116
10117      case "-x":
10118        return this.right > container.right;
10119
10120      case "+y":
10121        return this.top < container.top;
10122
10123      case "-y":
10124        return this.bottom > container.bottom;
10125    }
10126  }; // Find the percentage of the area that this box is overlapping with another
10127  // box.
10128
10129
10130  BoxPosition.prototype.intersectPercentage = function (b2) {
10131    var x = Math.max(0, Math.min(this.right, b2.right) - Math.max(this.left, b2.left)),
10132        y = Math.max(0, Math.min(this.bottom, b2.bottom) - Math.max(this.top, b2.top)),
10133        intersectArea = x * y;
10134    return intersectArea / (this.height * this.width);
10135  }; // Convert the positions from this box to CSS compatible positions using
10136  // the reference container's positions. This has to be done because this
10137  // box's positions are in reference to the viewport origin, whereas, CSS
10138  // values are in referecne to their respective edges.
10139
10140
10141  BoxPosition.prototype.toCSSCompatValues = function (reference) {
10142    return {
10143      top: this.top - reference.top,
10144      bottom: reference.bottom - this.bottom,
10145      left: this.left - reference.left,
10146      right: reference.right - this.right,
10147      height: this.height,
10148      width: this.width
10149    };
10150  }; // Get an object that represents the box's position without anything extra.
10151  // Can pass a StyleBox, HTMLElement, or another BoxPositon.
10152
10153
10154  BoxPosition.getSimpleBoxPosition = function (obj) {
10155    var height = obj.div ? obj.div.offsetHeight : obj.tagName ? obj.offsetHeight : 0;
10156    var width = obj.div ? obj.div.offsetWidth : obj.tagName ? obj.offsetWidth : 0;
10157    var top = obj.div ? obj.div.offsetTop : obj.tagName ? obj.offsetTop : 0;
10158    obj = obj.div ? obj.div.getBoundingClientRect() : obj.tagName ? obj.getBoundingClientRect() : obj;
10159    var ret = {
10160      left: obj.left,
10161      right: obj.right,
10162      top: obj.top || top,
10163      height: obj.height || height,
10164      bottom: obj.bottom || top + (obj.height || height),
10165      width: obj.width || width
10166    };
10167    return ret;
10168  }; // Move a StyleBox to its specified, or next best, position. The containerBox
10169  // is the box that contains the StyleBox, such as a div. boxPositions are
10170  // a list of other boxes that the styleBox can't overlap with.
10171
10172
10173  function moveBoxToLinePosition(window, styleBox, containerBox, boxPositions) {
10174    // Find the best position for a cue box, b, on the video. The axis parameter
10175    // is a list of axis, the order of which, it will move the box along. For example:
10176    // Passing ["+x", "-x"] will move the box first along the x axis in the positive
10177    // direction. If it doesn't find a good position for it there it will then move
10178    // it along the x axis in the negative direction.
10179    function findBestPosition(b, axis) {
10180      var bestPosition,
10181          specifiedPosition = new BoxPosition(b),
10182          percentage = 1; // Highest possible so the first thing we get is better.
10183
10184      for (var i = 0; i < axis.length; i++) {
10185        while (b.overlapsOppositeAxis(containerBox, axis[i]) || b.within(containerBox) && b.overlapsAny(boxPositions)) {
10186          b.move(axis[i]);
10187        } // We found a spot where we aren't overlapping anything. This is our
10188        // best position.
10189
10190
10191        if (b.within(containerBox)) {
10192          return b;
10193        }
10194
10195        var p = b.intersectPercentage(containerBox); // If we're outside the container box less then we were on our last try
10196        // then remember this position as the best position.
10197
10198        if (percentage > p) {
10199          bestPosition = new BoxPosition(b);
10200          percentage = p;
10201        } // Reset the box position to the specified position.
10202
10203
10204        b = new BoxPosition(specifiedPosition);
10205      }
10206
10207      return bestPosition || specifiedPosition;
10208    }
10209
10210    var boxPosition = new BoxPosition(styleBox),
10211        cue = styleBox.cue,
10212        linePos = computeLinePos(cue),
10213        axis = []; // If we have a line number to align the cue to.
10214
10215    if (cue.snapToLines) {
10216      var size;
10217
10218      switch (cue.vertical) {
10219        case "":
10220          axis = ["+y", "-y"];
10221          size = "height";
10222          break;
10223
10224        case "rl":
10225          axis = ["+x", "-x"];
10226          size = "width";
10227          break;
10228
10229        case "lr":
10230          axis = ["-x", "+x"];
10231          size = "width";
10232          break;
10233      }
10234
10235      var step = boxPosition.lineHeight,
10236          position = step * Math.round(linePos),
10237          maxPosition = containerBox[size] + step,
10238          initialAxis = axis[0]; // If the specified intial position is greater then the max position then
10239      // clamp the box to the amount of steps it would take for the box to
10240      // reach the max position.
10241
10242      if (Math.abs(position) > maxPosition) {
10243        position = position < 0 ? -1 : 1;
10244        position *= Math.ceil(maxPosition / step) * step;
10245      } // If computed line position returns negative then line numbers are
10246      // relative to the bottom of the video instead of the top. Therefore, we
10247      // need to increase our initial position by the length or width of the
10248      // video, depending on the writing direction, and reverse our axis directions.
10249
10250
10251      if (linePos < 0) {
10252        position += cue.vertical === "" ? containerBox.height : containerBox.width;
10253        axis = axis.reverse();
10254      } // Move the box to the specified position. This may not be its best
10255      // position.
10256
10257
10258      boxPosition.move(initialAxis, position);
10259    } else {
10260      // If we have a percentage line value for the cue.
10261      var calculatedPercentage = boxPosition.lineHeight / containerBox.height * 100;
10262
10263      switch (cue.lineAlign) {
10264        case "middle":
10265          linePos -= calculatedPercentage / 2;
10266          break;
10267
10268        case "end":
10269          linePos -= calculatedPercentage;
10270          break;
10271      } // Apply initial line position to the cue box.
10272
10273
10274      switch (cue.vertical) {
10275        case "":
10276          styleBox.applyStyles({
10277            top: styleBox.formatStyle(linePos, "%")
10278          });
10279          break;
10280
10281        case "rl":
10282          styleBox.applyStyles({
10283            left: styleBox.formatStyle(linePos, "%")
10284          });
10285          break;
10286
10287        case "lr":
10288          styleBox.applyStyles({
10289            right: styleBox.formatStyle(linePos, "%")
10290          });
10291          break;
10292      }
10293
10294      axis = ["+y", "-x", "+x", "-y"]; // Get the box position again after we've applied the specified positioning
10295      // to it.
10296
10297      boxPosition = new BoxPosition(styleBox);
10298    }
10299
10300    var bestPosition = findBestPosition(boxPosition, axis);
vendor: 12,961 bytes, lines 10301-10717
10301    styleBox.move(bestPosition.toCSSCompatValues(containerBox));
10302  }
10303
10304  function WebVTT$1() {} // Nothing
10305  // Helper to allow strings to be decoded instead of the default binary utf8 data.
10306
10307
10308  WebVTT$1.StringDecoder = function () {
10309    return {
10310      decode: function decode(data) {
10311        if (!data) {
10312          return "";
10313        }
10314
10315        if (typeof data !== "string") {
10316          throw new Error("Error - expected string data.");
10317        }
10318
10319        return decodeURIComponent(encodeURIComponent(data));
10320      }
10321    };
10322  };
10323
10324  WebVTT$1.convertCueToDOMTree = function (window, cuetext) {
10325    if (!window || !cuetext) {
10326      return null;
10327    }
10328
10329    return parseContent(window, cuetext);
10330  };
10331
10332  var FONT_SIZE_PERCENT = 0.05;
10333  var FONT_STYLE = "sans-serif";
10334  var CUE_BACKGROUND_PADDING = "1.5%"; // Runs the processing model over the cues and regions passed to it.
10335  // @param overlay A block level element (usually a div) that the computed cues
10336  //                and regions will be placed into.
10337
10338  WebVTT$1.processCues = function (window, cues, overlay) {
10339    if (!window || !cues || !overlay) {
10340      return null;
10341    } // Remove all previous children.
10342
10343
10344    while (overlay.firstChild) {
10345      overlay.removeChild(overlay.firstChild);
10346    }
10347
10348    var paddedOverlay = window.document.createElement("div");
10349    paddedOverlay.style.position = "absolute";
10350    paddedOverlay.style.left = "0";
10351    paddedOverlay.style.right = "0";
10352    paddedOverlay.style.top = "0";
10353    paddedOverlay.style.bottom = "0";
10354    paddedOverlay.style.margin = CUE_BACKGROUND_PADDING;
10355    overlay.appendChild(paddedOverlay); // Determine if we need to compute the display states of the cues. This could
10356    // be the case if a cue's state has been changed since the last computation or
10357    // if it has not been computed yet.
10358
10359    function shouldCompute(cues) {
10360      for (var i = 0; i < cues.length; i++) {
10361        if (cues[i].hasBeenReset || !cues[i].displayState) {
10362          return true;
10363        }
10364      }
10365
10366      return false;
10367    } // We don't need to recompute the cues' display states. Just reuse them.
10368
10369
10370    if (!shouldCompute(cues)) {
10371      for (var i = 0; i < cues.length; i++) {
10372        paddedOverlay.appendChild(cues[i].displayState);
10373      }
10374
10375      return;
10376    }
10377
10378    var boxPositions = [],
10379        containerBox = BoxPosition.getSimpleBoxPosition(paddedOverlay),
10380        fontSize = Math.round(containerBox.height * FONT_SIZE_PERCENT * 100) / 100;
10381    var styleOptions = {
10382      font: fontSize + "px " + FONT_STYLE
10383    };
10384
10385    (function () {
10386      var styleBox, cue;
10387
10388      for (var i = 0; i < cues.length; i++) {
10389        cue = cues[i]; // Compute the intial position and styles of the cue div.
10390
10391        styleBox = new CueStyleBox(window, cue, styleOptions);
10392        paddedOverlay.appendChild(styleBox.div); // Move the cue div to it's correct line position.
10393
10394        moveBoxToLinePosition(window, styleBox, containerBox, boxPositions); // Remember the computed div so that we don't have to recompute it later
10395        // if we don't have too.
10396
10397        cue.displayState = styleBox.div;
10398        boxPositions.push(BoxPosition.getSimpleBoxPosition(styleBox));
10399      }
10400    })();
10401  };
10402
10403  WebVTT$1.Parser = function (window, vttjs, decoder) {
10404    if (!decoder) {
10405      decoder = vttjs;
10406      vttjs = {};
10407    }
10408
10409    if (!vttjs) {
10410      vttjs = {};
10411    }
10412
10413    this.window = window;
10414    this.vttjs = vttjs;
10415    this.state = "INITIAL";
10416    this.buffer = "";
10417    this.decoder = decoder || new TextDecoder("utf8");
10418    this.regionList = [];
10419  };
10420
10421  WebVTT$1.Parser.prototype = {
10422    // If the error is a ParsingError then report it to the consumer if
10423    // possible. If it's not a ParsingError then throw it like normal.
10424    reportOrThrowError: function reportOrThrowError(e) {
10425      if (e instanceof ParsingError) {
10426        this.onparsingerror && this.onparsingerror(e);
10427      } else {
10428        throw e;
10429      }
10430    },
10431    parse: function parse(data) {
10432      var self = this; // If there is no data then we won't decode it, but will just try to parse
10433      // whatever is in buffer already. This may occur in circumstances, for
10434      // example when flush() is called.
10435
10436      if (data) {
10437        // Try to decode the data that we received.
10438        self.buffer += self.decoder.decode(data, {
10439          stream: true
10440        });
10441      }
10442
10443      function collectNextLine() {
10444        var buffer = self.buffer;
10445        var pos = 0;
10446
10447        while (pos < buffer.length && buffer[pos] !== '\r' && buffer[pos] !== '\n') {
10448          ++pos;
10449        }
10450
10451        var line = buffer.substr(0, pos); // Advance the buffer early in case we fail below.
10452
10453        if (buffer[pos] === '\r') {
10454          ++pos;
10455        }
10456
10457        if (buffer[pos] === '\n') {
10458          ++pos;
10459        }
10460
10461        self.buffer = buffer.substr(pos);
10462        return line;
10463      } // 3.4 WebVTT region and WebVTT region settings syntax
10464
10465
10466      function parseRegion(input) {
10467        var settings = new Settings();
10468        parseOptions(input, function (k, v) {
10469          switch (k) {
10470            case "id":
10471              settings.set(k, v);
10472              break;
10473
10474            case "width":
10475              settings.percent(k, v);
10476              break;
10477
10478            case "lines":
10479              settings.integer(k, v);
10480              break;
10481
10482            case "regionanchor":
10483            case "viewportanchor":
10484              var xy = v.split(',');
10485
10486              if (xy.length !== 2) {
10487                break;
10488              } // We have to make sure both x and y parse, so use a temporary
10489              // settings object here.
10490
10491
10492              var anchor = new Settings();
10493              anchor.percent("x", xy[0]);
10494              anchor.percent("y", xy[1]);
10495
10496              if (!anchor.has("x") || !anchor.has("y")) {
10497                break;
10498              }
10499
10500              settings.set(k + "X", anchor.get("x"));
10501              settings.set(k + "Y", anchor.get("y"));
10502              break;
10503
10504            case "scroll":
10505              settings.alt(k, v, ["up"]);
10506              break;
10507          }
10508        }, /=/, /\s/); // Create the region, using default values for any values that were not
10509        // specified.
10510
10511        if (settings.has("id")) {
10512          var region = new (self.vttjs.VTTRegion || self.window.VTTRegion)();
10513          region.width = settings.get("width", 100);
10514          region.lines = settings.get("lines", 3);
10515          region.regionAnchorX = settings.get("regionanchorX", 0);
10516          region.regionAnchorY = settings.get("regionanchorY", 100);
10517          region.viewportAnchorX = settings.get("viewportanchorX", 0);
10518          region.viewportAnchorY = settings.get("viewportanchorY", 100);
10519          region.scroll = settings.get("scroll", ""); // Register the region.
10520
10521          self.onregion && self.onregion(region); // Remember the VTTRegion for later in case we parse any VTTCues that
10522          // reference it.
10523
10524          self.regionList.push({
10525            id: settings.get("id"),
10526            region: region
10527          });
10528        }
10529      } // draft-pantos-http-live-streaming-20
10530      // https://tools.ietf.org/html/draft-pantos-http-live-streaming-20#section-3.5
10531      // 3.5 WebVTT
10532
10533
10534      function parseTimestampMap(input) {
10535        var settings = new Settings();
10536        parseOptions(input, function (k, v) {
10537          switch (k) {
10538            case "MPEGT":
10539              settings.integer(k + 'S', v);
10540              break;
10541
10542            case "LOCA":
10543              settings.set(k + 'L', parseTimeStamp(v));
10544              break;
10545          }
10546        }, /[^\d]:/, /,/);
10547        self.ontimestampmap && self.ontimestampmap({
10548          "MPEGTS": settings.get("MPEGTS"),
10549          "LOCAL": settings.get("LOCAL")
10550        });
10551      } // 3.2 WebVTT metadata header syntax
10552
10553
10554      function parseHeader(input) {
10555        if (input.match(/X-TIMESTAMP-MAP/)) {
10556          // This line contains HLS X-TIMESTAMP-MAP metadata
10557          parseOptions(input, function (k, v) {
10558            switch (k) {
10559              case "X-TIMESTAMP-MAP":
10560                parseTimestampMap(v);
10561                break;
10562            }
10563          }, /=/);
10564        } else {
10565          parseOptions(input, function (k, v) {
10566            switch (k) {
10567              case "Region":
10568                // 3.3 WebVTT region metadata header syntax
10569                parseRegion(v);
10570                break;
10571            }
10572          }, /:/);
10573        }
10574      } // 5.1 WebVTT file parsing.
10575
10576
10577      try {
10578        var line;
10579
10580        if (self.state === "INITIAL") {
10581          // We can't start parsing until we have the first line.
10582          if (!/\r\n|\n/.test(self.buffer)) {
10583            return this;
10584          }
10585
10586          line = collectNextLine();
10587          var m = line.match(/^WEBVTT([ \t].*)?$/);
10588
10589          if (!m || !m[0]) {
10590            throw new ParsingError(ParsingError.Errors.BadSignature);
10591          }
10592
10593          self.state = "HEADER";
10594        }
10595
10596        var alreadyCollectedLine = false;
10597
10598        while (self.buffer) {
10599          // We can't parse a line until we have the full line.
10600          if (!/\r\n|\n/.test(self.buffer)) {
10601            return this;
10602          }
10603
10604          if (!alreadyCollectedLine) {
10605            line = collectNextLine();
10606          } else {
10607            alreadyCollectedLine = false;
10608          }
10609
10610          switch (self.state) {
10611            case "HEADER":
10612              // 13-18 - Allow a header (metadata) under the WEBVTT line.
10613              if (/:/.test(line)) {
10614                parseHeader(line);
10615              } else if (!line) {
10616                // An empty line terminates the header and starts the body (cues).
10617                self.state = "ID";
10618              }
10619
10620              continue;
10621
10622            case "NOTE":
10623              // Ignore NOTE blocks.
10624              if (!line) {
10625                self.state = "ID";
10626              }
10627
10628              continue;
10629
10630            case "ID":
10631              // Check for the start of NOTE blocks.
10632              if (/^NOTE($|[ \t])/.test(line)) {
10633                self.state = "NOTE";
10634                break;
10635              } // 19-29 - Allow any number of line terminators, then initialize new cue values.
10636
10637
10638              if (!line) {
10639                continue;
10640              }
10641
10642              self.cue = new (self.vttjs.VTTCue || self.window.VTTCue)(0, 0, "");
10643              self.state = "CUE"; // 30-39 - Check if self line contains an optional identifier or timing data.
10644
10645              if (line.indexOf("-->") === -1) {
10646                self.cue.id = line;
10647                continue;
10648              }
10649
10650            // Process line as start of a cue.
10651
10652            /*falls through*/
10653
10654            case "CUE":
10655              // 40 - Collect cue timings and settings.
10656              try {
10657                parseCue(line, self.cue, self.regionList);
10658              } catch (e) {
10659                self.reportOrThrowError(e); // In case of an error ignore rest of the cue.
10660
10661                self.cue = null;
10662                self.state = "BADCUE";
10663                continue;
10664              }
10665
10666              self.state = "CUETEXT";
10667              continue;
10668
10669            case "CUETEXT":
10670              var hasSubstring = line.indexOf("-->") !== -1; // 34 - If we have an empty line then report the cue.
10671              // 35 - If we have the special substring '-->' then report the cue,
10672              // but do not collect the line as we need to process the current
10673              // one as a new cue.
10674
10675              if (!line || hasSubstring && (alreadyCollectedLine = true)) {
10676                // We are done parsing self cue.
10677                self.oncue && self.oncue(self.cue);
10678                self.cue = null;
10679                self.state = "ID";
10680                continue;
10681              }
10682
10683              if (self.cue.text) {
10684                self.cue.text += "\n";
10685              }
10686
10687              self.cue.text += line;
10688              continue;
10689
10690            case "BADCUE":
10691              // BADCUE
10692              // 54-62 - Collect and discard the remaining cue.
10693              if (!line) {
10694                self.state = "ID";
10695              }
10696
10697              continue;
10698          }
10699        }
10700      } catch (e) {
10701        self.reportOrThrowError(e); // If we are currently parsing a cue, report what we have.
10702
10703        if (self.state === "CUETEXT" && self.cue && self.oncue) {
10704          self.oncue(self.cue);
10705        }
10706
10707        self.cue = null; // Enter BADWEBVTT state if header was not parsed correctly otherwise
10708        // another exception occurred so enter BADCUE state.
10709
10710        self.state = self.state === "INITIAL" ? "BADWEBVTT" : "BADCUE";
10711      }
10712
10713      return this;
10714    },
10715    flush: function flush() {
10716      var self = this;
10717
vendor: 5,482 bytes, lines 10718-10916
10718      try {
10719        // Finish decoding the stream.
10720        self.buffer += self.decoder.decode(); // Synthesize the end of the current cue or region.
10721
10722        if (self.cue || self.state === "HEADER") {
10723          self.buffer += "\n\n";
10724          self.parse();
10725        } // If we've flushed, parsed, and we're still on the INITIAL state then
10726        // that means we don't have enough of the stream to parse the first
10727        // line.
10728
10729
10730        if (self.state === "INITIAL") {
10731          throw new ParsingError(ParsingError.Errors.BadSignature);
10732        }
10733      } catch (e) {
10734        self.reportOrThrowError(e);
10735      }
10736
10737      self.onflush && self.onflush();
10738      return this;
10739    }
10740  };
10741  var vtt = WebVTT$1;
10742
10743  /**
10744   * Copyright 2013 vtt.js Contributors
10745   *
10746   * Licensed under the Apache License, Version 2.0 (the "License");
10747   * you may not use this file except in compliance with the License.
10748   * You may obtain a copy of the License at
10749   *
10750   *   http://www.apache.org/licenses/LICENSE-2.0
10751   *
10752   * Unless required by applicable law or agreed to in writing, software
10753   * distributed under the License is distributed on an "AS IS" BASIS,
10754   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
10755   * See the License for the specific language governing permissions and
10756   * limitations under the License.
10757   */
10758  var autoKeyword = "auto";
10759  var directionSetting = {
10760    "": 1,
10761    "lr": 1,
10762    "rl": 1
10763  };
10764  var alignSetting = {
10765    "start": 1,
10766    "middle": 1,
10767    "end": 1,
10768    "left": 1,
10769    "right": 1
10770  };
10771
10772  function findDirectionSetting(value) {
10773    if (typeof value !== "string") {
10774      return false;
10775    }
10776
10777    var dir = directionSetting[value.toLowerCase()];
10778    return dir ? value.toLowerCase() : false;
10779  }
10780
10781  function findAlignSetting(value) {
10782    if (typeof value !== "string") {
10783      return false;
10784    }
10785
10786    var align = alignSetting[value.toLowerCase()];
10787    return align ? value.toLowerCase() : false;
10788  }
10789
10790  function VTTCue(startTime, endTime, text) {
10791    /**
10792     * Shim implementation specific properties. These properties are not in
10793     * the spec.
10794     */
10795    // Lets us know when the VTTCue's data has changed in such a way that we need
10796    // to recompute its display state. This lets us compute its display state
10797    // lazily.
10798    this.hasBeenReset = false;
10799    /**
10800     * VTTCue and TextTrackCue properties
10801     * http://dev.w3.org/html5/webvtt/#vttcue-interface
10802     */
10803
10804    var _id = "";
10805    var _pauseOnExit = false;
10806    var _startTime = startTime;
10807    var _endTime = endTime;
10808    var _text = text;
10809    var _region = null;
10810    var _vertical = "";
10811    var _snapToLines = true;
10812    var _line = "auto";
10813    var _lineAlign = "start";
10814    var _position = 50;
10815    var _positionAlign = "middle";
10816    var _size = 50;
10817    var _align = "middle";
10818    Object.defineProperties(this, {
10819      "id": {
10820        enumerable: true,
10821        get: function get() {
10822          return _id;
10823        },
10824        set: function set(value) {
10825          _id = "" + value;
10826        }
10827      },
10828      "pauseOnExit": {
10829        enumerable: true,
10830        get: function get() {
10831          return _pauseOnExit;
10832        },
10833        set: function set(value) {
10834          _pauseOnExit = !!value;
10835        }
10836      },
10837      "startTime": {
10838        enumerable: true,
10839        get: function get() {
10840          return _startTime;
10841        },
10842        set: function set(value) {
10843          if (typeof value !== "number") {
10844            throw new TypeError("Start time must be set to a number.");
10845          }
10846
10847          _startTime = value;
10848          this.hasBeenReset = true;
10849        }
10850      },
10851      "endTime": {
10852        enumerable: true,
10853        get: function get() {
10854          return _endTime;
10855        },
10856        set: function set(value) {
10857          if (typeof value !== "number") {
10858            throw new TypeError("End time must be set to a number.");
10859          }
10860
10861          _endTime = value;
10862          this.hasBeenReset = true;
10863        }
10864      },
10865      "text": {
10866        enumerable: true,
10867        get: function get() {
10868          return _text;
10869        },
10870        set: function set(value) {
10871          _text = "" + value;
10872          this.hasBeenReset = true;
10873        }
10874      },
10875      "region": {
10876        enumerable: true,
10877        get: function get() {
10878          return _region;
10879        },
10880        set: function set(value) {
10881          _region = value;
10882          this.hasBeenReset = true;
10883        }
10884      },
10885      "vertical": {
10886        enumerable: true,
10887        get: function get() {
10888          return _vertical;
10889        },
10890        set: function set(value) {
10891          var setting = findDirectionSetting(value); // Have to check for false because the setting an be an empty string.
10892
10893          if (setting === false) {
10894            throw new SyntaxError("An invalid or illegal string was specified.");
10895          }
10896
10897          _vertical = setting;
10898          this.hasBeenReset = true;
10899        }
10900      },
10901      "snapToLines": {
10902        enumerable: true,
10903        get: function get() {
10904          return _snapToLines;
10905        },
10906        set: function set(value) {
10907          _snapToLines = !!value;
10908          this.hasBeenReset = true;
10909        }
10910      },
10911      "line": {
10912        enumerable: true,
10913        get: function get() {
10914          return _line;
10915        },
10916        set: function set(value) {
vendor: 4,585 bytes, lines 10917-11082
10917          if (typeof value !== "number" && value !== autoKeyword) {
10918            throw new SyntaxError("An invalid number or illegal string was specified.");
10919          }
10920
10921          _line = value;
10922          this.hasBeenReset = true;
10923        }
10924      },
10925      "lineAlign": {
10926        enumerable: true,
10927        get: function get() {
10928          return _lineAlign;
10929        },
10930        set: function set(value) {
10931          var setting = findAlignSetting(value);
10932
10933          if (!setting) {
10934            throw new SyntaxError("An invalid or illegal string was specified.");
10935          }
10936
10937          _lineAlign = setting;
10938          this.hasBeenReset = true;
10939        }
10940      },
10941      "position": {
10942        enumerable: true,
10943        get: function get() {
10944          return _position;
10945        },
10946        set: function set(value) {
10947          if (value < 0 || value > 100) {
10948            throw new Error("Position must be between 0 and 100.");
10949          }
10950
10951          _position = value;
10952          this.hasBeenReset = true;
10953        }
10954      },
10955      "positionAlign": {
10956        enumerable: true,
10957        get: function get() {
10958          return _positionAlign;
10959        },
10960        set: function set(value) {
10961          var setting = findAlignSetting(value);
10962
10963          if (!setting) {
10964            throw new SyntaxError("An invalid or illegal string was specified.");
10965          }
10966
10967          _positionAlign = setting;
10968          this.hasBeenReset = true;
10969        }
10970      },
10971      "size": {
10972        enumerable: true,
10973        get: function get() {
10974          return _size;
10975        },
10976        set: function set(value) {
10977          if (value < 0 || value > 100) {
10978            throw new Error("Size must be between 0 and 100.");
10979          }
10980
10981          _size = value;
10982          this.hasBeenReset = true;
10983        }
10984      },
10985      "align": {
10986        enumerable: true,
10987        get: function get() {
10988          return _align;
10989        },
10990        set: function set(value) {
10991          var setting = findAlignSetting(value);
10992
10993          if (!setting) {
10994            throw new SyntaxError("An invalid or illegal string was specified.");
10995          }
10996
10997          _align = setting;
10998          this.hasBeenReset = true;
10999        }
11000      }
11001    });
11002    /**
11003     * Other <track> spec defined properties
11004     */
11005    // http://www.whatwg.org/specs/web-apps/current-work/multipage/the-video-element.html#text-track-cue-display-state
11006
11007    this.displayState = undefined;
11008  }
11009  /**
11010   * VTTCue methods
11011   */
11012
11013
11014  VTTCue.prototype.getCueAsHTML = function () {
11015    // Assume WebVTT.convertCueToDOMTree is on the global.
11016    return WebVTT.convertCueToDOMTree(window, this.text);
11017  };
11018
11019  var vttcue = VTTCue;
11020
11021  /**
11022   * Copyright 2013 vtt.js Contributors
11023   *
11024   * Licensed under the Apache License, Version 2.0 (the "License");
11025   * you may not use this file except in compliance with the License.
11026   * You may obtain a copy of the License at
11027   *
11028   *   http://www.apache.org/licenses/LICENSE-2.0
11029   *
11030   * Unless required by applicable law or agreed to in writing, software
11031   * distributed under the License is distributed on an "AS IS" BASIS,
11032   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11033   * See the License for the specific language governing permissions and
11034   * limitations under the License.
11035   */
11036  var scrollSetting = {
11037    "": true,
11038    "up": true
11039  };
11040
11041  function findScrollSetting(value) {
11042    if (typeof value !== "string") {
11043      return false;
11044    }
11045
11046    var scroll = scrollSetting[value.toLowerCase()];
11047    return scroll ? value.toLowerCase() : false;
11048  }
11049
11050  function isValidPercentValue(value) {
11051    return typeof value === "number" && value >= 0 && value <= 100;
11052  } // VTTRegion shim http://dev.w3.org/html5/webvtt/#vttregion-interface
11053
11054
11055  function VTTRegion() {
11056    var _width = 100;
11057    var _lines = 3;
11058    var _regionAnchorX = 0;
11059    var _regionAnchorY = 100;
11060    var _viewportAnchorX = 0;
11061    var _viewportAnchorY = 100;
11062    var _scroll = "";
11063    Object.defineProperties(this, {
11064      "width": {
11065        enumerable: true,
11066        get: function get() {
11067          return _width;
11068        },
11069        set: function set(value) {
11070          if (!isValidPercentValue(value)) {
11071            throw new Error("Width must be between 0 and 100.");
11072          }
11073
11074          _width = value;
11075        }
11076      },
11077      "lines": {
11078        enumerable: true,
11079        get: function get() {
11080          return _lines;
11081        },
11082        set: function set(value) {
11083          if (typeof value !== "number") {
11084            throw new TypeError("Lines must be set to a number.");
11085          }
11086
11087          _lines = value;
11088        }
11089      },
11090      "regionAnchorY": {
11091        enumerable: true,
11092        get: function get() {
11093          return _regionAnchorY;
11094        },
11095        set: function set(value) {
11096          if (!isValidPercentValue(value)) {
11097            throw new Error("RegionAnchorX must be between 0 and 100.");
11098          }
11099
11100          _regionAnchorY = value;
11101        }
11102      },
11103      "regionAnchorX": {
11104        enumerable: true,
11105        get: function get() {
11106          return _regionAnchorX;
11107        },
11108        set: function set(value) {
11109          if (!isValidPercentValue(value)) {
11110            throw new Error("RegionAnchorY must be between 0 and 100.");
11111          }
11112
11113          _regionAnchorX = value;
11114        }
11115      },
11116      "viewportAnchorY": {
11117        enumerable: true,
11118        get: function get() {
11119          return _viewportAnchorY;
11120        },
11121        set: function set(value) {
11122          if (!isValidPercentValue(value)) {
11123            throw new Error("ViewportAnchorY must be between 0 and 100.");
11124          }
11125
11126          _viewportAnchorY = value;
11127        }
11128      },
11129      "viewportAnchorX": {
11130        enumerable: true,
11131        get: function get() {
11132          return _viewportAnchorX;
11133        },
11134        set: function set(value) {
11135          if (!isValidPercentValue(value)) {
11136            throw new Error("ViewportAnchorX must be between 0 and 100.");
11137          }
11138
11139          _viewportAnchorX = value;
11140        }
11141      },
11142      "scroll": {
11143        enumerable: true,
11144        get: function get() {
11145          return _scroll;
11146        },
11147        set: function set(value) {
11148          var setting = findScrollSetting(value); // Have to check for false as an empty string is a legal value.
11149
11150          if (setting === false) {
11151            throw new SyntaxError("An invalid or illegal string was specified.");
11152          }
11153
11154          _scroll = setting;
11155        }
11156      }
11157    });
11158  }
11159
11160  var vttregion = VTTRegion;
11161
11162  var browserIndex = createCommonjsModule(function (module) {
11163    /**
11164     * Copyright 2013 vtt.js Contributors
11165     *
11166     * Licensed under the Apache License, Version 2.0 (the "License");
11167     * you may not use this file except in compliance with the License.
11168     * You may obtain a copy of the License at
11169     *
11170     *   http://www.apache.org/licenses/LICENSE-2.0
11171     *
11172     * Unless required by applicable law or agreed to in writing, software
11173     * distributed under the License is distributed on an "AS IS" BASIS,
11174     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11175     * See the License for the specific language governing permissions and
11176     * limitations under the License.
11177     */
11178    // Default exports for Node. Export the extended versions of VTTCue and
11179    // VTTRegion in Node since we likely want the capability to convert back and
11180    // forth between JSON. If we don't then it's not that big of a deal since we're
11181    // off browser.
11182    var vttjs = module.exports = {
11183      WebVTT: vtt,
11184      VTTCue: vttcue,
11185      VTTRegion: vttregion
11186    };
11187    window$1.vttjs = vttjs;
11188    window$1.WebVTT = vttjs.WebVTT;
11189    var cueShim = vttjs.VTTCue;
11190    var regionShim = vttjs.VTTRegion;
11191    var nativeVTTCue = window$1.VTTCue;
11192    var nativeVTTRegion = window$1.VTTRegion;
11193
11194    vttjs.shim = function () {
11195      window$1.VTTCue = cueShim;
11196      window$1.VTTRegion = regionShim;
11197    };
11198
11199    vttjs.restore = function () {
11200      window$1.VTTCue = nativeVTTCue;
11201      window$1.VTTRegion = nativeVTTRegion;
11202    };
11203
11204    if (!window$1.VTTCue) {
11205      vttjs.shim();
11206    }
11207  });
11208  var browserIndex_1 = browserIndex.WebVTT;
11209  var browserIndex_2 = browserIndex.VTTCue;
11210  var browserIndex_3 = browserIndex.VTTRegion;
11211
11212  /**
11213   * An Object containing a structure like: `{src: 'url', type: 'mimetype'}` or string
11214   * that just contains the src url alone.
11215   * * `var SourceObject = {src: 'http://ex.com/video.mp4', type: 'video/mp4'};`
11216     * `var SourceString = 'http://example.com/some-video.mp4';`
11217   *
11218   * @typedef {Object|string} Tech~SourceObject
11219   *
11220   * @property {string} src
11221   *           The url to the source
11222   *
11223   * @property {string} type
11224   *           The mime type of the source
11225   */
11226
11227  /**
11228   * A function used by {@link Tech} to create a new {@link TextTrack}.
11229   *
11230   * @private
11231   *
11232   * @param {Tech} self
11233   *        An instance of the Tech class.
11234   *
11235   * @param {string} kind
11236   *        `TextTrack` kind (subtitles, captions, descriptions, chapters, or metadata)
11237   *
11238   * @param {string} [label]
11239   *        Label to identify the text track
11240   *
11241   * @param {string} [language]
11242   *        Two letter language abbreviation
11243   *
11244   * @param {Object} [options={}]
11245   *        An object with additional text track options
11246   *
11247   * @return {TextTrack}
11248   *          The text track that was created.
11249   */
11250
11251  function createTrackHelper(self, kind, label, language, options) {
11252    if (options === void 0) {
11253      options = {};
11254    }
11255
11256    var tracks = self.textTracks();
11257    options.kind = kind;
11258
11259    if (label) {
11260      options.label = label;
11261    }
11262
11263    if (language) {
11264      options.language = language;
11265    }
11266
11267    options.tech = self;
11268    var track = new ALL.text.TrackClass(options);
11269    tracks.addTrack(track);
11270    return track;
11271  }
11272  /**
11273   * This is the base class for media playback technology controllers, such as
11274   * {@link Flash} and {@link HTML5}
11275   *
11276   * @extends Component
11277   */
11278
11279
11280  var Tech =
11281  /*#__PURE__*/
11282  function (_Component) {
11283    _inheritsLoose(Tech, _Component);
11284
11285    /**
11286    * Create an instance of this Tech.
11287    *
11288    * @param {Object} [options]
11289    *        The key/value store of player options.
11290    *
11291    * @param {Component~ReadyCallback} ready
11292    *        Callback function to call when the `HTML5` Tech is ready.
11293    */
11294    function Tech(options, ready) {
11295      var _this;
11296
11297      if (options === void 0) {
11298        options = {};
11299      }
11300
11301      if (ready === void 0) {
11302        ready = function ready() {};
11303      }
11304
11305      // we don't want the tech to report user activity automatically.
vendor: 38,923 bytes, lines 11306-12602
11306      // This is done manually in addControlsListeners
11307      options.reportTouchActivity = false;
11308      _this = _Component.call(this, null, options, ready) || this; // keep track of whether the current source has played at all to
11309      // implement a very limited played()
11310
11311      _this.hasStarted_ = false;
11312
11313      _this.on('playing', function () {
11314        this.hasStarted_ = true;
11315      });
11316
11317      _this.on('loadstart', function () {
11318        this.hasStarted_ = false;
11319      });
11320
11321      ALL.names.forEach(function (name) {
11322        var props = ALL[name];
11323
11324        if (options && options[props.getterName]) {
11325          _this[props.privateName] = options[props.getterName];
11326        }
11327      }); // Manually track progress in cases where the browser/flash player doesn't report it.
11328
11329      if (!_this.featuresProgressEvents) {
11330        _this.manualProgressOn();
11331      } // Manually track timeupdates in cases where the browser/flash player doesn't report it.
11332
11333
11334      if (!_this.featuresTimeupdateEvents) {
11335        _this.manualTimeUpdatesOn();
11336      }
11337
11338      ['Text', 'Audio', 'Video'].forEach(function (track) {
11339        if (options["native" + track + "Tracks"] === false) {
11340          _this["featuresNative" + track + "Tracks"] = false;
11341        }
11342      });
11343
11344      if (options.nativeCaptions === false || options.nativeTextTracks === false) {
11345        _this.featuresNativeTextTracks = false;
11346      } else if (options.nativeCaptions === true || options.nativeTextTracks === true) {
11347        _this.featuresNativeTextTracks = true;
11348      }
11349
11350      if (!_this.featuresNativeTextTracks) {
11351        _this.emulateTextTracks();
11352      }
11353
11354      _this.autoRemoteTextTracks_ = new ALL.text.ListClass();
11355
11356      _this.initTrackListeners(); // Turn on component tap events only if not using native controls
11357
11358
11359      if (!options.nativeControlsForTouch) {
11360        _this.emitTapEvents();
11361      }
11362
11363      if (_this.constructor) {
11364        _this.name_ = _this.constructor.name || 'Unknown Tech';
11365      }
11366
11367      return _this;
11368    }
11369    /**
11370     * A special function to trigger source set in a way that will allow player
11371     * to re-trigger if the player or tech are not ready yet.
11372     *
11373     * @fires Tech#sourceset
11374     * @param {string} src The source string at the time of the source changing.
11375     */
11376
11377
11378    var _proto = Tech.prototype;
11379
11380    _proto.triggerSourceset = function triggerSourceset(src) {
11381      var _this2 = this;
11382
11383      if (!this.isReady_) {
11384        // on initial ready we have to trigger source set
11385        // 1ms after ready so that player can watch for it.
11386        this.one('ready', function () {
11387          return _this2.setTimeout(function () {
11388            return _this2.triggerSourceset(src);
11389          }, 1);
11390        });
11391      }
11392      /**
11393       * Fired when the source is set on the tech causing the media element
11394       * to reload.
11395       *
11396       * @see {@link Player#event:sourceset}
11397       * @event Tech#sourceset
11398       * @type {EventTarget~Event}
11399       */
11400
11401
11402      this.trigger({
11403        src: src,
11404        type: 'sourceset'
11405      });
11406    }
11407    /* Fallbacks for unsupported event types
11408    ================================================================================ */
11409
11410    /**
11411     * Polyfill the `progress` event for browsers that don't support it natively.
11412     *
11413     * @see {@link Tech#trackProgress}
11414     */
11415    ;
11416
11417    _proto.manualProgressOn = function manualProgressOn() {
11418      this.on('durationchange', this.onDurationChange);
11419      this.manualProgress = true; // Trigger progress watching when a source begins loading
11420
11421      this.one('ready', this.trackProgress);
11422    }
11423    /**
11424     * Turn off the polyfill for `progress` events that was created in
11425     * {@link Tech#manualProgressOn}
11426     */
11427    ;
11428
11429    _proto.manualProgressOff = function manualProgressOff() {
11430      this.manualProgress = false;
11431      this.stopTrackingProgress();
11432      this.off('durationchange', this.onDurationChange);
11433    }
11434    /**
11435     * This is used to trigger a `progress` event when the buffered percent changes. It
11436     * sets an interval function that will be called every 500 milliseconds to check if the
11437     * buffer end percent has changed.
11438     *
11439     * > This function is called by {@link Tech#manualProgressOn}
11440     *
11441     * @param {EventTarget~Event} event
11442     *        The `ready` event that caused this to run.
11443     *
11444     * @listens Tech#ready
11445     * @fires Tech#progress
11446     */
11447    ;
11448
11449    _proto.trackProgress = function trackProgress(event) {
11450      this.stopTrackingProgress();
11451      this.progressInterval = this.setInterval(bind(this, function () {
11452        // Don't trigger unless buffered amount is greater than last time
11453        var numBufferedPercent = this.bufferedPercent();
11454
11455        if (this.bufferedPercent_ !== numBufferedPercent) {
11456          /**
11457           * See {@link Player#progress}
11458           *
11459           * @event Tech#progress
11460           * @type {EventTarget~Event}
11461           */
11462          this.trigger('progress');
11463        }
11464
11465        this.bufferedPercent_ = numBufferedPercent;
11466
11467        if (numBufferedPercent === 1) {
11468          this.stopTrackingProgress();
11469        }
11470      }), 500);
11471    }
11472    /**
11473     * Update our internal duration on a `durationchange` event by calling
11474     * {@link Tech#duration}.
11475     *
11476     * @param {EventTarget~Event} event
11477     *        The `durationchange` event that caused this to run.
11478     *
11479     * @listens Tech#durationchange
11480     */
11481    ;
11482
11483    _proto.onDurationChange = function onDurationChange(event) {
11484      this.duration_ = this.duration();
11485    }
11486    /**
11487     * Get and create a `TimeRange` object for buffering.
11488     *
11489     * @return {TimeRange}
11490     *         The time range object that was created.
11491     */
11492    ;
11493
11494    _proto.buffered = function buffered() {
11495      return createTimeRanges(0, 0);
11496    }
11497    /**
11498     * Get the percentage of the current video that is currently buffered.
11499     *
11500     * @return {number}
11501     *         A number from 0 to 1 that represents the decimal percentage of the
11502     *         video that is buffered.
11503     *
11504     */
11505    ;
11506
11507    _proto.bufferedPercent = function bufferedPercent$1() {
11508      return bufferedPercent(this.buffered(), this.duration_);
11509    }
11510    /**
11511     * Turn off the polyfill for `progress` events that was created in
11512     * {@link Tech#manualProgressOn}
11513     * Stop manually tracking progress events by clearing the interval that was set in
11514     * {@link Tech#trackProgress}.
11515     */
11516    ;
11517
11518    _proto.stopTrackingProgress = function stopTrackingProgress() {
11519      this.clearInterval(this.progressInterval);
11520    }
11521    /**
11522     * Polyfill the `timeupdate` event for browsers that don't support it.
11523     *
11524     * @see {@link Tech#trackCurrentTime}
11525     */
11526    ;
11527
11528    _proto.manualTimeUpdatesOn = function manualTimeUpdatesOn() {
11529      this.manualTimeUpdates = true;
11530      this.on('play', this.trackCurrentTime);
11531      this.on('pause', this.stopTrackingCurrentTime);
11532    }
11533    /**
11534     * Turn off the polyfill for `timeupdate` events that was created in
11535     * {@link Tech#manualTimeUpdatesOn}
11536     */
11537    ;
11538
11539    _proto.manualTimeUpdatesOff = function manualTimeUpdatesOff() {
11540      this.manualTimeUpdates = false;
11541      this.stopTrackingCurrentTime();
11542      this.off('play', this.trackCurrentTime);
11543      this.off('pause', this.stopTrackingCurrentTime);
11544    }
11545    /**
11546     * Sets up an interval function to track current time and trigger `timeupdate` every
11547     * 250 milliseconds.
11548     *
11549     * @listens Tech#play
11550     * @triggers Tech#timeupdate
11551     */
11552    ;
11553
11554    _proto.trackCurrentTime = function trackCurrentTime() {
11555      if (this.currentTimeInterval) {
11556        this.stopTrackingCurrentTime();
11557      }
11558
11559      this.currentTimeInterval = this.setInterval(function () {
11560        /**
11561         * Triggered at an interval of 250ms to indicated that time is passing in the video.
11562         *
11563         * @event Tech#timeupdate
11564         * @type {EventTarget~Event}
11565         */
11566        this.trigger({
11567          type: 'timeupdate',
11568          target: this,
11569          manuallyTriggered: true
11570        }); // 42 = 24 fps // 250 is what Webkit uses // FF uses 15
11571      }, 250);
11572    }
11573    /**
11574     * Stop the interval function created in {@link Tech#trackCurrentTime} so that the
11575     * `timeupdate` event is no longer triggered.
11576     *
11577     * @listens {Tech#pause}
11578     */
11579    ;
11580
11581    _proto.stopTrackingCurrentTime = function stopTrackingCurrentTime() {
11582      this.clearInterval(this.currentTimeInterval); // #1002 - if the video ends right before the next timeupdate would happen,
11583      // the progress bar won't make it all the way to the end
11584
11585      this.trigger({
11586        type: 'timeupdate',
11587        target: this,
11588        manuallyTriggered: true
11589      });
11590    }
11591    /**
11592     * Turn off all event polyfills, clear the `Tech`s {@link AudioTrackList},
11593     * {@link VideoTrackList}, and {@link TextTrackList}, and dispose of this Tech.
11594     *
11595     * @fires Component#dispose
11596     */
11597    ;
11598
11599    _proto.dispose = function dispose() {
11600      // clear out all tracks because we can't reuse them between techs
11601      this.clearTracks(NORMAL.names); // Turn off any manual progress or timeupdate tracking
11602
11603      if (this.manualProgress) {
11604        this.manualProgressOff();
11605      }
11606
11607      if (this.manualTimeUpdates) {
11608        this.manualTimeUpdatesOff();
11609      }
11610
11611      _Component.prototype.dispose.call(this);
11612    }
11613    /**
11614     * Clear out a single `TrackList` or an array of `TrackLists` given their names.
11615     *
11616     * > Note: Techs without source handlers should call this between sources for `video`
11617     *         & `audio` tracks. You don't want to use them between tracks!
11618     *
11619     * @param {string[]|string} types
11620     *        TrackList names to clear, valid names are `video`, `audio`, and
11621     *        `text`.
11622     */
11623    ;
11624
11625    _proto.clearTracks = function clearTracks(types) {
11626      var _this3 = this;
11627
11628      types = [].concat(types); // clear out all tracks because we can't reuse them between techs
11629
11630      types.forEach(function (type) {
11631        var list = _this3[type + "Tracks"]() || [];
11632        var i = list.length;
11633
11634        while (i--) {
11635          var track = list[i];
11636
11637          if (type === 'text') {
11638            _this3.removeRemoteTextTrack(track);
11639          }
11640
11641          list.removeTrack(track);
11642        }
11643      });
11644    }
11645    /**
11646     * Remove any TextTracks added via addRemoteTextTrack that are
11647     * flagged for automatic garbage collection
11648     */
11649    ;
11650
11651    _proto.cleanupAutoTextTracks = function cleanupAutoTextTracks() {
11652      var list = this.autoRemoteTextTracks_ || [];
11653      var i = list.length;
11654
11655      while (i--) {
11656        var track = list[i];
11657        this.removeRemoteTextTrack(track);
11658      }
11659    }
11660    /**
11661     * Reset the tech, which will removes all sources and reset the internal readyState.
11662     *
11663     * @abstract
11664     */
11665    ;
11666
11667    _proto.reset = function reset() {}
11668    /**
11669     * Get or set an error on the Tech.
11670     *
11671     * @param {MediaError} [err]
11672     *        Error to set on the Tech
11673     *
11674     * @return {MediaError|null}
11675     *         The current error object on the tech, or null if there isn't one.
11676     */
11677    ;
11678
11679    _proto.error = function error(err) {
11680      if (err !== undefined) {
11681        this.error_ = new MediaError(err);
11682        this.trigger('error');
11683      }
11684
11685      return this.error_;
11686    }
11687    /**
11688     * Returns the `TimeRange`s that have been played through for the current source.
11689     *
11690     * > NOTE: This implementation is incomplete. It does not track the played `TimeRange`.
11691     *         It only checks whether the source has played at all or not.
11692     *
11693     * @return {TimeRange}
11694     *         - A single time range if this video has played
11695     *         - An empty set of ranges if not.
11696     */
11697    ;
11698
11699    _proto.played = function played() {
11700      if (this.hasStarted_) {
11701        return createTimeRanges(0, 0);
11702      }
11703
11704      return createTimeRanges();
11705    }
11706    /**
11707     * Causes a manual time update to occur if {@link Tech#manualTimeUpdatesOn} was
11708     * previously called.
11709     *
11710     * @fires Tech#timeupdate
11711     */
11712    ;
11713
11714    _proto.setCurrentTime = function setCurrentTime() {
11715      // improve the accuracy of manual timeupdates
11716      if (this.manualTimeUpdates) {
11717        /**
11718         * A manual `timeupdate` event.
11719         *
11720         * @event Tech#timeupdate
11721         * @type {EventTarget~Event}
11722         */
11723        this.trigger({
11724          type: 'timeupdate',
11725          target: this,
11726          manuallyTriggered: true
11727        });
11728      }
11729    }
11730    /**
11731     * Turn on listeners for {@link VideoTrackList}, {@link {AudioTrackList}, and
11732     * {@link TextTrackList} events.
11733     *
11734     * This adds {@link EventTarget~EventListeners} for `addtrack`, and  `removetrack`.
11735     *
11736     * @fires Tech#audiotrackchange
11737     * @fires Tech#videotrackchange
11738     * @fires Tech#texttrackchange
11739     */
11740    ;
11741
11742    _proto.initTrackListeners = function initTrackListeners() {
11743      var _this4 = this;
11744
11745      /**
11746        * Triggered when tracks are added or removed on the Tech {@link AudioTrackList}
11747        *
11748        * @event Tech#audiotrackchange
11749        * @type {EventTarget~Event}
11750        */
11751
11752      /**
11753        * Triggered when tracks are added or removed on the Tech {@link VideoTrackList}
11754        *
11755        * @event Tech#videotrackchange
11756        * @type {EventTarget~Event}
11757        */
11758
11759      /**
11760        * Triggered when tracks are added or removed on the Tech {@link TextTrackList}
11761        *
11762        * @event Tech#texttrackchange
11763        * @type {EventTarget~Event}
11764        */
11765      NORMAL.names.forEach(function (name) {
11766        var props = NORMAL[name];
11767
11768        var trackListChanges = function trackListChanges() {
11769          _this4.trigger(name + "trackchange");
11770        };
11771
11772        var tracks = _this4[props.getterName]();
11773
11774        tracks.addEventListener('removetrack', trackListChanges);
11775        tracks.addEventListener('addtrack', trackListChanges);
11776
11777        _this4.on('dispose', function () {
11778          tracks.removeEventListener('removetrack', trackListChanges);
11779          tracks.removeEventListener('addtrack', trackListChanges);
11780        });
11781      });
11782    }
11783    /**
11784     * Emulate TextTracks using vtt.js if necessary
11785     *
11786     * @fires Tech#vttjsloaded
11787     * @fires Tech#vttjserror
11788     */
11789    ;
11790
11791    _proto.addWebVttScript_ = function addWebVttScript_() {
11792      var _this5 = this;
11793
11794      if (window$1.WebVTT) {
11795        return;
11796      } // Initially, Tech.el_ is a child of a dummy-div wait until the Component system
11797      // signals that the Tech is ready at which point Tech.el_ is part of the DOM
11798      // before inserting the WebVTT script
11799
11800
11801      if (document.body.contains(this.el())) {
11802        // load via require if available and vtt.js script location was not passed in
11803        // as an option. novtt builds will turn the above require call into an empty object
11804        // which will cause this if check to always fail.
11805        if (!this.options_['vtt.js'] && isPlain(browserIndex) && Object.keys(browserIndex).length > 0) {
11806          this.trigger('vttjsloaded');
11807          return;
11808        } // load vtt.js via the script location option or the cdn of no location was
11809        // passed in
11810
11811
11812        var script = document.createElement('script');
11813        script.src = this.options_['vtt.js'] || 'https://vjs.zencdn.net/vttjs/0.14.1/vtt.min.js';
11814
11815        script.onload = function () {
11816          /**
11817           * Fired when vtt.js is loaded.
11818           *
11819           * @event Tech#vttjsloaded
11820           * @type {EventTarget~Event}
11821           */
11822          _this5.trigger('vttjsloaded');
11823        };
11824
11825        script.onerror = function () {
11826          /**
11827           * Fired when vtt.js was not loaded due to an error
11828           *
11829           * @event Tech#vttjsloaded
11830           * @type {EventTarget~Event}
11831           */
11832          _this5.trigger('vttjserror');
11833        };
11834
11835        this.on('dispose', function () {
11836          script.onload = null;
11837          script.onerror = null;
11838        }); // but have not loaded yet and we set it to true before the inject so that
11839        // we don't overwrite the injected window.WebVTT if it loads right away
11840
11841        window$1.WebVTT = true;
11842        this.el().parentNode.appendChild(script);
11843      } else {
11844        this.ready(this.addWebVttScript_);
11845      }
11846    }
11847    /**
11848     * Emulate texttracks
11849     *
11850     */
11851    ;
11852
11853    _proto.emulateTextTracks = function emulateTextTracks() {
11854      var _this6 = this;
11855
11856      var tracks = this.textTracks();
11857      var remoteTracks = this.remoteTextTracks();
11858
11859      var handleAddTrack = function handleAddTrack(e) {
11860        return tracks.addTrack(e.track);
11861      };
11862
11863      var handleRemoveTrack = function handleRemoveTrack(e) {
11864        return tracks.removeTrack(e.track);
11865      };
11866
11867      remoteTracks.on('addtrack', handleAddTrack);
11868      remoteTracks.on('removetrack', handleRemoveTrack);
11869      this.addWebVttScript_();
11870
11871      var updateDisplay = function updateDisplay() {
11872        return _this6.trigger('texttrackchange');
11873      };
11874
11875      var textTracksChanges = function textTracksChanges() {
11876        updateDisplay();
11877
11878        for (var i = 0; i < tracks.length; i++) {
11879          var track = tracks[i];
11880          track.removeEventListener('cuechange', updateDisplay);
11881
11882          if (track.mode === 'showing') {
11883            track.addEventListener('cuechange', updateDisplay);
11884          }
11885        }
11886      };
11887
11888      textTracksChanges();
11889      tracks.addEventListener('change', textTracksChanges);
11890      tracks.addEventListener('addtrack', textTracksChanges);
11891      tracks.addEventListener('removetrack', textTracksChanges);
11892      this.on('dispose', function () {
11893        remoteTracks.off('addtrack', handleAddTrack);
11894        remoteTracks.off('removetrack', handleRemoveTrack);
11895        tracks.removeEventListener('change', textTracksChanges);
11896        tracks.removeEventListener('addtrack', textTracksChanges);
11897        tracks.removeEventListener('removetrack', textTracksChanges);
11898
11899        for (var i = 0; i < tracks.length; i++) {
11900          var track = tracks[i];
11901          track.removeEventListener('cuechange', updateDisplay);
11902        }
11903      });
11904    }
11905    /**
11906     * Create and returns a remote {@link TextTrack} object.
11907     *
11908     * @param {string} kind
11909     *        `TextTrack` kind (subtitles, captions, descriptions, chapters, or metadata)
11910     *
11911     * @param {string} [label]
11912     *        Label to identify the text track
11913     *
11914     * @param {string} [language]
11915     *        Two letter language abbreviation
11916     *
11917     * @return {TextTrack}
11918     *         The TextTrack that gets created.
11919     */
11920    ;
11921
11922    _proto.addTextTrack = function addTextTrack(kind, label, language) {
11923      if (!kind) {
11924        throw new Error('TextTrack kind is required but was not provided');
11925      }
11926
11927      return createTrackHelper(this, kind, label, language);
11928    }
11929    /**
11930     * Create an emulated TextTrack for use by addRemoteTextTrack
11931     *
11932     * This is intended to be overridden by classes that inherit from
11933     * Tech in order to create native or custom TextTracks.
11934     *
11935     * @param {Object} options
11936     *        The object should contain the options to initialize the TextTrack with.
11937     *
11938     * @param {string} [options.kind]
11939     *        `TextTrack` kind (subtitles, captions, descriptions, chapters, or metadata).
11940     *
11941     * @param {string} [options.label].
11942     *        Label to identify the text track
11943     *
11944     * @param {string} [options.language]
11945     *        Two letter language abbreviation.
11946     *
11947     * @return {HTMLTrackElement}
11948     *         The track element that gets created.
11949     */
11950    ;
11951
11952    _proto.createRemoteTextTrack = function createRemoteTextTrack(options) {
11953      var track = mergeOptions(options, {
11954        tech: this
11955      });
11956      return new REMOTE.remoteTextEl.TrackClass(track);
11957    }
11958    /**
11959     * Creates a remote text track object and returns an html track element.
11960     *
11961     * > Note: This can be an emulated {@link HTMLTrackElement} or a native one.
11962     *
11963     * @param {Object} options
11964     *        See {@link Tech#createRemoteTextTrack} for more detailed properties.
11965     *
11966     * @param {boolean} [manualCleanup=true]
11967     *        - When false: the TextTrack will be automatically removed from the video
11968     *          element whenever the source changes
11969     *        - When True: The TextTrack will have to be cleaned up manually
11970     *
11971     * @return {HTMLTrackElement}
11972     *         An Html Track Element.
11973     *
11974     * @deprecated The default functionality for this function will be equivalent
11975     *             to "manualCleanup=false" in the future. The manualCleanup parameter will
11976     *             also be removed.
11977     */
11978    ;
11979
11980    _proto.addRemoteTextTrack = function addRemoteTextTrack(options, manualCleanup) {
11981      var _this7 = this;
11982
11983      if (options === void 0) {
11984        options = {};
11985      }
11986
11987      var htmlTrackElement = this.createRemoteTextTrack(options);
11988
11989      if (manualCleanup !== true && manualCleanup !== false) {
11990        // deprecation warning
11991        log.warn('Calling addRemoteTextTrack without explicitly setting the "manualCleanup" parameter to `true` is deprecated and default to `false` in future version of video.js');
11992        manualCleanup = true;
11993      } // store HTMLTrackElement and TextTrack to remote list
11994
11995
11996      this.remoteTextTrackEls().addTrackElement_(htmlTrackElement);
11997      this.remoteTextTracks().addTrack(htmlTrackElement.track);
11998
11999      if (manualCleanup !== true) {
12000        // create the TextTrackList if it doesn't exist
12001        this.ready(function () {
12002          return _this7.autoRemoteTextTracks_.addTrack(htmlTrackElement.track);
12003        });
12004      }
12005
12006      return htmlTrackElement;
12007    }
12008    /**
12009     * Remove a remote text track from the remote `TextTrackList`.
12010     *
12011     * @param {TextTrack} track
12012     *        `TextTrack` to remove from the `TextTrackList`
12013     */
12014    ;
12015
12016    _proto.removeRemoteTextTrack = function removeRemoteTextTrack(track) {
12017      var trackElement = this.remoteTextTrackEls().getTrackElementByTrack_(track); // remove HTMLTrackElement and TextTrack from remote list
12018
12019      this.remoteTextTrackEls().removeTrackElement_(trackElement);
12020      this.remoteTextTracks().removeTrack(track);
12021      this.autoRemoteTextTracks_.removeTrack(track);
12022    }
12023    /**
12024     * Gets available media playback quality metrics as specified by the W3C's Media
12025     * Playback Quality API.
12026     *
12027     * @see [Spec]{@link https://wicg.github.io/media-playback-quality}
12028     *
12029     * @return {Object}
12030     *         An object with supported media playback quality metrics
12031     *
12032     * @abstract
12033     */
12034    ;
12035
12036    _proto.getVideoPlaybackQuality = function getVideoPlaybackQuality() {
12037      return {};
12038    }
12039    /**
12040     * Attempt to create a floating video window always on top of other windows
12041     * so that users may continue consuming media while they interact with other
12042     * content sites, or applications on their device.
12043     *
12044     * @see [Spec]{@link https://wicg.github.io/picture-in-picture}
12045     *
12046     * @return {Promise|undefined}
12047     *         A promise with a Picture-in-Picture window if the browser supports
12048     *         Promises (or one was passed in as an option). It returns undefined
12049     *         otherwise.
12050     *
12051     * @abstract
12052     */
12053    ;
12054
12055    _proto.requestPictureInPicture = function requestPictureInPicture() {
12056      var PromiseClass = this.options_.Promise || window$1.Promise;
12057
12058      if (PromiseClass) {
12059        return PromiseClass.reject();
12060      }
12061    }
12062    /**
12063     * A method to set a poster from a `Tech`.
12064     *
12065     * @abstract
12066     */
12067    ;
12068
12069    _proto.setPoster = function setPoster() {}
12070    /**
12071     * A method to check for the presence of the 'playsinline' <video> attribute.
12072     *
12073     * @abstract
12074     */
12075    ;
12076
12077    _proto.playsinline = function playsinline() {}
12078    /**
12079     * A method to set or unset the 'playsinline' <video> attribute.
12080     *
12081     * @abstract
12082     */
12083    ;
12084
12085    _proto.setPlaysinline = function setPlaysinline() {}
12086    /**
12087     * Attempt to force override of native audio tracks.
12088     *
12089     * @param {boolean} override - If set to true native audio will be overridden,
12090     * otherwise native audio will potentially be used.
12091     *
12092     * @abstract
12093     */
12094    ;
12095
12096    _proto.overrideNativeAudioTracks = function overrideNativeAudioTracks() {}
12097    /**
12098     * Attempt to force override of native video tracks.
12099     *
12100     * @param {boolean} override - If set to true native video will be overridden,
12101     * otherwise native video will potentially be used.
12102     *
12103     * @abstract
12104     */
12105    ;
12106
12107    _proto.overrideNativeVideoTracks = function overrideNativeVideoTracks() {}
12108    /*
12109     * Check if the tech can support the given mime-type.
12110     *
12111     * The base tech does not support any type, but source handlers might
12112     * overwrite this.
12113     *
12114     * @param  {string} type
12115     *         The mimetype to check for support
12116     *
12117     * @return {string}
12118     *         'probably', 'maybe', or empty string
12119     *
12120     * @see [Spec]{@link https://developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement/canPlayType}
12121     *
12122     * @abstract
12123     */
12124    ;
12125
12126    _proto.canPlayType = function canPlayType() {
12127      return '';
12128    }
12129    /**
12130     * Check if the type is supported by this tech.
12131     *
12132     * The base tech does not support any type, but source handlers might
12133     * overwrite this.
12134     *
12135     * @param {string} type
12136     *        The media type to check
12137     * @return {string} Returns the native video element's response
12138     */
12139    ;
12140
12141    Tech.canPlayType = function canPlayType() {
12142      return '';
12143    }
12144    /**
12145     * Check if the tech can support the given source
12146     *
12147     * @param {Object} srcObj
12148     *        The source object
12149     * @param {Object} options
12150     *        The options passed to the tech
12151     * @return {string} 'probably', 'maybe', or '' (empty string)
12152     */
12153    ;
12154
12155    Tech.canPlaySource = function canPlaySource(srcObj, options) {
12156      return Tech.canPlayType(srcObj.type);
12157    }
12158    /*
12159     * Return whether the argument is a Tech or not.
12160     * Can be passed either a Class like `Html5` or a instance like `player.tech_`
12161     *
12162     * @param {Object} component
12163     *        The item to check
12164     *
12165     * @return {boolean}
12166     *         Whether it is a tech or not
12167     *         - True if it is a tech
12168     *         - False if it is not
12169     */
12170    ;
12171
12172    Tech.isTech = function isTech(component) {
12173      return component.prototype instanceof Tech || component instanceof Tech || component === Tech;
12174    }
12175    /**
12176     * Registers a `Tech` into a shared list for videojs.
12177     *
12178     * @param {string} name
12179     *        Name of the `Tech` to register.
12180     *
12181     * @param {Object} tech
12182     *        The `Tech` class to register.
12183     */
12184    ;
12185
12186    Tech.registerTech = function registerTech(name, tech) {
12187      if (!Tech.techs_) {
12188        Tech.techs_ = {};
12189      }
12190
12191      if (!Tech.isTech(tech)) {
12192        throw new Error("Tech " + name + " must be a Tech");
12193      }
12194
12195      if (!Tech.canPlayType) {
12196        throw new Error('Techs must have a static canPlayType method on them');
12197      }
12198
12199      if (!Tech.canPlaySource) {
12200        throw new Error('Techs must have a static canPlaySource method on them');
12201      }
12202
12203      name = toTitleCase(name);
12204      Tech.techs_[name] = tech;
12205      Tech.techs_[toLowerCase(name)] = tech;
12206
12207      if (name !== 'Tech') {
12208        // camel case the techName for use in techOrder
12209        Tech.defaultTechOrder_.push(name);
12210      }
12211
12212      return tech;
12213    }
12214    /**
12215     * Get a `Tech` from the shared list by name.
12216     *
12217     * @param {string} name
12218     *        `camelCase` or `TitleCase` name of the Tech to get
12219     *
12220     * @return {Tech|undefined}
12221     *         The `Tech` or undefined if there was no tech with the name requested.
12222     */
12223    ;
12224
12225    Tech.getTech = function getTech(name) {
12226      if (!name) {
12227        return;
12228      }
12229
12230      if (Tech.techs_ && Tech.techs_[name]) {
12231        return Tech.techs_[name];
12232      }
12233
12234      name = toTitleCase(name);
12235
12236      if (window$1 && window$1.videojs && window$1.videojs[name]) {
12237        log.warn("The " + name + " tech was added to the videojs object when it should be registered using videojs.registerTech(name, tech)");
12238        return window$1.videojs[name];
12239      }
12240    };
12241
12242    return Tech;
12243  }(Component);
12244  /**
12245   * Get the {@link VideoTrackList}
12246   *
12247   * @returns {VideoTrackList}
12248   * @method Tech.prototype.videoTracks
12249   */
12250
12251  /**
12252   * Get the {@link AudioTrackList}
12253   *
12254   * @returns {AudioTrackList}
12255   * @method Tech.prototype.audioTracks
12256   */
12257
12258  /**
12259   * Get the {@link TextTrackList}
12260   *
12261   * @returns {TextTrackList}
12262   * @method Tech.prototype.textTracks
12263   */
12264
12265  /**
12266   * Get the remote element {@link TextTrackList}
12267   *
12268   * @returns {TextTrackList}
12269   * @method Tech.prototype.remoteTextTracks
12270   */
12271
12272  /**
12273   * Get the remote element {@link HtmlTrackElementList}
12274   *
12275   * @returns {HtmlTrackElementList}
12276   * @method Tech.prototype.remoteTextTrackEls
12277   */
12278
12279
12280  ALL.names.forEach(function (name) {
12281    var props = ALL[name];
12282
12283    Tech.prototype[props.getterName] = function () {
12284      this[props.privateName] = this[props.privateName] || new props.ListClass();
12285      return this[props.privateName];
12286    };
12287  });
12288  /**
12289   * List of associated text tracks
12290   *
12291   * @type {TextTrackList}
12292   * @private
12293   * @property Tech#textTracks_
12294   */
12295
12296  /**
12297   * List of associated audio tracks.
12298   *
12299   * @type {AudioTrackList}
12300   * @private
12301   * @property Tech#audioTracks_
12302   */
12303
12304  /**
12305   * List of associated video tracks.
12306   *
12307   * @type {VideoTrackList}
12308   * @private
12309   * @property Tech#videoTracks_
12310   */
12311
12312  /**
12313   * Boolean indicating whether the `Tech` supports volume control.
12314   *
12315   * @type {boolean}
12316   * @default
12317   */
12318
12319  Tech.prototype.featuresVolumeControl = true;
12320  /**
12321   * Boolean indicating whether the `Tech` supports muting volume.
12322   *
12323   * @type {bolean}
12324   * @default
12325   */
12326
12327  Tech.prototype.featuresMuteControl = true;
12328  /**
12329   * Boolean indicating whether the `Tech` supports fullscreen resize control.
12330   * Resizing plugins using request fullscreen reloads the plugin
12331   *
12332   * @type {boolean}
12333   * @default
12334   */
12335
12336  Tech.prototype.featuresFullscreenResize = false;
12337  /**
12338   * Boolean indicating whether the `Tech` supports changing the speed at which the video
12339   * plays. Examples:
12340   *   - Set player to play 2x (twice) as fast
12341   *   - Set player to play 0.5x (half) as fast
12342   *
12343   * @type {boolean}
12344   * @default
12345   */
12346
12347  Tech.prototype.featuresPlaybackRate = false;
12348  /**
12349   * Boolean indicating whether the `Tech` supports the `progress` event. This is currently
12350   * not triggered by video-js-swf. This will be used to determine if
12351   * {@link Tech#manualProgressOn} should be called.
12352   *
12353   * @type {boolean}
12354   * @default
12355   */
12356
12357  Tech.prototype.featuresProgressEvents = false;
12358  /**
12359   * Boolean indicating whether the `Tech` supports the `sourceset` event.
12360   *
12361   * A tech should set this to `true` and then use {@link Tech#triggerSourceset}
12362   * to trigger a {@link Tech#event:sourceset} at the earliest time after getting
12363   * a new source.
12364   *
12365   * @type {boolean}
12366   * @default
12367   */
12368
12369  Tech.prototype.featuresSourceset = false;
12370  /**
12371   * Boolean indicating whether the `Tech` supports the `timeupdate` event. This is currently
12372   * not triggered by video-js-swf. This will be used to determine if
12373   * {@link Tech#manualTimeUpdates} should be called.
12374   *
12375   * @type {boolean}
12376   * @default
12377   */
12378
12379  Tech.prototype.featuresTimeupdateEvents = false;
12380  /**
12381   * Boolean indicating whether the `Tech` supports the native `TextTrack`s.
12382   * This will help us integrate with native `TextTrack`s if the browser supports them.
12383   *
12384   * @type {boolean}
12385   * @default
12386   */
12387
12388  Tech.prototype.featuresNativeTextTracks = false;
12389  /**
12390   * A functional mixin for techs that want to use the Source Handler pattern.
12391   * Source handlers are scripts for handling specific formats.
12392   * The source handler pattern is used for adaptive formats (HLS, DASH) that
12393   * manually load video data and feed it into a Source Buffer (Media Source Extensions)
12394   * Example: `Tech.withSourceHandlers.call(MyTech);`
12395   *
12396   * @param {Tech} _Tech
12397   *        The tech to add source handler functions to.
12398   *
12399   * @mixes Tech~SourceHandlerAdditions
12400   */
12401
12402  Tech.withSourceHandlers = function (_Tech) {
12403    /**
12404     * Register a source handler
12405     *
12406     * @param {Function} handler
12407     *        The source handler class
12408     *
12409     * @param {number} [index]
12410     *        Register it at the following index
12411     */
12412    _Tech.registerSourceHandler = function (handler, index) {
12413      var handlers = _Tech.sourceHandlers;
12414
12415      if (!handlers) {
12416        handlers = _Tech.sourceHandlers = [];
12417      }
12418
12419      if (index === undefined) {
12420        // add to the end of the list
12421        index = handlers.length;
12422      }
12423
12424      handlers.splice(index, 0, handler);
12425    };
12426    /**
12427     * Check if the tech can support the given type. Also checks the
12428     * Techs sourceHandlers.
12429     *
12430     * @param {string} type
12431     *         The mimetype to check.
12432     *
12433     * @return {string}
12434     *         'probably', 'maybe', or '' (empty string)
12435     */
12436
12437
12438    _Tech.canPlayType = function (type) {
12439      var handlers = _Tech.sourceHandlers || [];
12440      var can;
12441
12442      for (var i = 0; i < handlers.length; i++) {
12443        can = handlers[i].canPlayType(type);
12444
12445        if (can) {
12446          return can;
12447        }
12448      }
12449
12450      return '';
12451    };
12452    /**
12453     * Returns the first source handler that supports the source.
12454     *
12455     * TODO: Answer question: should 'probably' be prioritized over 'maybe'
12456     *
12457     * @param {Tech~SourceObject} source
12458     *        The source object
12459     *
12460     * @param {Object} options
12461     *        The options passed to the tech
12462     *
12463     * @return {SourceHandler|null}
12464     *          The first source handler that supports the source or null if
12465     *          no SourceHandler supports the source
12466     */
12467
12468
12469    _Tech.selectSourceHandler = function (source, options) {
12470      var handlers = _Tech.sourceHandlers || [];
12471      var can;
12472
12473      for (var i = 0; i < handlers.length; i++) {
12474        can = handlers[i].canHandleSource(source, options);
12475
12476        if (can) {
12477          return handlers[i];
12478        }
12479      }
12480
12481      return null;
12482    };
12483    /**
12484     * Check if the tech can support the given source.
12485     *
12486     * @param {Tech~SourceObject} srcObj
12487     *        The source object
12488     *
12489     * @param {Object} options
12490     *        The options passed to the tech
12491     *
12492     * @return {string}
12493     *         'probably', 'maybe', or '' (empty string)
12494     */
12495
12496
12497    _Tech.canPlaySource = function (srcObj, options) {
12498      var sh = _Tech.selectSourceHandler(srcObj, options);
12499
12500      if (sh) {
12501        return sh.canHandleSource(srcObj, options);
12502      }
12503
12504      return '';
12505    };
12506    /**
12507     * When using a source handler, prefer its implementation of
12508     * any function normally provided by the tech.
12509     */
12510
12511
12512    var deferrable = ['seekable', 'seeking', 'duration'];
12513    /**
12514     * A wrapper around {@link Tech#seekable} that will call a `SourceHandler`s seekable
12515     * function if it exists, with a fallback to the Techs seekable function.
12516     *
12517     * @method _Tech.seekable
12518     */
12519
12520    /**
12521     * A wrapper around {@link Tech#duration} that will call a `SourceHandler`s duration
12522     * function if it exists, otherwise it will fallback to the techs duration function.
12523     *
12524     * @method _Tech.duration
12525     */
12526
12527    deferrable.forEach(function (fnName) {
12528      var originalFn = this[fnName];
12529
12530      if (typeof originalFn !== 'function') {
12531        return;
12532      }
12533
12534      this[fnName] = function () {
12535        if (this.sourceHandler_ && this.sourceHandler_[fnName]) {
12536          return this.sourceHandler_[fnName].apply(this.sourceHandler_, arguments);
12537        }
12538
12539        return originalFn.apply(this, arguments);
12540      };
12541    }, _Tech.prototype);
12542    /**
12543     * Create a function for setting the source using a source object
12544     * and source handlers.
12545     * Should never be called unless a source handler was found.
12546     *
12547     * @param {Tech~SourceObject} source
12548     *        A source object with src and type keys
12549     */
12550
12551    _Tech.prototype.setSource = function (source) {
12552      var sh = _Tech.selectSourceHandler(source, this.options_);
12553
12554      if (!sh) {
12555        // Fall back to a native source hander when unsupported sources are
12556        // deliberately set
12557        if (_Tech.nativeSourceHandler) {
12558          sh = _Tech.nativeSourceHandler;
12559        } else {
12560          log.error('No source handler found for the current source.');
12561        }
12562      } // Dispose any existing source handler
12563
12564
12565      this.disposeSourceHandler();
12566      this.off('dispose', this.disposeSourceHandler);
12567
12568      if (sh !== _Tech.nativeSourceHandler) {
12569        this.currentSource_ = source;
12570      }
12571
12572      this.sourceHandler_ = sh.handleSource(source, this, this.options_);
12573      this.one('dispose', this.disposeSourceHandler);
12574    };
12575    /**
12576     * Clean up any existing SourceHandlers and listeners when the Tech is disposed.
12577     *
12578     * @listens Tech#dispose
12579     */
12580
12581
12582    _Tech.prototype.disposeSourceHandler = function () {
12583      // if we have a source and get another one
12584      // then we are loading something new
12585      // than clear all of our current tracks
12586      if (this.currentSource_) {
12587        this.clearTracks(['audio', 'video']);
12588        this.currentSource_ = null;
12589      } // always clean up auto-text tracks
12590
12591
12592      this.cleanupAutoTextTracks();
12593
12594      if (this.sourceHandler_) {
12595        if (this.sourceHandler_.dispose) {
12596          this.sourceHandler_.dispose();
12597        }
12598
12599        this.sourceHandler_ = null;
12600      }
12601    };
12602  };
vendor: 4,491 bytes, lines 12602-12752
12602 // The base Tech class needs to be registered as a Component. It is the only
12603  // Tech that can be registered as a Component.
12604
12605
12606  Component.registerComponent('Tech', Tech);
12607  Tech.registerTech('Tech', Tech);
12608  /**
12609   * A list of techs that should be added to techOrder on Players
12610   *
12611   * @private
12612   */
12613
12614  Tech.defaultTechOrder_ = [];
12615
12616  /**
12617   * @file middleware.js
12618   * @module middleware
12619   */
12620  var middlewares = {};
12621  var middlewareInstances = {};
12622  var TERMINATOR = {};
12623  /**
12624   * A middleware object is a plain JavaScript object that has methods that
12625   * match the {@link Tech} methods found in the lists of allowed
12626   * {@link module:middleware.allowedGetters|getters},
12627   * {@link module:middleware.allowedSetters|setters}, and
12628   * {@link module:middleware.allowedMediators|mediators}.
12629   *
12630   * @typedef {Object} MiddlewareObject
12631   */
12632
12633  /**
12634   * A middleware factory function that should return a
12635   * {@link module:middleware~MiddlewareObject|MiddlewareObject}.
12636   *
12637   * This factory will be called for each player when needed, with the player
12638   * passed in as an argument.
12639   *
12640   * @callback MiddlewareFactory
12641   * @param {Player} player
12642   *        A Video.js player.
12643   */
12644
12645  /**
12646   * Define a middleware that the player should use by way of a factory function
12647   * that returns a middleware object.
12648   *
12649   * @param  {string} type
12650   *         The MIME type to match or `"*"` for all MIME types.
12651   *
12652   * @param  {MiddlewareFactory} middleware
12653   *         A middleware factory function that will be executed for
12654   *         matching types.
12655   */
12656
12657  function use(type, middleware) {
12658    middlewares[type] = middlewares[type] || [];
12659    middlewares[type].push(middleware);
12660  }
12661  /**
12662   * Asynchronously sets a source using middleware by recursing through any
12663   * matching middlewares and calling `setSource` on each, passing along the
12664   * previous returned value each time.
12665   *
12666   * @param  {Player} player
12667   *         A {@link Player} instance.
12668   *
12669   * @param  {Tech~SourceObject} src
12670   *         A source object.
12671   *
12672   * @param  {Function}
12673   *         The next middleware to run.
12674   */
12675
12676  function setSource(player, src, next) {
12677    player.setTimeout(function () {
12678      return setSourceHelper(src, middlewares[src.type], next, player);
12679    }, 1);
12680  }
12681  /**
12682   * When the tech is set, passes the tech to each middleware's `setTech` method.
12683   *
12684   * @param {Object[]} middleware
12685   *        An array of middleware instances.
12686   *
12687   * @param {Tech} tech
12688   *        A Video.js tech.
12689   */
12690
12691  function setTech(middleware, tech) {
12692    middleware.forEach(function (mw) {
12693      return mw.setTech && mw.setTech(tech);
12694    });
12695  }
12696  /**
12697   * Calls a getter on the tech first, through each middleware
12698   * from right to left to the player.
12699   *
12700   * @param  {Object[]} middleware
12701   *         An array of middleware instances.
12702   *
12703   * @param  {Tech} tech
12704   *         The current tech.
12705   *
12706   * @param  {string} method
12707   *         A method name.
12708   *
12709   * @return {Mixed}
12710   *         The final value from the tech after middleware has intercepted it.
12711   */
12712
12713  function get(middleware, tech, method) {
12714    return middleware.reduceRight(middlewareIterator(method), tech[method]());
12715  }
12716  /**
12717   * Takes the argument given to the player and calls the setter method on each
12718   * middleware from left to right to the tech.
12719   *
12720   * @param  {Object[]} middleware
12721   *         An array of middleware instances.
12722   *
12723   * @param  {Tech} tech
12724   *         The current tech.
12725   *
12726   * @param  {string} method
12727   *         A method name.
12728   *
12729   * @param  {Mixed} arg
12730   *         The value to set on the tech.
12731   *
12732   * @return {Mixed}
12733   *         The return value of the `method` of the `tech`.
12734   */
12735
12736  function set(middleware, tech, method, arg) {
12737    return tech[method](middleware.reduce(middlewareIterator(method), arg));
12738  }
12739  /**
12740   * Takes the argument given to the player and calls the `call` version of the
12741   * method on each middleware from left to right.
12742   *
12743   * Then, call the passed in method on the tech and return the result unchanged
12744   * back to the player, through middleware, this time from right to left.
12745   *
12746   * @param  {Object[]} middleware
12747   *         An array of middleware instances.
12748   *
12749   * @param  {Tech} tech
12750   *         The current tech.
12751   *
12752   * @param  {string} method
vendor: 52,193 bytes, lines 12753-14541
12753   *         A method name.
12754   *
12755   * @param  {Mixed} arg
12756   *         The value to set on the tech.
12757   *
12758   * @return {Mixed}
12759   *         The return value of the `method` of the `tech`, regardless of the
12760   *         return values of middlewares.
12761   */
12762
12763  function mediate(middleware, tech, method, arg) {
12764    if (arg === void 0) {
12765      arg = null;
12766    }
12767
12768    var callMethod = 'call' + toTitleCase(method);
12769    var middlewareValue = middleware.reduce(middlewareIterator(callMethod), arg);
12770    var terminated = middlewareValue === TERMINATOR; // deprecated. The `null` return value should instead return TERMINATOR to
12771    // prevent confusion if a techs method actually returns null.
12772
12773    var returnValue = terminated ? null : tech[method](middlewareValue);
12774    executeRight(middleware, method, returnValue, terminated);
12775    return returnValue;
12776  }
12777  /**
12778   * Enumeration of allowed getters where the keys are method names.
12779   *
12780   * @type {Object}
12781   */
12782
12783  var allowedGetters = {
12784    buffered: 1,
12785    currentTime: 1,
12786    duration: 1,
12787    seekable: 1,
12788    played: 1,
12789    paused: 1,
12790    volume: 1
12791  };
12792  /**
12793   * Enumeration of allowed setters where the keys are method names.
12794   *
12795   * @type {Object}
12796   */
12797
12798  var allowedSetters = {
12799    setCurrentTime: 1,
12800    setVolume: 1
12801  };
12802  /**
12803   * Enumeration of allowed mediators where the keys are method names.
12804   *
12805   * @type {Object}
12806   */
12807
12808  var allowedMediators = {
12809    play: 1,
12810    pause: 1
12811  };
12812
12813  function middlewareIterator(method) {
12814    return function (value, mw) {
12815      // if the previous middleware terminated, pass along the termination
12816      if (value === TERMINATOR) {
12817        return TERMINATOR;
12818      }
12819
12820      if (mw[method]) {
12821        return mw[method](value);
12822      }
12823
12824      return value;
12825    };
12826  }
12827
12828  function executeRight(mws, method, value, terminated) {
12829    for (var i = mws.length - 1; i >= 0; i--) {
12830      var mw = mws[i];
12831
12832      if (mw[method]) {
12833        mw[method](terminated, value);
12834      }
12835    }
12836  }
12837  /**
12838   * Clear the middleware cache for a player.
12839   *
12840   * @param  {Player} player
12841   *         A {@link Player} instance.
12842   */
12843
12844
12845  function clearCacheForPlayer(player) {
12846    middlewareInstances[player.id()] = null;
12847  }
12848  /**
12849   * {
12850   *  [playerId]: [[mwFactory, mwInstance], ...]
12851   * }
12852   *
12853   * @private
12854   */
12855
12856  function getOrCreateFactory(player, mwFactory) {
12857    var mws = middlewareInstances[player.id()];
12858    var mw = null;
12859
12860    if (mws === undefined || mws === null) {
12861      mw = mwFactory(player);
12862      middlewareInstances[player.id()] = [[mwFactory, mw]];
12863      return mw;
12864    }
12865
12866    for (var i = 0; i < mws.length; i++) {
12867      var _mws$i = mws[i],
12868          mwf = _mws$i[0],
12869          mwi = _mws$i[1];
12870
12871      if (mwf !== mwFactory) {
12872        continue;
12873      }
12874
12875      mw = mwi;
12876    }
12877
12878    if (mw === null) {
12879      mw = mwFactory(player);
12880      mws.push([mwFactory, mw]);
12881    }
12882
12883    return mw;
12884  }
12885
12886  function setSourceHelper(src, middleware, next, player, acc, lastRun) {
12887    if (src === void 0) {
12888      src = {};
12889    }
12890
12891    if (middleware === void 0) {
12892      middleware = [];
12893    }
12894
12895    if (acc === void 0) {
12896      acc = [];
12897    }
12898
12899    if (lastRun === void 0) {
12900      lastRun = false;
12901    }
12902
12903    var _middleware = middleware,
12904        mwFactory = _middleware[0],
12905        mwrest = _middleware.slice(1); // if mwFactory is a string, then we're at a fork in the road
12906
12907
12908    if (typeof mwFactory === 'string') {
12909      setSourceHelper(src, middlewares[mwFactory], next, player, acc, lastRun); // if we have an mwFactory, call it with the player to get the mw,
12910      // then call the mw's setSource method
12911    } else if (mwFactory) {
12912      var mw = getOrCreateFactory(player, mwFactory); // if setSource isn't present, implicitly select this middleware
12913
12914      if (!mw.setSource) {
12915        acc.push(mw);
12916        return setSourceHelper(src, mwrest, next, player, acc, lastRun);
12917      }
12918
12919      mw.setSource(assign({}, src), function (err, _src) {
12920        // something happened, try the next middleware on the current level
12921        // make sure to use the old src
12922        if (err) {
12923          return setSourceHelper(src, mwrest, next, player, acc, lastRun);
12924        } // we've succeeded, now we need to go deeper
12925
12926
12927        acc.push(mw); // if it's the same type, continue down the current chain
12928        // otherwise, we want to go down the new chain
12929
12930        setSourceHelper(_src, src.type === _src.type ? mwrest : middlewares[_src.type], next, player, acc, lastRun);
12931      });
12932    } else if (mwrest.length) {
12933      setSourceHelper(src, mwrest, next, player, acc, lastRun);
12934    } else if (lastRun) {
12935      next(src, acc);
12936    } else {
12937      setSourceHelper(src, middlewares['*'], next, player, acc, true);
12938    }
12939  }
12940
12941  /**
12942   * Mimetypes
12943   *
12944   * @see http://hul.harvard.edu/ois/////systems/wax/wax-public-help/mimetypes.htm
12945   * @typedef Mimetypes~Kind
12946   * @enum
12947   */
12948
12949  var MimetypesKind = {
12950    opus: 'video/ogg',
12951    ogv: 'video/ogg',
12952    mp4: 'video/mp4',
12953    mov: 'video/mp4',
12954    m4v: 'video/mp4',
12955    mkv: 'video/x-matroska',
12956    m4a: 'audio/mp4',
12957    mp3: 'audio/mpeg',
12958    aac: 'audio/aac',
12959    oga: 'audio/ogg',
12960    m3u8: 'application/x-mpegURL',
12961    jpg: 'image/jpeg',
12962    jpeg: 'image/jpeg',
12963    gif: 'image/gif',
12964    png: 'image/png',
12965    svg: 'image/svg+xml',
12966    webp: 'image/webp'
12967  };
12968  /**
12969   * Get the mimetype of a given src url if possible
12970   *
12971   * @param {string} src
12972   *        The url to the src
12973   *
12974   * @return {string}
12975   *         return the mimetype if it was known or empty string otherwise
12976   */
12977
12978  var getMimetype = function getMimetype(src) {
12979    if (src === void 0) {
12980      src = '';
12981    }
12982
12983    var ext = getFileExtension(src);
12984    var mimetype = MimetypesKind[ext.toLowerCase()];
12985    return mimetype || '';
12986  };
12987  /**
12988   * Find the mime type of a given source string if possible. Uses the player
12989   * source cache.
12990   *
12991   * @param {Player} player
12992   *        The player object
12993   *
12994   * @param {string} src
12995   *        The source string
12996   *
12997   * @return {string}
12998   *         The type that was found
12999   */
13000
13001  var findMimetype = function findMimetype(player, src) {
13002    if (!src) {
13003      return '';
13004    } // 1. check for the type in the `source` cache
13005
13006
13007    if (player.cache_.source.src === src && player.cache_.source.type) {
13008      return player.cache_.source.type;
13009    } // 2. see if we have this source in our `currentSources` cache
13010
13011
13012    var matchingSources = player.cache_.sources.filter(function (s) {
13013      return s.src === src;
13014    });
13015
13016    if (matchingSources.length) {
13017      return matchingSources[0].type;
13018    } // 3. look for the src url in source elements and use the type there
13019
13020
13021    var sources = player.$$('source');
13022
13023    for (var i = 0; i < sources.length; i++) {
13024      var s = sources[i];
13025
13026      if (s.type && s.src && s.src === src) {
13027        return s.type;
13028      }
13029    } // 4. finally fallback to our list of mime types based on src url extension
13030
13031
13032    return getMimetype(src);
13033  };
13034
13035  /**
13036   * @module filter-source
13037   */
13038  /**
13039   * Filter out single bad source objects or multiple source objects in an
13040   * array. Also flattens nested source object arrays into a 1 dimensional
13041   * array of source objects.
13042   *
13043   * @param {Tech~SourceObject|Tech~SourceObject[]} src
13044   *        The src object to filter
13045   *
13046   * @return {Tech~SourceObject[]}
13047   *         An array of sourceobjects containing only valid sources
13048   *
13049   * @private
13050   */
13051
13052  var filterSource = function filterSource(src) {
13053    // traverse array
13054    if (Array.isArray(src)) {
13055      var newsrc = [];
13056      src.forEach(function (srcobj) {
13057        srcobj = filterSource(srcobj);
13058
13059        if (Array.isArray(srcobj)) {
13060          newsrc = newsrc.concat(srcobj);
13061        } else if (isObject(srcobj)) {
13062          newsrc.push(srcobj);
13063        }
13064      });
13065      src = newsrc;
13066    } else if (typeof src === 'string' && src.trim()) {
13067      // convert string into object
13068      src = [fixSource({
13069        src: src
13070      })];
13071    } else if (isObject(src) && typeof src.src === 'string' && src.src && src.src.trim()) {
13072      // src is already valid
13073      src = [fixSource(src)];
13074    } else {
13075      // invalid source, turn it into an empty array
13076      src = [];
13077    }
13078
13079    return src;
13080  };
13081  /**
13082   * Checks src mimetype, adding it when possible
13083   *
13084   * @param {Tech~SourceObject} src
13085   *        The src object to check
13086   * @return {Tech~SourceObject}
13087   *        src Object with known type
13088   */
13089
13090
13091  function fixSource(src) {
13092    if (!src.type) {
13093      var mimetype = getMimetype(src.src);
13094
13095      if (mimetype) {
13096        src.type = mimetype;
13097      }
13098    }
13099
13100    return src;
13101  }
13102
13103  /**
13104   * The `MediaLoader` is the `Component` that decides which playback technology to load
13105   * when a player is initialized.
13106   *
13107   * @extends Component
13108   */
13109
13110  var MediaLoader =
13111  /*#__PURE__*/
13112  function (_Component) {
13113    _inheritsLoose(MediaLoader, _Component);
13114
13115    /**
13116     * Create an instance of this class.
13117     *
13118     * @param {Player} player
13119     *        The `Player` that this class should attach to.
13120     *
13121     * @param {Object} [options]
13122     *        The key/value store of player options.
13123     *
13124     * @param {Component~ReadyCallback} [ready]
13125     *        The function that is run when this component is ready.
13126     */
13127    function MediaLoader(player, options, ready) {
13128      var _this;
13129
13130      // MediaLoader has no element
13131      var options_ = mergeOptions({
13132        createEl: false
13133      }, options);
13134      _this = _Component.call(this, player, options_, ready) || this; // If there are no sources when the player is initialized,
13135      // load the first supported playback technology.
13136
13137      if (!options.playerOptions.sources || options.playerOptions.sources.length === 0) {
13138        for (var i = 0, j = options.playerOptions.techOrder; i < j.length; i++) {
13139          var techName = toTitleCase(j[i]);
13140          var tech = Tech.getTech(techName); // Support old behavior of techs being registered as components.
13141          // Remove once that deprecated behavior is removed.
13142
13143          if (!techName) {
13144            tech = Component.getComponent(techName);
13145          } // Check if the browser supports this technology
13146
13147
13148          if (tech && tech.isSupported()) {
13149            player.loadTech_(techName);
13150            break;
13151          }
13152        }
13153      } else {
13154        // Loop through playback technologies (HTML5, Flash) and check for support.
13155        // Then load the best source.
13156        // A few assumptions here:
13157        //   All playback technologies respect preload false.
13158        player.src(options.playerOptions.sources);
13159      }
13160
13161      return _this;
13162    }
13163
13164    return MediaLoader;
13165  }(Component);
13166
13167  Component.registerComponent('MediaLoader', MediaLoader);
13168
13169  /**
13170   * Component which is clickable or keyboard actionable, but is not a
13171   * native HTML button.
13172   *
13173   * @extends Component
13174   */
13175
13176  var ClickableComponent =
13177  /*#__PURE__*/
13178  function (_Component) {
13179    _inheritsLoose(ClickableComponent, _Component);
13180
13181    /**
13182     * Creates an instance of this class.
13183     *
13184     * @param  {Player} player
13185     *         The `Player` that this class should be attached to.
13186     *
13187     * @param  {Object} [options]
13188     *         The key/value store of player options.
13189     */
13190    function ClickableComponent(player, options) {
13191      var _this;
13192
13193      _this = _Component.call(this, player, options) || this;
13194
13195      _this.emitTapEvents();
13196
13197      _this.enable();
13198
13199      return _this;
13200    }
13201    /**
13202     * Create the `ClickableComponent`s DOM element.
13203     *
13204     * @param {string} [tag=div]
13205     *        The element's node type.
13206     *
13207     * @param {Object} [props={}]
13208     *        An object of properties that should be set on the element.
13209     *
13210     * @param {Object} [attributes={}]
13211     *        An object of attributes that should be set on the element.
13212     *
13213     * @return {Element}
13214     *         The element that gets created.
13215     */
13216
13217
13218    var _proto = ClickableComponent.prototype;
13219
13220    _proto.createEl = function createEl(tag, props, attributes) {
13221      if (tag === void 0) {
13222        tag = 'div';
13223      }
13224
13225      if (props === void 0) {
13226        props = {};
13227      }
13228
13229      if (attributes === void 0) {
13230        attributes = {};
13231      }
13232
13233      props = assign({
13234        innerHTML: '<span aria-hidden="true" class="vjs-icon-placeholder"></span>',
13235        className: this.buildCSSClass(),
13236        tabIndex: 0
13237      }, props);
13238
13239      if (tag === 'button') {
13240        log.error("Creating a ClickableComponent with an HTML element of " + tag + " is not supported; use a Button instead.");
13241      } // Add ARIA attributes for clickable element which is not a native HTML button
13242
13243
13244      attributes = assign({
13245        role: 'button'
13246      }, attributes);
13247      this.tabIndex_ = props.tabIndex;
13248
13249      var el = _Component.prototype.createEl.call(this, tag, props, attributes);
13250
13251      this.createControlTextEl(el);
13252      return el;
13253    };
13254
13255    _proto.dispose = function dispose() {
13256      // remove controlTextEl_ on dispose
13257      this.controlTextEl_ = null;
13258
13259      _Component.prototype.dispose.call(this);
13260    }
13261    /**
13262     * Create a control text element on this `ClickableComponent`
13263     *
13264     * @param {Element} [el]
13265     *        Parent element for the control text.
13266     *
13267     * @return {Element}
13268     *         The control text element that gets created.
13269     */
13270    ;
13271
13272    _proto.createControlTextEl = function createControlTextEl(el) {
13273      this.controlTextEl_ = createEl('span', {
13274        className: 'vjs-control-text'
13275      }, {
13276        // let the screen reader user know that the text of the element may change
13277        'aria-live': 'polite'
13278      });
13279
13280      if (el) {
13281        el.appendChild(this.controlTextEl_);
13282      }
13283
13284      this.controlText(this.controlText_, el);
13285      return this.controlTextEl_;
13286    }
13287    /**
13288     * Get or set the localize text to use for the controls on the `ClickableComponent`.
13289     *
13290     * @param {string} [text]
13291     *        Control text for element.
13292     *
13293     * @param {Element} [el=this.el()]
13294     *        Element to set the title on.
13295     *
13296     * @return {string}
13297     *         - The control text when getting
13298     */
13299    ;
13300
13301    _proto.controlText = function controlText(text, el) {
13302      if (el === void 0) {
13303        el = this.el();
13304      }
13305
13306      if (text === undefined) {
13307        return this.controlText_ || 'Need Text';
13308      }
13309
13310      var localizedText = this.localize(text);
13311      this.controlText_ = text;
13312      textContent(this.controlTextEl_, localizedText);
13313
13314      if (!this.nonIconControl) {
13315        // Set title attribute if only an icon is shown
13316        el.setAttribute('title', localizedText);
13317      }
13318    }
13319    /**
13320     * Builds the default DOM `className`.
13321     *
13322     * @return {string}
13323     *         The DOM `className` for this object.
13324     */
13325    ;
13326
13327    _proto.buildCSSClass = function buildCSSClass() {
13328      return "vjs-control vjs-button " + _Component.prototype.buildCSSClass.call(this);
13329    }
13330    /**
13331     * Enable this `ClickableComponent`
13332     */
13333    ;
13334
13335    _proto.enable = function enable() {
13336      if (!this.enabled_) {
13337        this.enabled_ = true;
13338        this.removeClass('vjs-disabled');
13339        this.el_.setAttribute('aria-disabled', 'false');
13340
13341        if (typeof this.tabIndex_ !== 'undefined') {
13342          this.el_.setAttribute('tabIndex', this.tabIndex_);
13343        }
13344
13345        this.on(['tap', 'click'], this.handleClick);
13346        this.on('keydown', this.handleKeyDown);
13347      }
13348    }
13349    /**
13350     * Disable this `ClickableComponent`
13351     */
13352    ;
13353
13354    _proto.disable = function disable() {
13355      this.enabled_ = false;
13356      this.addClass('vjs-disabled');
13357      this.el_.setAttribute('aria-disabled', 'true');
13358
13359      if (typeof this.tabIndex_ !== 'undefined') {
13360        this.el_.removeAttribute('tabIndex');
13361      }
13362
13363      this.off('mouseover', this.handleMouseOver);
13364      this.off('mouseout', this.handleMouseOut);
13365      this.off(['tap', 'click'], this.handleClick);
13366      this.off('keydown', this.handleKeyDown);
13367    }
13368    /**
13369     * Event handler that is called when a `ClickableComponent` receives a
13370     * `click` or `tap` event.
13371     *
13372     * @param {EventTarget~Event} event
13373     *        The `tap` or `click` event that caused this function to be called.
13374     *
13375     * @listens tap
13376     * @listens click
13377     * @abstract
13378     */
13379    ;
13380
13381    _proto.handleClick = function handleClick(event) {}
13382    /**
13383     * Event handler that is called when a `ClickableComponent` receives a
13384     * `keydown` event.
13385     *
13386     * By default, if the key is Space or Enter, it will trigger a `click` event.
13387     *
13388     * @param {EventTarget~Event} event
13389     *        The `keydown` event that caused this function to be called.
13390     *
13391     * @listens keydown
13392     */
13393    ;
13394
13395    _proto.handleKeyDown = function handleKeyDown(event) {
13396      // Support Space or Enter key operation to fire a click event. Also,
13397      // prevent the event from propagating through the DOM and triggering
13398      // Player hotkeys.
13399      if (keycode.isEventKey(event, 'Space') || keycode.isEventKey(event, 'Enter')) {
13400        event.preventDefault();
13401        event.stopPropagation();
13402        this.trigger('click');
13403      } else {
13404        // Pass keypress handling up for unsupported keys
13405        _Component.prototype.handleKeyDown.call(this, event);
13406      }
13407    };
13408
13409    return ClickableComponent;
13410  }(Component);
13411
13412  Component.registerComponent('ClickableComponent', ClickableComponent);
13413
13414  /**
13415   * A `ClickableComponent` that handles showing the poster image for the player.
13416   *
13417   * @extends ClickableComponent
13418   */
13419
13420  var PosterImage =
13421  /*#__PURE__*/
13422  function (_ClickableComponent) {
13423    _inheritsLoose(PosterImage, _ClickableComponent);
13424
13425    /**
13426     * Create an instance of this class.
13427     *
13428     * @param {Player} player
13429     *        The `Player` that this class should attach to.
13430     *
13431     * @param {Object} [options]
13432     *        The key/value store of player options.
13433     */
13434    function PosterImage(player, options) {
13435      var _this;
13436
13437      _this = _ClickableComponent.call(this, player, options) || this;
13438
13439      _this.update();
13440
13441      player.on('posterchange', bind(_assertThisInitialized(_this), _this.update));
13442      return _this;
13443    }
13444    /**
13445     * Clean up and dispose of the `PosterImage`.
13446     */
13447
13448
13449    var _proto = PosterImage.prototype;
13450
13451    _proto.dispose = function dispose() {
13452      this.player().off('posterchange', this.update);
13453
13454      _ClickableComponent.prototype.dispose.call(this);
13455    }
13456    /**
13457     * Create the `PosterImage`s DOM element.
13458     *
13459     * @return {Element}
13460     *         The element that gets created.
13461     */
13462    ;
13463
13464    _proto.createEl = function createEl$1() {
13465      var el = createEl('div', {
13466        className: 'vjs-poster',
13467        // Don't want poster to be tabbable.
13468        tabIndex: -1
13469      });
13470      return el;
13471    }
13472    /**
13473     * An {@link EventTarget~EventListener} for {@link Player#posterchange} events.
13474     *
13475     * @listens Player#posterchange
13476     *
13477     * @param {EventTarget~Event} [event]
13478     *        The `Player#posterchange` event that triggered this function.
13479     */
13480    ;
13481
13482    _proto.update = function update(event) {
13483      var url = this.player().poster();
13484      this.setSrc(url); // If there's no poster source we should display:none on this component
13485      // so it's not still clickable or right-clickable
13486
13487      if (url) {
13488        this.show();
13489      } else {
13490        this.hide();
13491      }
13492    }
13493    /**
13494     * Set the source of the `PosterImage` depending on the display method.
13495     *
13496     * @param {string} url
13497     *        The URL to the source for the `PosterImage`.
13498     */
13499    ;
13500
13501    _proto.setSrc = function setSrc(url) {
13502      var backgroundImage = ''; // Any falsy value should stay as an empty string, otherwise
13503      // this will throw an extra error
13504
13505      if (url) {
13506        backgroundImage = "url(\"" + url + "\")";
13507      }
13508
13509      this.el_.style.backgroundImage = backgroundImage;
13510    }
13511    /**
13512     * An {@link EventTarget~EventListener} for clicks on the `PosterImage`. See
13513     * {@link ClickableComponent#handleClick} for instances where this will be triggered.
13514     *
13515     * @listens tap
13516     * @listens click
13517     * @listens keydown
13518     *
13519     * @param {EventTarget~Event} event
13520     +        The `click`, `tap` or `keydown` event that caused this function to be called.
13521     */
13522    ;
13523
13524    _proto.handleClick = function handleClick(event) {
13525      // We don't want a click to trigger playback when controls are disabled
13526      if (!this.player_.controls()) {
13527        return;
13528      }
13529
13530      if (this.player_.tech(true)) {
13531        this.player_.tech(true).focus();
13532      }
13533
13534      if (this.player_.paused()) {
13535        silencePromise(this.player_.play());
13536      } else {
13537        this.player_.pause();
13538      }
13539    };
13540
13541    return PosterImage;
13542  }(ClickableComponent);
13543
13544  Component.registerComponent('PosterImage', PosterImage);
13545
13546  var darkGray = '#222';
13547  var lightGray = '#ccc';
13548  var fontMap = {
13549    monospace: 'monospace',
13550    sansSerif: 'sans-serif',
13551    serif: 'serif',
13552    monospaceSansSerif: '"Andale Mono", "Lucida Console", monospace',
13553    monospaceSerif: '"Courier New", monospace',
13554    proportionalSansSerif: 'sans-serif',
13555    proportionalSerif: 'serif',
13556    casual: '"Comic Sans MS", Impact, fantasy',
13557    script: '"Monotype Corsiva", cursive',
13558    smallcaps: '"Andale Mono", "Lucida Console", monospace, sans-serif'
13559  };
13560  /**
13561   * Construct an rgba color from a given hex color code.
13562   *
13563   * @param {number} color
13564   *        Hex number for color, like #f0e or #f604e2.
13565   *
13566   * @param {number} opacity
13567   *        Value for opacity, 0.0 - 1.0.
13568   *
13569   * @return {string}
13570   *         The rgba color that was created, like 'rgba(255, 0, 0, 0.3)'.
13571   */
13572
13573  function constructColor(color, opacity) {
13574    var hex;
13575
13576    if (color.length === 4) {
13577      // color looks like "#f0e"
13578      hex = color[1] + color[1] + color[2] + color[2] + color[3] + color[3];
13579    } else if (color.length === 7) {
13580      // color looks like "#f604e2"
13581      hex = color.slice(1);
13582    } else {
13583      throw new Error('Invalid color code provided, ' + color + '; must be formatted as e.g. #f0e or #f604e2.');
13584    }
13585
13586    return 'rgba(' + parseInt(hex.slice(0, 2), 16) + ',' + parseInt(hex.slice(2, 4), 16) + ',' + parseInt(hex.slice(4, 6), 16) + ',' + opacity + ')';
13587  }
13588  /**
13589   * Try to update the style of a DOM element. Some style changes will throw an error,
13590   * particularly in IE8. Those should be noops.
13591   *
13592   * @param {Element} el
13593   *        The DOM element to be styled.
13594   *
13595   * @param {string} style
13596   *        The CSS property on the element that should be styled.
13597   *
13598   * @param {string} rule
13599   *        The style rule that should be applied to the property.
13600   *
13601   * @private
13602   */
13603
13604  function tryUpdateStyle(el, style, rule) {
13605    try {
13606      el.style[style] = rule;
13607    } catch (e) {
13608      // Satisfies linter.
13609      return;
13610    }
13611  }
13612  /**
13613   * The component for displaying text track cues.
13614   *
13615   * @extends Component
13616   */
13617
13618
13619  var TextTrackDisplay =
13620  /*#__PURE__*/
13621  function (_Component) {
13622    _inheritsLoose(TextTrackDisplay, _Component);
13623
13624    /**
13625     * Creates an instance of this class.
13626     *
13627     * @param {Player} player
13628     *        The `Player` that this class should be attached to.
13629     *
13630     * @param {Object} [options]
13631     *        The key/value store of player options.
13632     *
13633     * @param {Component~ReadyCallback} [ready]
13634     *        The function to call when `TextTrackDisplay` is ready.
13635     */
13636    function TextTrackDisplay(player, options, ready) {
13637      var _this;
13638
13639      _this = _Component.call(this, player, options, ready) || this;
13640      var updateDisplayHandler = bind(_assertThisInitialized(_this), _this.updateDisplay);
13641      player.on('loadstart', bind(_assertThisInitialized(_this), _this.toggleDisplay));
13642      player.on('texttrackchange', updateDisplayHandler);
13643      player.on('loadedmetadata', bind(_assertThisInitialized(_this), _this.preselectTrack)); // This used to be called during player init, but was causing an error
13644      // if a track should show by default and the display hadn't loaded yet.
13645      // Should probably be moved to an external track loader when we support
13646      // tracks that don't need a display.
13647
13648      player.ready(bind(_assertThisInitialized(_this), function () {
13649        if (player.tech_ && player.tech_.featuresNativeTextTracks) {
13650          this.hide();
13651          return;
13652        }
13653
13654        player.on('fullscreenchange', updateDisplayHandler);
13655        player.on('playerresize', updateDisplayHandler);
13656        window$1.addEventListener('orientationchange', updateDisplayHandler);
13657        player.on('dispose', function () {
13658          return window$1.removeEventListener('orientationchange', updateDisplayHandler);
13659        });
13660        var tracks = this.options_.playerOptions.tracks || [];
13661
13662        for (var i = 0; i < tracks.length; i++) {
13663          this.player_.addRemoteTextTrack(tracks[i], true);
13664        }
13665
13666        this.preselectTrack();
13667      }));
13668      return _this;
13669    }
13670    /**
13671    * Preselect a track following this precedence:
13672    * - matches the previously selected {@link TextTrack}'s language and kind
13673    * - matches the previously selected {@link TextTrack}'s language only
13674    * - is the first default captions track
13675    * - is the first default descriptions track
13676    *
13677    * @listens Player#loadstart
13678    */
13679
13680
13681    var _proto = TextTrackDisplay.prototype;
13682
13683    _proto.preselectTrack = function preselectTrack() {
13684      var modes = {
13685        captions: 1,
13686        subtitles: 1
13687      };
13688      var trackList = this.player_.textTracks();
13689      var userPref = this.player_.cache_.selectedLanguage;
13690      var firstDesc;
13691      var firstCaptions;
13692      var preferredTrack;
13693
13694      for (var i = 0; i < trackList.length; i++) {
13695        var track = trackList[i];
13696
13697        if (userPref && userPref.enabled && userPref.language && userPref.language === track.language && track.kind in modes) {
13698          // Always choose the track that matches both language and kind
13699          if (track.kind === userPref.kind) {
13700            preferredTrack = track; // or choose the first track that matches language
13701          } else if (!preferredTrack) {
13702            preferredTrack = track;
13703          } // clear everything if offTextTrackMenuItem was clicked
13704
13705        } else if (userPref && !userPref.enabled) {
13706          preferredTrack = null;
13707          firstDesc = null;
13708          firstCaptions = null;
13709        } else if (track["default"]) {
13710          if (track.kind === 'descriptions' && !firstDesc) {
13711            firstDesc = track;
13712          } else if (track.kind in modes && !firstCaptions) {
13713            firstCaptions = track;
13714          }
13715        }
13716      } // The preferredTrack matches the user preference and takes
13717      // precedence over all the other tracks.
13718      // So, display the preferredTrack before the first default track
13719      // and the subtitles/captions track before the descriptions track
13720
13721
13722      if (preferredTrack) {
13723        preferredTrack.mode = 'showing';
13724      } else if (firstCaptions) {
13725        firstCaptions.mode = 'showing';
13726      } else if (firstDesc) {
13727        firstDesc.mode = 'showing';
13728      }
13729    }
13730    /**
13731     * Turn display of {@link TextTrack}'s from the current state into the other state.
13732     * There are only two states:
13733     * - 'shown'
13734     * - 'hidden'
13735     *
13736     * @listens Player#loadstart
13737     */
13738    ;
13739
13740    _proto.toggleDisplay = function toggleDisplay() {
13741      if (this.player_.tech_ && this.player_.tech_.featuresNativeTextTracks) {
13742        this.hide();
13743      } else {
13744        this.show();
13745      }
13746    }
13747    /**
13748     * Create the {@link Component}'s DOM element.
13749     *
13750     * @return {Element}
13751     *         The element that was created.
13752     */
13753    ;
13754
13755    _proto.createEl = function createEl() {
13756      return _Component.prototype.createEl.call(this, 'div', {
13757        className: 'vjs-text-track-display'
13758      }, {
13759        'aria-live': 'off',
13760        'aria-atomic': 'true'
13761      });
13762    }
13763    /**
13764     * Clear all displayed {@link TextTrack}s.
13765     */
13766    ;
13767
13768    _proto.clearDisplay = function clearDisplay() {
13769      if (typeof window$1.WebVTT === 'function') {
13770        window$1.WebVTT.processCues(window$1, [], this.el_);
13771      }
13772    }
13773    /**
13774     * Update the displayed TextTrack when a either a {@link Player#texttrackchange} or
13775     * a {@link Player#fullscreenchange} is fired.
13776     *
13777     * @listens Player#texttrackchange
13778     * @listens Player#fullscreenchange
13779     */
13780    ;
13781
13782    _proto.updateDisplay = function updateDisplay() {
13783      var tracks = this.player_.textTracks();
13784      var allowMultipleShowingTracks = this.options_.allowMultipleShowingTracks;
13785      this.clearDisplay();
13786
13787      if (allowMultipleShowingTracks) {
13788        var showingTracks = [];
13789
13790        for (var _i = 0; _i < tracks.length; ++_i) {
13791          var track = tracks[_i];
13792
13793          if (track.mode !== 'showing') {
13794            continue;
13795          }
13796
13797          showingTracks.push(track);
13798        }
13799
13800        this.updateForTrack(showingTracks);
13801        return;
13802      } //  Track display prioritization model: if multiple tracks are 'showing',
13803      //  display the first 'subtitles' or 'captions' track which is 'showing',
13804      //  otherwise display the first 'descriptions' track which is 'showing'
13805
13806
13807      var descriptionsTrack = null;
13808      var captionsSubtitlesTrack = null;
13809      var i = tracks.length;
13810
13811      while (i--) {
13812        var _track = tracks[i];
13813
13814        if (_track.mode === 'showing') {
13815          if (_track.kind === 'descriptions') {
13816            descriptionsTrack = _track;
13817          } else {
13818            captionsSubtitlesTrack = _track;
13819          }
13820        }
13821      }
13822
13823      if (captionsSubtitlesTrack) {
13824        if (this.getAttribute('aria-live') !== 'off') {
13825          this.setAttribute('aria-live', 'off');
13826        }
13827
13828        this.updateForTrack(captionsSubtitlesTrack);
13829      } else if (descriptionsTrack) {
13830        if (this.getAttribute('aria-live') !== 'assertive') {
13831          this.setAttribute('aria-live', 'assertive');
13832        }
13833
13834        this.updateForTrack(descriptionsTrack);
13835      }
13836    }
13837    /**
13838     * Style {@Link TextTrack} activeCues according to {@Link TextTrackSettings}.
13839     *
13840     * @param {TextTrack} track
13841     *        Text track object containing active cues to style.
13842     */
13843    ;
13844
13845    _proto.updateDisplayState = function updateDisplayState(track) {
13846      var overrides = this.player_.textTrackSettings.getValues();
13847      var cues = track.activeCues;
13848      var i = cues.length;
13849
13850      while (i--) {
13851        var cue = cues[i];
13852
13853        if (!cue) {
13854          continue;
13855        }
13856
13857        var cueDiv = cue.displayState;
13858
13859        if (overrides.color) {
13860          cueDiv.firstChild.style.color = overrides.color;
13861        }
13862
13863        if (overrides.textOpacity) {
13864          tryUpdateStyle(cueDiv.firstChild, 'color', constructColor(overrides.color || '#fff', overrides.textOpacity));
13865        }
13866
13867        if (overrides.backgroundColor) {
13868          cueDiv.firstChild.style.backgroundColor = overrides.backgroundColor;
13869        }
13870
13871        if (overrides.backgroundOpacity) {
13872          tryUpdateStyle(cueDiv.firstChild, 'backgroundColor', constructColor(overrides.backgroundColor || '#000', overrides.backgroundOpacity));
13873        }
13874
13875        if (overrides.windowColor) {
13876          if (overrides.windowOpacity) {
13877            tryUpdateStyle(cueDiv, 'backgroundColor', constructColor(overrides.windowColor, overrides.windowOpacity));
13878          } else {
13879            cueDiv.style.backgroundColor = overrides.windowColor;
13880          }
13881        }
13882
13883        if (overrides.edgeStyle) {
13884          if (overrides.edgeStyle === 'dropshadow') {
13885            cueDiv.firstChild.style.textShadow = "2px 2px 3px " + darkGray + ", 2px 2px 4px " + darkGray + ", 2px 2px 5px " + darkGray;
13886          } else if (overrides.edgeStyle === 'raised') {
13887            cueDiv.firstChild.style.textShadow = "1px 1px " + darkGray + ", 2px 2px " + darkGray + ", 3px 3px " + darkGray;
13888          } else if (overrides.edgeStyle === 'depressed') {
13889            cueDiv.firstChild.style.textShadow = "1px 1px " + lightGray + ", 0 1px " + lightGray + ", -1px -1px " + darkGray + ", 0 -1px " + darkGray;
13890          } else if (overrides.edgeStyle === 'uniform') {
13891            cueDiv.firstChild.style.textShadow = "0 0 4px " + darkGray + ", 0 0 4px " + darkGray + ", 0 0 4px " + darkGray + ", 0 0 4px " + darkGray;
13892          }
13893        }
13894
13895        if (overrides.fontPercent && overrides.fontPercent !== 1) {
13896          var fontSize = window$1.parseFloat(cueDiv.style.fontSize);
13897          cueDiv.style.fontSize = fontSize * overrides.fontPercent + 'px';
13898          cueDiv.style.height = 'auto';
13899          cueDiv.style.top = 'auto';
13900          cueDiv.style.bottom = '2px';
13901        }
13902
13903        if (overrides.fontFamily && overrides.fontFamily !== 'default') {
13904          if (overrides.fontFamily === 'small-caps') {
13905            cueDiv.firstChild.style.fontVariant = 'small-caps';
13906          } else {
13907            cueDiv.firstChild.style.fontFamily = fontMap[overrides.fontFamily];
13908          }
13909        }
13910      }
13911    }
13912    /**
13913     * Add an {@link TextTrack} to to the {@link Tech}s {@link TextTrackList}.
13914     *
13915     * @param {TextTrack|TextTrack[]} tracks
13916     *        Text track object or text track array to be added to the list.
13917     */
13918    ;
13919
13920    _proto.updateForTrack = function updateForTrack(tracks) {
13921      if (!Array.isArray(tracks)) {
13922        tracks = [tracks];
13923      }
13924
13925      if (typeof window$1.WebVTT !== 'function' || tracks.every(function (track) {
13926        return !track.activeCues;
13927      })) {
13928        return;
13929      }
13930
13931      var cues = []; // push all active track cues
13932
13933      for (var i = 0; i < tracks.length; ++i) {
13934        var track = tracks[i];
13935
13936        for (var j = 0; j < track.activeCues.length; ++j) {
13937          cues.push(track.activeCues[j]);
13938        }
13939      } // removes all cues before it processes new ones
13940
13941
13942      window$1.WebVTT.processCues(window$1, cues, this.el_); // add unique class to each language text track & add settings styling if necessary
13943
13944      for (var _i2 = 0; _i2 < tracks.length; ++_i2) {
13945        var _track2 = tracks[_i2];
13946
13947        for (var _j = 0; _j < _track2.activeCues.length; ++_j) {
13948          var cueEl = _track2.activeCues[_j].displayState;
13949          addClass(cueEl, 'vjs-text-track-cue');
13950          addClass(cueEl, 'vjs-text-track-cue-' + (_track2.language ? _track2.language : _i2));
13951        }
13952
13953        if (this.player_.textTrackSettings) {
13954          this.updateDisplayState(_track2);
13955        }
13956      }
13957    };
13958
13959    return TextTrackDisplay;
13960  }(Component);
13961
13962  Component.registerComponent('TextTrackDisplay', TextTrackDisplay);
13963
13964  /**
13965   * A loading spinner for use during waiting/loading events.
13966   *
13967   * @extends Component
13968   */
13969
13970  var LoadingSpinner =
13971  /*#__PURE__*/
13972  function (_Component) {
13973    _inheritsLoose(LoadingSpinner, _Component);
13974
13975    function LoadingSpinner() {
13976      return _Component.apply(this, arguments) || this;
13977    }
13978
13979    var _proto = LoadingSpinner.prototype;
13980
13981    /**
13982     * Create the `LoadingSpinner`s DOM element.
13983     *
13984     * @return {Element}
13985     *         The dom element that gets created.
13986     */
13987    _proto.createEl = function createEl$1() {
13988      var isAudio = this.player_.isAudio();
13989      var playerType = this.localize(isAudio ? 'Audio Player' : 'Video Player');
13990      var controlText = createEl('span', {
13991        className: 'vjs-control-text',
13992        innerHTML: this.localize('{1} is loading.', [playerType])
13993      });
13994
13995      var el = _Component.prototype.createEl.call(this, 'div', {
13996        className: 'vjs-loading-spinner',
13997        dir: 'ltr'
13998      });
13999
14000      el.appendChild(controlText);
14001      return el;
14002    };
14003
14004    return LoadingSpinner;
14005  }(Component);
14006
14007  Component.registerComponent('LoadingSpinner', LoadingSpinner);
14008
14009  /**
14010   * Base class for all buttons.
14011   *
14012   * @extends ClickableComponent
14013   */
14014
14015  var Button =
14016  /*#__PURE__*/
14017  function (_ClickableComponent) {
14018    _inheritsLoose(Button, _ClickableComponent);
14019
14020    function Button() {
14021      return _ClickableComponent.apply(this, arguments) || this;
14022    }
14023
14024    var _proto = Button.prototype;
14025
14026    /**
14027     * Create the `Button`s DOM element.
14028     *
14029     * @param {string} [tag="button"]
14030     *        The element's node type. This argument is IGNORED: no matter what
14031     *        is passed, it will always create a `button` element.
14032     *
14033     * @param {Object} [props={}]
14034     *        An object of properties that should be set on the element.
14035     *
14036     * @param {Object} [attributes={}]
14037     *        An object of attributes that should be set on the element.
14038     *
14039     * @return {Element}
14040     *         The element that gets created.
14041     */
14042    _proto.createEl = function createEl(tag, props, attributes) {
14043      if (props === void 0) {
14044        props = {};
14045      }
14046
14047      if (attributes === void 0) {
14048        attributes = {};
14049      }
14050
14051      tag = 'button';
14052      props = assign({
14053        innerHTML: '<span aria-hidden="true" class="vjs-icon-placeholder"></span>',
14054        className: this.buildCSSClass()
14055      }, props); // Add attributes for button element
14056
14057      attributes = assign({
14058        // Necessary since the default button type is "submit"
14059        type: 'button'
14060      }, attributes);
14061      var el = Component.prototype.createEl.call(this, tag, props, attributes);
14062      this.createControlTextEl(el);
14063      return el;
14064    }
14065    /**
14066     * Add a child `Component` inside of this `Button`.
14067     *
14068     * @param {string|Component} child
14069     *        The name or instance of a child to add.
14070     *
14071     * @param {Object} [options={}]
14072     *        The key/value store of options that will get passed to children of
14073     *        the child.
14074     *
14075     * @return {Component}
14076     *         The `Component` that gets added as a child. When using a string the
14077     *         `Component` will get created by this process.
14078     *
14079     * @deprecated since version 5
14080     */
14081    ;
14082
14083    _proto.addChild = function addChild(child, options) {
14084      if (options === void 0) {
14085        options = {};
14086      }
14087
14088      var className = this.constructor.name;
14089      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
14090
14091      return Component.prototype.addChild.call(this, child, options);
14092    }
14093    /**
14094     * Enable the `Button` element so that it can be activated or clicked. Use this with
14095     * {@link Button#disable}.
14096     */
14097    ;
14098
14099    _proto.enable = function enable() {
14100      _ClickableComponent.prototype.enable.call(this);
14101
14102      this.el_.removeAttribute('disabled');
14103    }
14104    /**
14105     * Disable the `Button` element so that it cannot be activated or clicked. Use this with
14106     * {@link Button#enable}.
14107     */
14108    ;
14109
14110    _proto.disable = function disable() {
14111      _ClickableComponent.prototype.disable.call(this);
14112
14113      this.el_.setAttribute('disabled', 'disabled');
14114    }
14115    /**
14116     * This gets called when a `Button` has focus and `keydown` is triggered via a key
14117     * press.
14118     *
14119     * @param {EventTarget~Event} event
14120     *        The event that caused this function to get called.
14121     *
14122     * @listens keydown
14123     */
14124    ;
14125
14126    _proto.handleKeyDown = function handleKeyDown(event) {
14127      // Ignore Space or Enter key operation, which is handled by the browser for
14128      // a button - though not for its super class, ClickableComponent. Also,
14129      // prevent the event from propagating through the DOM and triggering Player
14130      // hotkeys. We do not preventDefault here because we _want_ the browser to
14131      // handle it.
14132      if (keycode.isEventKey(event, 'Space') || keycode.isEventKey(event, 'Enter')) {
14133        event.stopPropagation();
14134        return;
14135      } // Pass keypress handling up for unsupported keys
14136
14137
14138      _ClickableComponent.prototype.handleKeyDown.call(this, event);
14139    };
14140
14141    return Button;
14142  }(ClickableComponent);
14143
14144  Component.registerComponent('Button', Button);
14145
14146  /**
14147   * The initial play button that shows before the video has played. The hiding of the
14148   * `BigPlayButton` get done via CSS and `Player` states.
14149   *
14150   * @extends Button
14151   */
14152
14153  var BigPlayButton =
14154  /*#__PURE__*/
14155  function (_Button) {
14156    _inheritsLoose(BigPlayButton, _Button);
14157
14158    function BigPlayButton(player, options) {
14159      var _this;
14160
14161      _this = _Button.call(this, player, options) || this;
14162      _this.mouseused_ = false;
14163
14164      _this.on('mousedown', _this.handleMouseDown);
14165
14166      return _this;
14167    }
14168    /**
14169     * Builds the default DOM `className`.
14170     *
14171     * @return {string}
14172     *         The DOM `className` for this object. Always returns 'vjs-big-play-button'.
14173     */
14174
14175
14176    var _proto = BigPlayButton.prototype;
14177
14178    _proto.buildCSSClass = function buildCSSClass() {
14179      return 'vjs-big-play-button';
14180    }
14181    /**
14182     * This gets called when a `BigPlayButton` "clicked". See {@link ClickableComponent}
14183     * for more detailed information on what a click can be.
14184     *
14185     * @param {EventTarget~Event} event
14186     *        The `keydown`, `tap`, or `click` event that caused this function to be
14187     *        called.
14188     *
14189     * @listens tap
14190     * @listens click
14191     */
14192    ;
14193
14194    _proto.handleClick = function handleClick(event) {
14195      var playPromise = this.player_.play(); // exit early if clicked via the mouse
14196
14197      if (this.mouseused_ && event.clientX && event.clientY) {
14198        silencePromise(playPromise);
14199
14200        if (this.player_.tech(true)) {
14201          this.player_.tech(true).focus();
14202        }
14203
14204        return;
14205      }
14206
14207      var cb = this.player_.getChild('controlBar');
14208      var playToggle = cb && cb.getChild('playToggle');
14209
14210      if (!playToggle) {
14211        this.player_.tech(true).focus();
14212        return;
14213      }
14214
14215      var playFocus = function playFocus() {
14216        return playToggle.focus();
14217      };
14218
14219      if (isPromise(playPromise)) {
14220        playPromise.then(playFocus, function () {});
14221      } else {
14222        this.setTimeout(playFocus, 1);
14223      }
14224    };
14225
14226    _proto.handleKeyDown = function handleKeyDown(event) {
14227      this.mouseused_ = false;
14228
14229      _Button.prototype.handleKeyDown.call(this, event);
14230    };
14231
14232    _proto.handleMouseDown = function handleMouseDown(event) {
14233      this.mouseused_ = true;
14234    };
14235
14236    return BigPlayButton;
14237  }(Button);
14238  /**
14239   * The text that should display over the `BigPlayButton`s controls. Added to for localization.
14240   *
14241   * @type {string}
14242   * @private
14243   */
14244
14245
14246  BigPlayButton.prototype.controlText_ = 'Play Video';
14247  Component.registerComponent('BigPlayButton', BigPlayButton);
14248
14249  /**
14250   * The `CloseButton` is a `{@link Button}` that fires a `close` event when
14251   * it gets clicked.
14252   *
14253   * @extends Button
14254   */
14255
14256  var CloseButton =
14257  /*#__PURE__*/
14258  function (_Button) {
14259    _inheritsLoose(CloseButton, _Button);
14260
14261    /**
14262    * Creates an instance of the this class.
14263    *
14264    * @param  {Player} player
14265    *         The `Player` that this class should be attached to.
14266    *
14267    * @param  {Object} [options]
14268    *         The key/value store of player options.
14269    */
14270    function CloseButton(player, options) {
14271      var _this;
14272
14273      _this = _Button.call(this, player, options) || this;
14274
14275      _this.controlText(options && options.controlText || _this.localize('Close'));
14276
14277      return _this;
14278    }
14279    /**
14280    * Builds the default DOM `className`.
14281    *
14282    * @return {string}
14283    *         The DOM `className` for this object.
14284    */
14285
14286
14287    var _proto = CloseButton.prototype;
14288
14289    _proto.buildCSSClass = function buildCSSClass() {
14290      return "vjs-close-button " + _Button.prototype.buildCSSClass.call(this);
14291    }
14292    /**
14293     * This gets called when a `CloseButton` gets clicked. See
14294     * {@link ClickableComponent#handleClick} for more information on when
14295     * this will be triggered
14296     *
14297     * @param {EventTarget~Event} event
14298     *        The `keydown`, `tap`, or `click` event that caused this function to be
14299     *        called.
14300     *
14301     * @listens tap
14302     * @listens click
14303     * @fires CloseButton#close
14304     */
14305    ;
14306
14307    _proto.handleClick = function handleClick(event) {
14308      /**
14309       * Triggered when the a `CloseButton` is clicked.
14310       *
14311       * @event CloseButton#close
14312       * @type {EventTarget~Event}
14313       *
14314       * @property {boolean} [bubbles=false]
14315       *           set to false so that the close event does not
14316       *           bubble up to parents if there is no listener
14317       */
14318      this.trigger({
14319        type: 'close',
14320        bubbles: false
14321      });
14322    }
14323    /**
14324     * Event handler that is called when a `CloseButton` receives a
14325     * `keydown` event.
14326     *
14327     * By default, if the key is Esc, it will trigger a `click` event.
14328     *
14329     * @param {EventTarget~Event} event
14330     *        The `keydown` event that caused this function to be called.
14331     *
14332     * @listens keydown
14333     */
14334    ;
14335
14336    _proto.handleKeyDown = function handleKeyDown(event) {
14337      // Esc button will trigger `click` event
14338      if (keycode.isEventKey(event, 'Esc')) {
14339        event.preventDefault();
14340        event.stopPropagation();
14341        this.trigger('click');
14342      } else {
14343        // Pass keypress handling up for unsupported keys
14344        _Button.prototype.handleKeyDown.call(this, event);
14345      }
14346    };
14347
14348    return CloseButton;
14349  }(Button);
14350
14351  Component.registerComponent('CloseButton', CloseButton);
14352
14353  /**
14354   * Button to toggle between play and pause.
14355   *
14356   * @extends Button
14357   */
14358
14359  var PlayToggle =
14360  /*#__PURE__*/
14361  function (_Button) {
14362    _inheritsLoose(PlayToggle, _Button);
14363
14364    /**
14365     * Creates an instance of this class.
14366     *
14367     * @param {Player} player
14368     *        The `Player` that this class should be attached to.
14369     *
14370     * @param {Object} [options={}]
14371     *        The key/value store of player options.
14372     */
14373    function PlayToggle(player, options) {
14374      var _this;
14375
14376      if (options === void 0) {
14377        options = {};
14378      }
14379
14380      _this = _Button.call(this, player, options) || this; // show or hide replay icon
14381
14382      options.replay = options.replay === undefined || options.replay;
14383
14384      _this.on(player, 'play', _this.handlePlay);
14385
14386      _this.on(player, 'pause', _this.handlePause);
14387
14388      if (options.replay) {
14389        _this.on(player, 'ended', _this.handleEnded);
14390      }
14391
14392      return _this;
14393    }
14394    /**
14395     * Builds the default DOM `className`.
14396     *
14397     * @return {string}
14398     *         The DOM `className` for this object.
14399     */
14400
14401
14402    var _proto = PlayToggle.prototype;
14403
14404    _proto.buildCSSClass = function buildCSSClass() {
14405      return "vjs-play-control " + _Button.prototype.buildCSSClass.call(this);
14406    }
14407    /**
14408     * This gets called when an `PlayToggle` is "clicked". See
14409     * {@link ClickableComponent} for more detailed information on what a click can be.
14410     *
14411     * @param {EventTarget~Event} [event]
14412     *        The `keydown`, `tap`, or `click` event that caused this function to be
14413     *        called.
14414     *
14415     * @listens tap
14416     * @listens click
14417     */
14418    ;
14419
14420    _proto.handleClick = function handleClick(event) {
14421      if (this.player_.paused()) {
14422        this.player_.play();
14423      } else {
14424        this.player_.pause();
14425      }
14426    }
14427    /**
14428     * This gets called once after the video has ended and the user seeks so that
14429     * we can change the replay button back to a play button.
14430     *
14431     * @param {EventTarget~Event} [event]
14432     *        The event that caused this function to run.
14433     *
14434     * @listens Player#seeked
14435     */
14436    ;
14437
14438    _proto.handleSeeked = function handleSeeked(event) {
14439      this.removeClass('vjs-ended');
14440
14441      if (this.player_.paused()) {
14442        this.handlePause(event);
14443      } else {
14444        this.handlePlay(event);
14445      }
14446    }
14447    /**
14448     * Add the vjs-playing class to the element so it can change appearance.
14449     *
14450     * @param {EventTarget~Event} [event]
14451     *        The event that caused this function to run.
14452     *
14453     * @listens Player#play
14454     */
14455    ;
14456
14457    _proto.handlePlay = function handlePlay(event) {
14458      this.removeClass('vjs-ended');
14459      this.removeClass('vjs-paused');
14460      this.addClass('vjs-playing'); // change the button text to "Pause"
14461
14462      this.controlText('Pause');
14463    }
14464    /**
14465     * Add the vjs-paused class to the element so it can change appearance.
14466     *
14467     * @param {EventTarget~Event} [event]
14468     *        The event that caused this function to run.
14469     *
14470     * @listens Player#pause
14471     */
14472    ;
14473
14474    _proto.handlePause = function handlePause(event) {
14475      this.removeClass('vjs-playing');
14476      this.addClass('vjs-paused'); // change the button text to "Play"
14477
14478      this.controlText('Play');
14479    }
14480    /**
14481     * Add the vjs-ended class to the element so it can change appearance
14482     *
14483     * @param {EventTarget~Event} [event]
14484     *        The event that caused this function to run.
14485     *
14486     * @listens Player#ended
14487     */
14488    ;
14489
14490    _proto.handleEnded = function handleEnded(event) {
14491      this.removeClass('vjs-playing');
14492      this.addClass('vjs-ended'); // change the button text to "Replay"
14493
14494      this.controlText('Replay'); // on the next seek remove the replay button
14495
14496      this.one(this.player_, 'seeked', this.handleSeeked);
14497    };
14498
14499    return PlayToggle;
14500  }(Button);
14501  /**
14502   * The text that should display over the `PlayToggle`s controls. Added for localization.
14503   *
14504   * @type {string}
14505   * @private
14506   */
14507
14508
14509  PlayToggle.prototype.controlText_ = 'Play';
14510  Component.registerComponent('PlayToggle', PlayToggle);
14511
14512  /**
14513   * @file format-time.js
14514   * @module format-time
14515   */
14516
14517  /**
14518   * Format seconds as a time string, H:MM:SS or M:SS. Supplying a guide (in
14519   * seconds) will force a number of leading zeros to cover the length of the
14520   * guide.
14521   *
14522   * @private
14523   * @param  {number} seconds
14524   *         Number of seconds to be turned into a string
14525   *
14526   * @param  {number} guide
14527   *         Number (in seconds) to model the string after
14528   *
14529   * @return {string}
14530   *         Time formatted as H:MM:SS or M:SS
14531   */
14532  var defaultImplementation = function defaultImplementation(seconds, guide) {
14533    seconds = seconds < 0 ? 0 : seconds;
14534    var s = Math.floor(seconds % 60);
14535    var m = Math.floor(seconds / 60 % 60);
14536    var h = Math.floor(seconds / 3600);
14537    var gm = Math.floor(guide / 60 % 60);
14538    var gh = Math.floor(guide / 3600); // handle invalid times
14539
14540    if (isNaN(seconds) || seconds === Infinity) {
14541      // '-' is false for all relational operators (e.g. <, >
vendor: 6,494 bytes, lines 14541-14761
14541=) so this setting
14542      // will add the minimum number of fields specified by the guide
14543      h = m = s = '-';
14544    } // Check if we need to show hours
14545
14546
14547    h = h > 0 || gh > 0 ? h + ':' : ''; // If hours are showing, we may need to add a leading zero.
14548    // Always show at least one digit of minutes.
14549
14550    m = ((h || gm >= 10) && m < 10 ? '0' + m : m) + ':'; // Check if leading zero is need for seconds
14551
14552    s = s < 10 ? '0' + s : s;
14553    return h + m + s;
14554  }; // Internal pointer to the current implementation.
14555
14556
14557  var implementation$3 = defaultImplementation;
14558  /**
14559   * Replaces the default formatTime implementation with a custom implementation.
14560   *
14561   * @param {Function} customImplementation
14562   *        A function which will be used in place of the default formatTime
14563   *        implementation. Will receive the current time in seconds and the
14564   *        guide (in seconds) as arguments.
14565   */
14566
14567  function setFormatTime(customImplementation) {
14568    implementation$3 = customImplementation;
14569  }
14570  /**
14571   * Resets formatTime to the default implementation.
14572   */
14573
14574  function resetFormatTime() {
14575    implementation$3 = defaultImplementation;
14576  }
14577  /**
14578   * Delegates to either the default time formatting function or a custom
14579   * function supplied via `setFormatTime`.
14580   *
14581   * Formats seconds as a time string (H:MM:SS or M:SS). Supplying a
14582   * guide (in seconds) will force a number of leading zeros to cover the
14583   * length of the guide.
14584   *
14585   * @static
14586   * @example  formatTime(125, 600) === "02:05"
14587   * @param    {number} seconds
14588   *           Number of seconds to be turned into a string
14589   *
14590   * @param    {number} guide
14591   *           Number (in seconds) to model the string after
14592   *
14593   * @return   {string}
14594   *           Time formatted as H:MM:SS or M:SS
14595   */
14596
14597  function formatTime(seconds, guide) {
14598    if (guide === void 0) {
14599      guide = seconds;
14600    }
14601
14602    return implementation$3(seconds, guide);
14603  }
14604
14605  /**
14606   * Displays time information about the video
14607   *
14608   * @extends Component
14609   */
14610
14611  var TimeDisplay =
14612  /*#__PURE__*/
14613  function (_Component) {
14614    _inheritsLoose(TimeDisplay, _Component);
14615
14616    /**
14617     * Creates an instance of this class.
14618     *
14619     * @param {Player} player
14620     *        The `Player` that this class should be attached to.
14621     *
14622     * @param {Object} [options]
14623     *        The key/value store of player options.
14624     */
14625    function TimeDisplay(player, options) {
14626      var _this;
14627
14628      _this = _Component.call(this, player, options) || this;
14629      _this.throttledUpdateContent = throttle(bind(_assertThisInitialized(_this), _this.updateContent), UPDATE_REFRESH_INTERVAL);
14630
14631      _this.on(player, 'timeupdate', _this.throttledUpdateContent);
14632
14633      return _this;
14634    }
14635    /**
14636     * Create the `Component`'s DOM element
14637     *
14638     * @return {Element}
14639     *         The element that was created.
14640     */
14641
14642
14643    var _proto = TimeDisplay.prototype;
14644
14645    _proto.createEl = function createEl$1() {
14646      var className = this.buildCSSClass();
14647
14648      var el = _Component.prototype.createEl.call(this, 'div', {
14649        className: className + " vjs-time-control vjs-control",
14650        innerHTML: "<span class=\"vjs-control-text\" role=\"presentation\">" + this.localize(this.labelText_) + "\xA0</span>"
14651      });
14652
14653      this.contentEl_ = createEl('span', {
14654        className: className + "-display"
14655      }, {
14656        // tell screen readers not to automatically read the time as it changes
14657        'aria-live': 'off',
14658        // span elements have no implicit role, but some screen readers (notably VoiceOver)
14659        // treat them as a break between items in the DOM when using arrow keys
14660        // (or left-to-right swipes on iOS) to read contents of a page. Using
14661        // role='presentation' causes VoiceOver to NOT treat this span as a break.
14662        'role': 'presentation'
14663      });
14664      this.updateTextNode_();
14665      el.appendChild(this.contentEl_);
14666      return el;
14667    };
14668
14669    _proto.dispose = function dispose() {
14670      this.contentEl_ = null;
14671      this.textNode_ = null;
14672
14673      _Component.prototype.dispose.call(this);
14674    }
14675    /**
14676     * Updates the "remaining time" text node with new content using the
14677     * contents of the `formattedTime_` property.
14678     *
14679     * @private
14680     */
14681    ;
14682
14683    _proto.updateTextNode_ = function updateTextNode_() {
14684      if (!this.contentEl_) {
14685        return;
14686      }
14687
14688      while (this.contentEl_.firstChild) {
14689        this.contentEl_.removeChild(this.contentEl_.firstChild);
14690      }
14691
14692      this.textNode_ = document.createTextNode(this.formattedTime_ || this.formatTime_(0));
14693      this.contentEl_.appendChild(this.textNode_);
14694    }
14695    /**
14696     * Generates a formatted time for this component to use in display.
14697     *
14698     * @param  {number} time
14699     *         A numeric time, in seconds.
14700     *
14701     * @return {string}
14702     *         A formatted time
14703     *
14704     * @private
14705     */
14706    ;
14707
14708    _proto.formatTime_ = function formatTime_(time) {
14709      return formatTime(time);
14710    }
14711    /**
14712     * Updates the time display text node if it has what was passed in changed
14713     * the formatted time.
14714     *
14715     * @param {number} time
14716     *        The time to update to
14717     *
14718     * @private
14719     */
14720    ;
14721
14722    _proto.updateFormattedTime_ = function updateFormattedTime_(time) {
14723      var formattedTime = this.formatTime_(time);
14724
14725      if (formattedTime === this.formattedTime_) {
14726        return;
14727      }
14728
14729      this.formattedTime_ = formattedTime;
14730      this.requestAnimationFrame(this.updateTextNode_);
14731    }
14732    /**
14733     * To be filled out in the child class, should update the displayed time
14734     * in accordance with the fact that the current time has changed.
14735     *
14736     * @param {EventTarget~Event} [event]
14737     *        The `timeupdate`  event that caused this to run.
14738     *
14739     * @listens Player#timeupdate
14740     */
14741    ;
14742
14743    _proto.updateContent = function updateContent(event) {};
14744
14745    return TimeDisplay;
14746  }(Component);
14747  /**
14748   * The text that is added to the `TimeDisplay` for screen reader users.
14749   *
14750   * @type {string}
14751   * @private
14752   */
14753
14754
14755  TimeDisplay.prototype.labelText_ = 'Time';
14756  /**
14757   * The text that should display over the `TimeDisplay`s controls. Added to for localization.
14758   *
14759   * @type {string}
14760   * @private
14761   *
14762   * @deprecated in v7; controlText_ is not used in non-active display Components
14763   */
14764
14765  TimeDisplay.prototype.controlText_ = 'Time';
14766  Component.registerComponent('TimeDisplay', TimeDisplay);
14767
14768  /**
14769   * Displays the current time
14770   *
14771   * @extends Component
14772   */
14773
14774  var CurrentTimeDisplay =
14775  /*#__PURE__*/
14776  function (_TimeDisplay) {
14777    _inheritsLoose(CurrentTimeDisplay, _TimeDisplay);
14778
14779    /**
14780     * Creates an instance of this class.
14781     *
14782     * @param {Player} player
14783     *        The `Player` that this class should be attached to.
14784     *
14785     * @param {Object} [options]
14786     *        The key/value store of player options.
14787     */
14788    function CurrentTimeDisplay(player, options) {
14789      var _this;
14790
14791      _this = _TimeDisplay.call(this, player, options) || this;
14792
14793      _this.on(player, 'ended', _this.handleEnded);
14794
14795      return _this;
14796    }
14797    /**
14798     * Builds the default DOM `className`.
14799     *
14800     * @return {string}
14801     *         The DOM `className` for this object.
14802     */
14803
14804
14805    var _proto = CurrentTimeDisplay.prototype;
14806
14807    _proto.buildCSSClass = function buildCSSClass() {
14808      return 'vjs-current-time';
14809    }
14810    /**
14811     * Update current time display
14812     *
14813     * @param {EventTarget~Event} [event]
14814     *        The `timeupdate` event that caused this function to run.
14815     *
14816     * @listens Player#timeupdate
14817     */
14818    ;
14819
14820    _proto.updateContent = function updateContent(event) {
14821      // Allows for smooth scrubbing, when player can't keep up.
14822      var time = this.player_.scrubbing() ? this.player_.getCache().currentTime : this.player_.currentTime();
14823      this.updateFormattedTime_(time);
14824    }
14825    /**
14826     * When the player fires ended there should be no time left. Sadly
14827     * this is not always the case, lets make it seem like that is the case
14828     * for users.
14829     *
14830     * @param {EventTarget~Event} [event]
14831     *        The `ended` event that caused this to run.
14832     *
14833     * @listens Player#ended
14834     */
14835    ;
14836
14837    _proto.handleEnded = function handleEnded(event) {
14838      if (!this.player_.duration()) {
14839        return;
14840      }
14841
14842      this.updateFormattedTime_(this.player_.duration());
14843    };
14844
14845    return CurrentTimeDisplay;
14846  }(TimeDisplay);
14847  /**
14848   * The text that is added to the `CurrentTimeDisplay` for screen reader users.
14849   *
14850   * @type {string}
14851   * @private
14852   */
14853
14854
14855  CurrentTimeDisplay.prototype.labelText_ = 'Current Time';
14856  /**
14857   * The text that should display over the `CurrentTimeDisplay`s controls. Added to for localization.
14858   *
14859   * @type {string}
14860   * @private
14861   *
14862   * @deprecated in v7; controlText_ is not used in non-active display Components
14863   */
14864
14865  CurrentTimeDisplay.prototype.controlText_ = 'Current Time';
14866  Component.registerComponent('CurrentTimeDisplay', CurrentTimeDisplay);
14867
14868  /**
14869   * Displays the duration
14870   *
14871   * @extends Component
14872   */
14873
14874  var DurationDisplay =
14875  /*#__PURE__*/
14876  function (_TimeDisplay) {
14877    _inheritsLoose(DurationDisplay, _TimeDisplay);
14878
14879    /**
14880     * Creates an instance of this class.
14881     *
14882     * @param {Player} player
14883     *        The `Player` that this class should be attached to.
14884     *
14885     * @param {Object} [options]
14886     *        The key/value store of player options.
14887     */
14888    function DurationDisplay(player, options) {
14889      var _this;
14890
14891      _this = _TimeDisplay.call(this, player, options) || this; // we do not want to/need to throttle duration changes,
14892      // as they should always display the changed duration as
14893      // it has changed
14894
14895      _this.on(player, 'durationchange', _this.updateContent); // Listen to loadstart because the player duration is reset when a new media element is loaded,
14896      // but the durationchange on the user agent will not fire.
14897      // @see [Spec]{@link https://www.w3.org/TR/2011/WD-html5-20110113/video.html#media-element-load-algorithm}
14898
14899
14900      _this.on(player, 'loadstart', _this.updateContent); // Also listen for timeupdate (in the parent) and loadedmetadata because removing those
vendor: 129,789 bytes, lines 14901-19300
14901      // listeners could have broken dependent applications/libraries. These
14902      // can likely be removed for 7.0.
14903
14904
14905      _this.on(player, 'loadedmetadata', _this.throttledUpdateContent);
14906
14907      return _this;
14908    }
14909    /**
14910     * Builds the default DOM `className`.
14911     *
14912     * @return {string}
14913     *         The DOM `className` for this object.
14914     */
14915
14916
14917    var _proto = DurationDisplay.prototype;
14918
14919    _proto.buildCSSClass = function buildCSSClass() {
14920      return 'vjs-duration';
14921    }
14922    /**
14923     * Update duration time display.
14924     *
14925     * @param {EventTarget~Event} [event]
14926     *        The `durationchange`, `timeupdate`, or `loadedmetadata` event that caused
14927     *        this function to be called.
14928     *
14929     * @listens Player#durationchange
14930     * @listens Player#timeupdate
14931     * @listens Player#loadedmetadata
14932     */
14933    ;
14934
14935    _proto.updateContent = function updateContent(event) {
14936      var duration = this.player_.duration();
14937
14938      if (this.duration_ !== duration) {
14939        this.duration_ = duration;
14940        this.updateFormattedTime_(duration);
14941      }
14942    };
14943
14944    return DurationDisplay;
14945  }(TimeDisplay);
14946  /**
14947   * The text that is added to the `DurationDisplay` for screen reader users.
14948   *
14949   * @type {string}
14950   * @private
14951   */
14952
14953
14954  DurationDisplay.prototype.labelText_ = 'Duration';
14955  /**
14956   * The text that should display over the `DurationDisplay`s controls. Added to for localization.
14957   *
14958   * @type {string}
14959   * @private
14960   *
14961   * @deprecated in v7; controlText_ is not used in non-active display Components
14962   */
14963
14964  DurationDisplay.prototype.controlText_ = 'Duration';
14965  Component.registerComponent('DurationDisplay', DurationDisplay);
14966
14967  /**
14968   * The separator between the current time and duration.
14969   * Can be hidden if it's not needed in the design.
14970   *
14971   * @extends Component
14972   */
14973
14974  var TimeDivider =
14975  /*#__PURE__*/
14976  function (_Component) {
14977    _inheritsLoose(TimeDivider, _Component);
14978
14979    function TimeDivider() {
14980      return _Component.apply(this, arguments) || this;
14981    }
14982
14983    var _proto = TimeDivider.prototype;
14984
14985    /**
14986     * Create the component's DOM element
14987     *
14988     * @return {Element}
14989     *         The element that was created.
14990     */
14991    _proto.createEl = function createEl() {
14992      return _Component.prototype.createEl.call(this, 'div', {
14993        className: 'vjs-time-control vjs-time-divider',
14994        innerHTML: '<div><span>/</span></div>'
14995      }, {
14996        // this element and its contents can be hidden from assistive techs since
14997        // it is made extraneous by the announcement of the control text
14998        // for the current time and duration displays
14999        'aria-hidden': true
15000      });
15001    };
15002
15003    return TimeDivider;
15004  }(Component);
15005
15006  Component.registerComponent('TimeDivider', TimeDivider);
15007
15008  /**
15009   * Displays the time left in the video
15010   *
15011   * @extends Component
15012   */
15013
15014  var RemainingTimeDisplay =
15015  /*#__PURE__*/
15016  function (_TimeDisplay) {
15017    _inheritsLoose(RemainingTimeDisplay, _TimeDisplay);
15018
15019    /**
15020     * Creates an instance of this class.
15021     *
15022     * @param {Player} player
15023     *        The `Player` that this class should be attached to.
15024     *
15025     * @param {Object} [options]
15026     *        The key/value store of player options.
15027     */
15028    function RemainingTimeDisplay(player, options) {
15029      var _this;
15030
15031      _this = _TimeDisplay.call(this, player, options) || this;
15032
15033      _this.on(player, 'durationchange', _this.throttledUpdateContent);
15034
15035      _this.on(player, 'ended', _this.handleEnded);
15036
15037      return _this;
15038    }
15039    /**
15040     * Builds the default DOM `className`.
15041     *
15042     * @return {string}
15043     *         The DOM `className` for this object.
15044     */
15045
15046
15047    var _proto = RemainingTimeDisplay.prototype;
15048
15049    _proto.buildCSSClass = function buildCSSClass() {
15050      return 'vjs-remaining-time';
15051    }
15052    /**
15053     * Create the `Component`'s DOM element with the "minus" characted prepend to the time
15054     *
15055     * @return {Element}
15056     *         The element that was created.
15057     */
15058    ;
15059
15060    _proto.createEl = function createEl$1() {
15061      var el = _TimeDisplay.prototype.createEl.call(this);
15062
15063      el.insertBefore(createEl('span', {}, {
15064        'aria-hidden': true
15065      }, '-'), this.contentEl_);
15066      return el;
15067    }
15068    /**
15069     * Update remaining time display.
15070     *
15071     * @param {EventTarget~Event} [event]
15072     *        The `timeupdate` or `durationchange` event that caused this to run.
15073     *
15074     * @listens Player#timeupdate
15075     * @listens Player#durationchange
15076     */
15077    ;
15078
15079    _proto.updateContent = function updateContent(event) {
15080      if (typeof this.player_.duration() !== 'number') {
15081        return;
15082      } // @deprecated We should only use remainingTimeDisplay
15083      // as of video.js 7
15084
15085
15086      if (this.player_.remainingTimeDisplay) {
15087        this.updateFormattedTime_(this.player_.remainingTimeDisplay());
15088      } else {
15089        this.updateFormattedTime_(this.player_.remainingTime());
15090      }
15091    }
15092    /**
15093     * When the player fires ended there should be no time left. Sadly
15094     * this is not always the case, lets make it seem like that is the case
15095     * for users.
15096     *
15097     * @param {EventTarget~Event} [event]
15098     *        The `ended` event that caused this to run.
15099     *
15100     * @listens Player#ended
15101     */
15102    ;
15103
15104    _proto.handleEnded = function handleEnded(event) {
15105      if (!this.player_.duration()) {
15106        return;
15107      }
15108
15109      this.updateFormattedTime_(0);
15110    };
15111
15112    return RemainingTimeDisplay;
15113  }(TimeDisplay);
15114  /**
15115   * The text that is added to the `RemainingTimeDisplay` for screen reader users.
15116   *
15117   * @type {string}
15118   * @private
15119   */
15120
15121
15122  RemainingTimeDisplay.prototype.labelText_ = 'Remaining Time';
15123  /**
15124   * The text that should display over the `RemainingTimeDisplay`s controls. Added to for localization.
15125   *
15126   * @type {string}
15127   * @private
15128   *
15129   * @deprecated in v7; controlText_ is not used in non-active display Components
15130   */
15131
15132  RemainingTimeDisplay.prototype.controlText_ = 'Remaining Time';
15133  Component.registerComponent('RemainingTimeDisplay', RemainingTimeDisplay);
15134
15135  /**
15136   * Displays the live indicator when duration is Infinity.
15137   *
15138   * @extends Component
15139   */
15140
15141  var LiveDisplay =
15142  /*#__PURE__*/
15143  function (_Component) {
15144    _inheritsLoose(LiveDisplay, _Component);
15145
15146    /**
15147     * Creates an instance of this class.
15148     *
15149     * @param {Player} player
15150     *        The `Player` that this class should be attached to.
15151     *
15152     * @param {Object} [options]
15153     *        The key/value store of player options.
15154     */
15155    function LiveDisplay(player, options) {
15156      var _this;
15157
15158      _this = _Component.call(this, player, options) || this;
15159
15160      _this.updateShowing();
15161
15162      _this.on(_this.player(), 'durationchange', _this.updateShowing);
15163
15164      return _this;
15165    }
15166    /**
15167     * Create the `Component`'s DOM element
15168     *
15169     * @return {Element}
15170     *         The element that was created.
15171     */
15172
15173
15174    var _proto = LiveDisplay.prototype;
15175
15176    _proto.createEl = function createEl$1() {
15177      var el = _Component.prototype.createEl.call(this, 'div', {
15178        className: 'vjs-live-control vjs-control'
15179      });
15180
15181      this.contentEl_ = createEl('div', {
15182        className: 'vjs-live-display',
15183        innerHTML: "<span class=\"vjs-control-text\">" + this.localize('Stream Type') + "\xA0</span>" + this.localize('LIVE')
15184      }, {
15185        'aria-live': 'off'
15186      });
15187      el.appendChild(this.contentEl_);
15188      return el;
15189    };
15190
15191    _proto.dispose = function dispose() {
15192      this.contentEl_ = null;
15193
15194      _Component.prototype.dispose.call(this);
15195    }
15196    /**
15197     * Check the duration to see if the LiveDisplay should be showing or not. Then show/hide
15198     * it accordingly
15199     *
15200     * @param {EventTarget~Event} [event]
15201     *        The {@link Player#durationchange} event that caused this function to run.
15202     *
15203     * @listens Player#durationchange
15204     */
15205    ;
15206
15207    _proto.updateShowing = function updateShowing(event) {
15208      if (this.player().duration() === Infinity) {
15209        this.show();
15210      } else {
15211        this.hide();
15212      }
15213    };
15214
15215    return LiveDisplay;
15216  }(Component);
15217
15218  Component.registerComponent('LiveDisplay', LiveDisplay);
15219
15220  /**
15221   * Displays the live indicator when duration is Infinity.
15222   *
15223   * @extends Component
15224   */
15225
15226  var SeekToLive =
15227  /*#__PURE__*/
15228  function (_Button) {
15229    _inheritsLoose(SeekToLive, _Button);
15230
15231    /**
15232     * Creates an instance of this class.
15233     *
15234     * @param {Player} player
15235     *        The `Player` that this class should be attached to.
15236     *
15237     * @param {Object} [options]
15238     *        The key/value store of player options.
15239     */
15240    function SeekToLive(player, options) {
15241      var _this;
15242
15243      _this = _Button.call(this, player, options) || this;
15244
15245      _this.updateLiveEdgeStatus();
15246
15247      if (_this.player_.liveTracker) {
15248        _this.on(_this.player_.liveTracker, 'liveedgechange', _this.updateLiveEdgeStatus);
15249      }
15250
15251      return _this;
15252    }
15253    /**
15254     * Create the `Component`'s DOM element
15255     *
15256     * @return {Element}
15257     *         The element that was created.
15258     */
15259
15260
15261    var _proto = SeekToLive.prototype;
15262
15263    _proto.createEl = function createEl$1() {
15264      var el = _Button.prototype.createEl.call(this, 'button', {
15265        className: 'vjs-seek-to-live-control vjs-control'
15266      });
15267
15268      this.textEl_ = createEl('span', {
15269        className: 'vjs-seek-to-live-text',
15270        innerHTML: this.localize('LIVE')
15271      }, {
15272        'aria-hidden': 'true'
15273      });
15274      el.appendChild(this.textEl_);
15275      return el;
15276    }
15277    /**
15278     * Update the state of this button if we are at the live edge
15279     * or not
15280     */
15281    ;
15282
15283    _proto.updateLiveEdgeStatus = function updateLiveEdgeStatus(e) {
15284      // default to live edge
15285      if (!this.player_.liveTracker || this.player_.liveTracker.atLiveEdge()) {
15286        this.setAttribute('aria-disabled', true);
15287        this.addClass('vjs-at-live-edge');
15288        this.controlText('Seek to live, currently playing live');
15289      } else {
15290        this.setAttribute('aria-disabled', false);
15291        this.removeClass('vjs-at-live-edge');
15292        this.controlText('Seek to live, currently behind live');
15293      }
15294    }
15295    /**
15296     * On click bring us as near to the live point as possible.
15297     * This requires that we wait for the next `live-seekable-change`
15298     * event which will happen every segment length seconds.
15299     */
15300    ;
15301
15302    _proto.handleClick = function handleClick() {
15303      this.player_.liveTracker.seekToLiveEdge();
15304    }
15305    /**
15306     * Dispose of the element and stop tracking
15307     */
15308    ;
15309
15310    _proto.dispose = function dispose() {
15311      if (this.player_.liveTracker) {
15312        this.off(this.player_.liveTracker, 'liveedgechange', this.updateLiveEdgeStatus);
15313      }
15314
15315      this.textEl_ = null;
15316
15317      _Button.prototype.dispose.call(this);
15318    };
15319
15320    return SeekToLive;
15321  }(Button);
15322
15323  SeekToLive.prototype.controlText_ = 'Seek to live, currently playing live';
15324  Component.registerComponent('SeekToLive', SeekToLive);
15325
15326  /**
15327   * The base functionality for a slider. Can be vertical or horizontal.
15328   * For instance the volume bar or the seek bar on a video is a slider.
15329   *
15330   * @extends Component
15331   */
15332
15333  var Slider =
15334  /*#__PURE__*/
15335  function (_Component) {
15336    _inheritsLoose(Slider, _Component);
15337
15338    /**
15339    * Create an instance of this class
15340    *
15341    * @param {Player} player
15342    *        The `Player` that this class should be attached to.
15343    *
15344    * @param {Object} [options]
15345    *        The key/value store of player options.
15346    */
15347    function Slider(player, options) {
15348      var _this;
15349
15350      _this = _Component.call(this, player, options) || this; // Set property names to bar to match with the child Slider class is looking for
15351
15352      _this.bar = _this.getChild(_this.options_.barName); // Set a horizontal or vertical class on the slider depending on the slider type
15353
15354      _this.vertical(!!_this.options_.vertical);
15355
15356      _this.enable();
15357
15358      return _this;
15359    }
15360    /**
15361     * Are controls are currently enabled for this slider or not.
15362     *
15363     * @return {boolean}
15364     *         true if controls are enabled, false otherwise
15365     */
15366
15367
15368    var _proto = Slider.prototype;
15369
15370    _proto.enabled = function enabled() {
15371      return this.enabled_;
15372    }
15373    /**
15374     * Enable controls for this slider if they are disabled
15375     */
15376    ;
15377
15378    _proto.enable = function enable() {
15379      if (this.enabled()) {
15380        return;
15381      }
15382
15383      this.on('mousedown', this.handleMouseDown);
15384      this.on('touchstart', this.handleMouseDown);
15385      this.on('keydown', this.handleKeyDown);
15386      this.on('click', this.handleClick); // TODO: deprecated, controlsvisible does not seem to be fired
15387
15388      this.on(this.player_, 'controlsvisible', this.update);
15389
15390      if (this.playerEvent) {
15391        this.on(this.player_, this.playerEvent, this.update);
15392      }
15393
15394      this.removeClass('disabled');
15395      this.setAttribute('tabindex', 0);
15396      this.enabled_ = true;
15397    }
15398    /**
15399     * Disable controls for this slider if they are enabled
15400     */
15401    ;
15402
15403    _proto.disable = function disable() {
15404      if (!this.enabled()) {
15405        return;
15406      }
15407
15408      var doc = this.bar.el_.ownerDocument;
15409      this.off('mousedown', this.handleMouseDown);
15410      this.off('touchstart', this.handleMouseDown);
15411      this.off('keydown', this.handleKeyDown);
15412      this.off('click', this.handleClick);
15413      this.off(this.player_, 'controlsvisible', this.update);
15414      this.off(doc, 'mousemove', this.handleMouseMove);
15415      this.off(doc, 'mouseup', this.handleMouseUp);
15416      this.off(doc, 'touchmove', this.handleMouseMove);
15417      this.off(doc, 'touchend', this.handleMouseUp);
15418      this.removeAttribute('tabindex');
15419      this.addClass('disabled');
15420
15421      if (this.playerEvent) {
15422        this.off(this.player_, this.playerEvent, this.update);
15423      }
15424
15425      this.enabled_ = false;
15426    }
15427    /**
15428     * Create the `Slider`s DOM element.
15429     *
15430     * @param {string} type
15431     *        Type of element to create.
15432     *
15433     * @param {Object} [props={}]
15434     *        List of properties in Object form.
15435     *
15436     * @param {Object} [attributes={}]
15437     *        list of attributes in Object form.
15438     *
15439     * @return {Element}
15440     *         The element that gets created.
15441     */
15442    ;
15443
15444    _proto.createEl = function createEl(type, props, attributes) {
15445      if (props === void 0) {
15446        props = {};
15447      }
15448
15449      if (attributes === void 0) {
15450        attributes = {};
15451      }
15452
15453      // Add the slider element class to all sub classes
15454      props.className = props.className + ' vjs-slider';
15455      props = assign({
15456        tabIndex: 0
15457      }, props);
15458      attributes = assign({
15459        'role': 'slider',
15460        'aria-valuenow': 0,
15461        'aria-valuemin': 0,
15462        'aria-valuemax': 100,
15463        'tabIndex': 0
15464      }, attributes);
15465      return _Component.prototype.createEl.call(this, type, props, attributes);
15466    }
15467    /**
15468     * Handle `mousedown` or `touchstart` events on the `Slider`.
15469     *
15470     * @param {EventTarget~Event} event
15471     *        `mousedown` or `touchstart` event that triggered this function
15472     *
15473     * @listens mousedown
15474     * @listens touchstart
15475     * @fires Slider#slideractive
15476     */
15477    ;
15478
15479    _proto.handleMouseDown = function handleMouseDown(event) {
15480      var doc = this.bar.el_.ownerDocument;
15481
15482      if (event.type === 'mousedown') {
15483        event.preventDefault();
15484      } // Do not call preventDefault() on touchstart in Chrome
15485      // to avoid console warnings. Use a 'touch-action: none' style
15486      // instead to prevent unintented scrolling.
15487      // https://developers.google.com/web/updates/2017/01/scrolling-intervention
15488
15489
15490      if (event.type === 'touchstart' && !IS_CHROME) {
15491        event.preventDefault();
15492      }
15493
15494      blockTextSelection();
15495      this.addClass('vjs-sliding');
15496      /**
15497       * Triggered when the slider is in an active state
15498       *
15499       * @event Slider#slideractive
15500       * @type {EventTarget~Event}
15501       */
15502
15503      this.trigger('slideractive');
15504      this.on(doc, 'mousemove', this.handleMouseMove);
15505      this.on(doc, 'mouseup', this.handleMouseUp);
15506      this.on(doc, 'touchmove', this.handleMouseMove);
15507      this.on(doc, 'touchend', this.handleMouseUp);
15508      this.handleMouseMove(event);
15509    }
15510    /**
15511     * Handle the `mousemove`, `touchmove`, and `mousedown` events on this `Slider`.
15512     * The `mousemove` and `touchmove` events will only only trigger this function during
15513     * `mousedown` and `touchstart`. This is due to {@link Slider#handleMouseDown} and
15514     * {@link Slider#handleMouseUp}.
15515     *
15516     * @param {EventTarget~Event} event
15517     *        `mousedown`, `mousemove`, `touchstart`, or `touchmove` event that triggered
15518     *        this function
15519     *
15520     * @listens mousemove
15521     * @listens touchmove
15522     */
15523    ;
15524
15525    _proto.handleMouseMove = function handleMouseMove(event) {}
15526    /**
15527     * Handle `mouseup` or `touchend` events on the `Slider`.
15528     *
15529     * @param {EventTarget~Event} event
15530     *        `mouseup` or `touchend` event that triggered this function.
15531     *
15532     * @listens touchend
15533     * @listens mouseup
15534     * @fires Slider#sliderinactive
15535     */
15536    ;
15537
15538    _proto.handleMouseUp = function handleMouseUp() {
15539      var doc = this.bar.el_.ownerDocument;
15540      unblockTextSelection();
15541      this.removeClass('vjs-sliding');
15542      /**
15543       * Triggered when the slider is no longer in an active state.
15544       *
15545       * @event Slider#sliderinactive
15546       * @type {EventTarget~Event}
15547       */
15548
15549      this.trigger('sliderinactive');
15550      this.off(doc, 'mousemove', this.handleMouseMove);
15551      this.off(doc, 'mouseup', this.handleMouseUp);
15552      this.off(doc, 'touchmove', this.handleMouseMove);
15553      this.off(doc, 'touchend', this.handleMouseUp);
15554      this.update();
15555    }
15556    /**
15557     * Update the progress bar of the `Slider`.
15558     *
15559     * @return {number}
15560     *          The percentage of progress the progress bar represents as a
15561     *          number from 0 to 1.
15562     */
15563    ;
15564
15565    _proto.update = function update() {
15566      // In VolumeBar init we have a setTimeout for update that pops and update
15567      // to the end of the execution stack. The player is destroyed before then
15568      // update will cause an error
15569      if (!this.el_) {
15570        return;
15571      } // If scrubbing, we could use a cached value to make the handle keep up
15572      // with the user's mouse. On HTML5 browsers scrubbing is really smooth, but
15573      // some flash players are slow, so we might want to utilize this later.
15574      // var progress =  (this.player_.scrubbing()) ? this.player_.getCache().currentTime / this.player_.duration() : this.player_.currentTime() / this.player_.duration();
15575
15576
15577      var progress = this.getPercent();
15578      var bar = this.bar; // If there's no bar...
15579
15580      if (!bar) {
15581        return;
15582      } // Protect against no duration and other division issues
15583
15584
15585      if (typeof progress !== 'number' || progress !== progress || progress < 0 || progress === Infinity) {
15586        progress = 0;
15587      } // Convert to a percentage for setting
15588
15589
15590      var percentage = (progress * 100).toFixed(2) + '%';
15591      var style = bar.el().style; // Set the new bar width or height
15592
15593      var sizeKey = this.vertical() ? 'height' : 'width';
15594
15595      if (style[sizeKey] !== percentage) {
15596        style[sizeKey] = percentage;
15597      }
15598
15599      return progress;
15600    }
15601    /**
15602     * Calculate distance for slider
15603     *
15604     * @param {EventTarget~Event} event
15605     *        The event that caused this function to run.
15606     *
15607     * @return {number}
15608     *         The current position of the Slider.
15609     *         - position.x for vertical `Slider`s
15610     *         - position.y for horizontal `Slider`s
15611     */
15612    ;
15613
15614    _proto.calculateDistance = function calculateDistance(event) {
15615      var position = getPointerPosition(this.el_, event);
15616
15617      if (this.vertical()) {
15618        return position.y;
15619      }
15620
15621      return position.x;
15622    }
15623    /**
15624     * Handle a `keydown` event on the `Slider`. Watches for left, rigth, up, and down
15625     * arrow keys. This function will only be called when the slider has focus. See
15626     * {@link Slider#handleFocus} and {@link Slider#handleBlur}.
15627     *
15628     * @param {EventTarget~Event} event
15629     *        the `keydown` event that caused this function to run.
15630     *
15631     * @listens keydown
15632     */
15633    ;
15634
15635    _proto.handleKeyDown = function handleKeyDown(event) {
15636      // Left and Down Arrows
15637      if (keycode.isEventKey(event, 'Left') || keycode.isEventKey(event, 'Down')) {
15638        event.preventDefault();
15639        event.stopPropagation();
15640        this.stepBack(); // Up and Right Arrows
15641      } else if (keycode.isEventKey(event, 'Right') || keycode.isEventKey(event, 'Up')) {
15642        event.preventDefault();
15643        event.stopPropagation();
15644        this.stepForward();
15645      } else {
15646        // Pass keydown handling up for unsupported keys
15647        _Component.prototype.handleKeyDown.call(this, event);
15648      }
15649    }
15650    /**
15651     * Listener for click events on slider, used to prevent clicks
15652     *   from bubbling up to parent elements like button menus.
15653     *
15654     * @param {Object} event
15655     *        Event that caused this object to run
15656     */
15657    ;
15658
15659    _proto.handleClick = function handleClick(event) {
15660      event.stopPropagation();
15661      event.preventDefault();
15662    }
15663    /**
15664     * Get/set if slider is horizontal for vertical
15665     *
15666     * @param {boolean} [bool]
15667     *        - true if slider is vertical,
15668     *        - false is horizontal
15669     *
15670     * @return {boolean}
15671     *         - true if slider is vertical, and getting
15672     *         - false if the slider is horizontal, and getting
15673     */
15674    ;
15675
15676    _proto.vertical = function vertical(bool) {
15677      if (bool === undefined) {
15678        return this.vertical_ || false;
15679      }
15680
15681      this.vertical_ = !!bool;
15682
15683      if (this.vertical_) {
15684        this.addClass('vjs-slider-vertical');
15685      } else {
15686        this.addClass('vjs-slider-horizontal');
15687      }
15688    };
15689
15690    return Slider;
15691  }(Component);
15692
15693  Component.registerComponent('Slider', Slider);
15694
15695  /**
15696   * Shows loading progress
15697   *
15698   * @extends Component
15699   */
15700
15701  var LoadProgressBar =
15702  /*#__PURE__*/
15703  function (_Component) {
15704    _inheritsLoose(LoadProgressBar, _Component);
15705
15706    /**
15707     * Creates an instance of this class.
15708     *
15709     * @param {Player} player
15710     *        The `Player` that this class should be attached to.
15711     *
15712     * @param {Object} [options]
15713     *        The key/value store of player options.
15714     */
15715    function LoadProgressBar(player, options) {
15716      var _this;
15717
15718      _this = _Component.call(this, player, options) || this;
15719      _this.partEls_ = [];
15720
15721      _this.on(player, 'progress', _this.update);
15722
15723      return _this;
15724    }
15725    /**
15726     * Create the `Component`'s DOM element
15727     *
15728     * @return {Element}
15729     *         The element that was created.
15730     */
15731
15732
15733    var _proto = LoadProgressBar.prototype;
15734
15735    _proto.createEl = function createEl() {
15736      return _Component.prototype.createEl.call(this, 'div', {
15737        className: 'vjs-load-progress',
15738        innerHTML: "<span class=\"vjs-control-text\"><span>" + this.localize('Loaded') + "</span>: <span class=\"vjs-control-text-loaded-percentage\">0%</span></span>"
15739      });
15740    };
15741
15742    _proto.dispose = function dispose() {
15743      this.partEls_ = null;
15744
15745      _Component.prototype.dispose.call(this);
15746    }
15747    /**
15748     * Update progress bar
15749     *
15750     * @param {EventTarget~Event} [event]
15751     *        The `progress` event that caused this function to run.
15752     *
15753     * @listens Player#progress
15754     */
15755    ;
15756
15757    _proto.update = function update(event) {
15758      var liveTracker = this.player_.liveTracker;
15759      var buffered = this.player_.buffered();
15760      var duration = liveTracker && liveTracker.isLive() ? liveTracker.seekableEnd() : this.player_.duration();
15761      var bufferedEnd = this.player_.bufferedEnd();
15762      var children = this.partEls_;
15763      var controlTextPercentage = this.$('.vjs-control-text-loaded-percentage'); // get the percent width of a time compared to the total end
15764
15765      var percentify = function percentify(time, end, rounded) {
15766        // no NaN
15767        var percent = time / end || 0;
15768        percent = (percent >= 1 ? 1 : percent) * 100;
15769
15770        if (rounded) {
15771          percent = percent.toFixed(2);
15772        }
15773
15774        return percent + '%';
15775      }; // update the width of the progress bar
15776
15777
15778      this.el_.style.width = percentify(bufferedEnd, duration); // update the control-text
15779
15780      textContent(controlTextPercentage, percentify(bufferedEnd, duration, true)); // add child elements to represent the individual buffered time ranges
15781
15782      for (var i = 0; i < buffered.length; i++) {
15783        var start = buffered.start(i);
15784        var end = buffered.end(i);
15785        var part = children[i];
15786
15787        if (!part) {
15788          part = this.el_.appendChild(createEl());
15789          children[i] = part;
15790        } // set the percent based on the width of the progress bar (bufferedEnd)
15791
15792
15793        part.style.left = percentify(start, bufferedEnd);
15794        part.style.width = percentify(end - start, bufferedEnd);
15795      } // remove unused buffered range elements
15796
15797
15798      for (var _i = children.length; _i > buffered.length; _i--) {
15799        this.el_.removeChild(children[_i - 1]);
15800      }
15801
15802      children.length = buffered.length;
15803    };
15804
15805    return LoadProgressBar;
15806  }(Component);
15807
15808  Component.registerComponent('LoadProgressBar', LoadProgressBar);
15809
15810  /**
15811   * Time tooltips display a time above the progress bar.
15812   *
15813   * @extends Component
15814   */
15815
15816  var TimeTooltip =
15817  /*#__PURE__*/
15818  function (_Component) {
15819    _inheritsLoose(TimeTooltip, _Component);
15820
15821    /**
15822     * Creates an instance of this class.
15823     *
15824     * @param {Player} player
15825     *        The {@link Player} that this class should be attached to.
15826     *
15827     * @param {Object} [options]
15828     *        The key/value store of player options.
15829     */
15830    function TimeTooltip(player, options) {
15831      var _this;
15832
15833      _this = _Component.call(this, player, options) || this;
15834      _this.update = throttle(bind(_assertThisInitialized(_this), _this.update), UPDATE_REFRESH_INTERVAL);
15835      return _this;
15836    }
15837    /**
15838     * Create the time tooltip DOM element
15839     *
15840     * @return {Element}
15841     *         The element that was created.
15842     */
15843
15844
15845    var _proto = TimeTooltip.prototype;
15846
15847    _proto.createEl = function createEl() {
15848      return _Component.prototype.createEl.call(this, 'div', {
15849        className: 'vjs-time-tooltip'
15850      }, {
15851        'aria-hidden': 'true'
15852      });
15853    }
15854    /**
15855     * Updates the position of the time tooltip relative to the `SeekBar`.
15856     *
15857     * @param {Object} seekBarRect
15858     *        The `ClientRect` for the {@link SeekBar} element.
15859     *
15860     * @param {number} seekBarPoint
15861     *        A number from 0 to 1, representing a horizontal reference point
15862     *        from the left edge of the {@link SeekBar}
15863     */
15864    ;
15865
15866    _proto.update = function update(seekBarRect, seekBarPoint, content) {
15867      var tooltipRect = getBoundingClientRect(this.el_);
15868      var playerRect = getBoundingClientRect(this.player_.el());
15869      var seekBarPointPx = seekBarRect.width * seekBarPoint; // do nothing if either rect isn't available
15870      // for example, if the player isn't in the DOM for testing
15871
15872      if (!playerRect || !tooltipRect) {
15873        return;
15874      } // This is the space left of the `seekBarPoint` available within the bounds
15875      // of the player. We calculate any gap between the left edge of the player
15876      // and the left edge of the `SeekBar` and add the number of pixels in the
15877      // `SeekBar` before hitting the `seekBarPoint`
15878
15879
15880      var spaceLeftOfPoint = seekBarRect.left - playerRect.left + seekBarPointPx; // This is the space right of the `seekBarPoint` available within the bounds
15881      // of the player. We calculate the number of pixels from the `seekBarPoint`
15882      // to the right edge of the `SeekBar` and add to that any gap between the
15883      // right edge of the `SeekBar` and the player.
15884
15885      var spaceRightOfPoint = seekBarRect.width - seekBarPointPx + (playerRect.right - seekBarRect.right); // This is the number of pixels by which the tooltip will need to be pulled
15886      // further to the right to center it over the `seekBarPoint`.
15887
15888      var pullTooltipBy = tooltipRect.width / 2; // Adjust the `pullTooltipBy` distance to the left or right depending on
15889      // the results of the space calculations above.
15890
15891      if (spaceLeftOfPoint < pullTooltipBy) {
15892        pullTooltipBy += pullTooltipBy - spaceLeftOfPoint;
15893      } else if (spaceRightOfPoint < pullTooltipBy) {
15894        pullTooltipBy = spaceRightOfPoint;
15895      } // Due to the imprecision of decimal/ratio based calculations and varying
15896      // rounding behaviors, there are cases where the spacing adjustment is off
15897      // by a pixel or two. This adds insurance to these calculations.
15898
15899
15900      if (pullTooltipBy < 0) {
15901        pullTooltipBy = 0;
15902      } else if (pullTooltipBy > tooltipRect.width) {
15903        pullTooltipBy = tooltipRect.width;
15904      }
15905
15906      this.el_.style.right = "-" + pullTooltipBy + "px";
15907      this.write(content);
15908    }
15909    /**
15910     * Write the time to the tooltip DOM element.
15911     *
15912     * @param {string} content
15913     *        The formatted time for the tooltip.
15914     */
15915    ;
15916
15917    _proto.write = function write(content) {
15918      textContent(this.el_, content);
15919    }
15920    /**
15921     * Updates the position of the time tooltip relative to the `SeekBar`.
15922     *
15923     * @param {Object} seekBarRect
15924     *        The `ClientRect` for the {@link SeekBar} element.
15925     *
15926     * @param {number} seekBarPoint
15927     *        A number from 0 to 1, representing a horizontal reference point
15928     *        from the left edge of the {@link SeekBar}
15929     *
15930     * @param {number} time
15931     *        The time to update the tooltip to, not used during live playback
15932     *
15933     * @param {Function} cb
15934     *        A function that will be called during the request animation frame
15935     *        for tooltips that need to do additional animations from the default
15936     */
15937    ;
15938
15939    _proto.updateTime = function updateTime(seekBarRect, seekBarPoint, time, cb) {
15940      var _this2 = this;
15941
15942      // If there is an existing rAF ID, cancel it so we don't over-queue.
15943      if (this.rafId_) {
15944        this.cancelAnimationFrame(this.rafId_);
15945      }
15946
15947      this.rafId_ = this.requestAnimationFrame(function () {
15948        var content;
15949
15950        var duration = _this2.player_.duration();
15951
15952        if (_this2.player_.liveTracker && _this2.player_.liveTracker.isLive()) {
15953          var liveWindow = _this2.player_.liveTracker.liveWindow();
15954
15955          var secondsBehind = liveWindow - seekBarPoint * liveWindow;
15956          content = (secondsBehind < 1 ? '' : '-') + formatTime(secondsBehind, liveWindow);
15957        } else {
15958          content = formatTime(time, duration);
15959        }
15960
15961        _this2.update(seekBarRect, seekBarPoint, content);
15962
15963        if (cb) {
15964          cb();
15965        }
15966      });
15967    };
15968
15969    return TimeTooltip;
15970  }(Component);
15971
15972  Component.registerComponent('TimeTooltip', TimeTooltip);
15973
15974  /**
15975   * Used by {@link SeekBar} to display media playback progress as part of the
15976   * {@link ProgressControl}.
15977   *
15978   * @extends Component
15979   */
15980
15981  var PlayProgressBar =
15982  /*#__PURE__*/
15983  function (_Component) {
15984    _inheritsLoose(PlayProgressBar, _Component);
15985
15986    /**
15987     * Creates an instance of this class.
15988     *
15989     * @param {Player} player
15990     *        The {@link Player} that this class should be attached to.
15991     *
15992     * @param {Object} [options]
15993     *        The key/value store of player options.
15994     */
15995    function PlayProgressBar(player, options) {
15996      var _this;
15997
15998      _this = _Component.call(this, player, options) || this;
15999      _this.update = throttle(bind(_assertThisInitialized(_this), _this.update), UPDATE_REFRESH_INTERVAL);
16000      return _this;
16001    }
16002    /**
16003     * Create the the DOM element for this class.
16004     *
16005     * @return {Element}
16006     *         The element that was created.
16007     */
16008
16009
16010    var _proto = PlayProgressBar.prototype;
16011
16012    _proto.createEl = function createEl() {
16013      return _Component.prototype.createEl.call(this, 'div', {
16014        className: 'vjs-play-progress vjs-slider-bar'
16015      }, {
16016        'aria-hidden': 'true'
16017      });
16018    }
16019    /**
16020     * Enqueues updates to its own DOM as well as the DOM of its
16021     * {@link TimeTooltip} child.
16022     *
16023     * @param {Object} seekBarRect
16024     *        The `ClientRect` for the {@link SeekBar} element.
16025     *
16026     * @param {number} seekBarPoint
16027     *        A number from 0 to 1, representing a horizontal reference point
16028     *        from the left edge of the {@link SeekBar}
16029     */
16030    ;
16031
16032    _proto.update = function update(seekBarRect, seekBarPoint) {
16033      var timeTooltip = this.getChild('timeTooltip');
16034
16035      if (!timeTooltip) {
16036        return;
16037      }
16038
16039      var time = this.player_.scrubbing() ? this.player_.getCache().currentTime : this.player_.currentTime();
16040      timeTooltip.updateTime(seekBarRect, seekBarPoint, time);
16041    };
16042
16043    return PlayProgressBar;
16044  }(Component);
16045  /**
16046   * Default options for {@link PlayProgressBar}.
16047   *
16048   * @type {Object}
16049   * @private
16050   */
16051
16052
16053  PlayProgressBar.prototype.options_ = {
16054    children: []
16055  }; // Time tooltips should not be added to a player on mobile devices
16056
16057  if (!IS_IOS && !IS_ANDROID) {
16058    PlayProgressBar.prototype.options_.children.push('timeTooltip');
16059  }
16060
16061  Component.registerComponent('PlayProgressBar', PlayProgressBar);
16062
16063  /**
16064   * The {@link MouseTimeDisplay} component tracks mouse movement over the
16065   * {@link ProgressControl}. It displays an indicator and a {@link TimeTooltip}
16066   * indicating the time which is represented by a given point in the
16067   * {@link ProgressControl}.
16068   *
16069   * @extends Component
16070   */
16071
16072  var MouseTimeDisplay =
16073  /*#__PURE__*/
16074  function (_Component) {
16075    _inheritsLoose(MouseTimeDisplay, _Component);
16076
16077    /**
16078     * Creates an instance of this class.
16079     *
16080     * @param {Player} player
16081     *        The {@link Player} that this class should be attached to.
16082     *
16083     * @param {Object} [options]
16084     *        The key/value store of player options.
16085     */
16086    function MouseTimeDisplay(player, options) {
16087      var _this;
16088
16089      _this = _Component.call(this, player, options) || this;
16090      _this.update = throttle(bind(_assertThisInitialized(_this), _this.update), UPDATE_REFRESH_INTERVAL);
16091      return _this;
16092    }
16093    /**
16094     * Create the DOM element for this class.
16095     *
16096     * @return {Element}
16097     *         The element that was created.
16098     */
16099
16100
16101    var _proto = MouseTimeDisplay.prototype;
16102
16103    _proto.createEl = function createEl() {
16104      return _Component.prototype.createEl.call(this, 'div', {
16105        className: 'vjs-mouse-display'
16106      });
16107    }
16108    /**
16109     * Enqueues updates to its own DOM as well as the DOM of its
16110     * {@link TimeTooltip} child.
16111     *
16112     * @param {Object} seekBarRect
16113     *        The `ClientRect` for the {@link SeekBar} element.
16114     *
16115     * @param {number} seekBarPoint
16116     *        A number from 0 to 1, representing a horizontal reference point
16117     *        from the left edge of the {@link SeekBar}
16118     */
16119    ;
16120
16121    _proto.update = function update(seekBarRect, seekBarPoint) {
16122      var _this2 = this;
16123
16124      var time = seekBarPoint * this.player_.duration();
16125      this.getChild('timeTooltip').updateTime(seekBarRect, seekBarPoint, time, function () {
16126        _this2.el_.style.left = seekBarRect.width * seekBarPoint + "px";
16127      });
16128    };
16129
16130    return MouseTimeDisplay;
16131  }(Component);
16132  /**
16133   * Default options for `MouseTimeDisplay`
16134   *
16135   * @type {Object}
16136   * @private
16137   */
16138
16139
16140  MouseTimeDisplay.prototype.options_ = {
16141    children: ['timeTooltip']
16142  };
16143  Component.registerComponent('MouseTimeDisplay', MouseTimeDisplay);
16144
16145  var STEP_SECONDS = 5; // The multiplier of STEP_SECONDS that PgUp/PgDown move the timeline.
16146
16147  var PAGE_KEY_MULTIPLIER = 12; // The interval at which the bar should update as it progresses.
16148
16149  var UPDATE_REFRESH_INTERVAL$1 = 30;
16150  /**
16151   * Seek bar and container for the progress bars. Uses {@link PlayProgressBar}
16152   * as its `bar`.
16153   *
16154   * @extends Slider
16155   */
16156
16157  var SeekBar =
16158  /*#__PURE__*/
16159  function (_Slider) {
16160    _inheritsLoose(SeekBar, _Slider);
16161
16162    /**
16163     * Creates an instance of this class.
16164     *
16165     * @param {Player} player
16166     *        The `Player` that this class should be attached to.
16167     *
16168     * @param {Object} [options]
16169     *        The key/value store of player options.
16170     */
16171    function SeekBar(player, options) {
16172      var _this;
16173
16174      _this = _Slider.call(this, player, options) || this;
16175
16176      _this.setEventHandlers_();
16177
16178      return _this;
16179    }
16180    /**
16181     * Sets the event handlers
16182     *
16183     * @private
16184     */
16185
16186
16187    var _proto = SeekBar.prototype;
16188
16189    _proto.setEventHandlers_ = function setEventHandlers_() {
16190      this.update = throttle(bind(this, this.update), UPDATE_REFRESH_INTERVAL$1);
16191      this.on(this.player_, 'timeupdate', this.update);
16192      this.on(this.player_, 'ended', this.handleEnded);
16193      this.on(this.player_, 'durationchange', this.update);
16194
16195      if (this.player_.liveTracker) {
16196        this.on(this.player_.liveTracker, 'liveedgechange', this.update);
16197      } // when playing, let's ensure we smoothly update the play progress bar
16198      // via an interval
16199
16200
16201      this.updateInterval = null;
16202      this.on(this.player_, ['playing'], this.enableInterval_);
16203      this.on(this.player_, ['ended', 'pause', 'waiting'], this.disableInterval_); // we don't need to update the play progress if the document is hidden,
16204      // also, this causes the CPU to spike and eventually crash the page on IE11.
16205
16206      if ('hidden' in document && 'visibilityState' in document) {
16207        this.on(document, 'visibilitychange', this.toggleVisibility_);
16208      }
16209    };
16210
16211    _proto.toggleVisibility_ = function toggleVisibility_(e) {
16212      if (document.hidden) {
16213        this.disableInterval_(e);
16214      } else {
16215        this.enableInterval_(); // we just switched back to the page and someone may be looking, so, update ASAP
16216
16217        this.requestAnimationFrame(this.update);
16218      }
16219    };
16220
16221    _proto.enableInterval_ = function enableInterval_() {
16222      var _this2 = this;
16223
16224      this.clearInterval(this.updateInterval);
16225      this.updateInterval = this.setInterval(function () {
16226        _this2.requestAnimationFrame(_this2.update);
16227      }, UPDATE_REFRESH_INTERVAL$1);
16228    };
16229
16230    _proto.disableInterval_ = function disableInterval_(e) {
16231      if (this.player_.liveTracker && this.player_.liveTracker.isLive() && e.type !== 'ended') {
16232        return;
16233      }
16234
16235      this.clearInterval(this.updateInterval);
16236    }
16237    /**
16238     * Create the `Component`'s DOM element
16239     *
16240     * @return {Element}
16241     *         The element that was created.
16242     */
16243    ;
16244
16245    _proto.createEl = function createEl() {
16246      return _Slider.prototype.createEl.call(this, 'div', {
16247        className: 'vjs-progress-holder'
16248      }, {
16249        'aria-label': this.localize('Progress Bar')
16250      });
16251    }
16252    /**
16253     * This function updates the play progress bar and accessibility
16254     * attributes to whatever is passed in.
16255     *
16256     * @param {number} currentTime
16257     *        The currentTime value that should be used for accessibility
16258     *
16259     * @param {number} percent
16260     *        The percentage as a decimal that the bar should be filled from 0-1.
16261     *
16262     * @private
16263     */
16264    ;
16265
16266    _proto.update_ = function update_(currentTime, percent) {
16267      var liveTracker = this.player_.liveTracker;
16268      var duration = this.player_.duration();
16269
16270      if (liveTracker && liveTracker.isLive()) {
16271        duration = this.player_.liveTracker.liveCurrentTime();
16272      } // machine readable value of progress bar (percentage complete)
16273
16274
16275      this.el_.setAttribute('aria-valuenow', (percent * 100).toFixed(2)); // human readable value of progress bar (time complete)
16276
16277      this.el_.setAttribute('aria-valuetext', this.localize('progress bar timing: currentTime={1} duration={2}', [formatTime(currentTime, duration), formatTime(duration, duration)], '{1} of {2}')); // Update the `PlayProgressBar`.
16278
16279      if (this.bar) {
16280        this.bar.update(getBoundingClientRect(this.el_), percent);
16281      }
16282    }
16283    /**
16284     * Update the seek bar's UI.
16285     *
16286     * @param {EventTarget~Event} [event]
16287     *        The `timeupdate` or `ended` event that caused this to run.
16288     *
16289     * @listens Player#timeupdate
16290     *
16291     * @return {number}
16292     *          The current percent at a number from 0-1
16293     */
16294    ;
16295
16296    _proto.update = function update(event) {
16297      // if the offsetParent is null, then this element is hidden, in which case
16298      // we don't need to update it.
16299      if (this.el().offsetParent === null) {
16300        return;
16301      }
16302
16303      var percent = _Slider.prototype.update.call(this);
16304
16305      this.update_(this.getCurrentTime_(), percent);
16306      return percent;
16307    }
16308    /**
16309     * Get the value of current time but allows for smooth scrubbing,
16310     * when player can't keep up.
16311     *
16312     * @return {number}
16313     *         The current time value to display
16314     *
16315     * @private
16316     */
16317    ;
16318
16319    _proto.getCurrentTime_ = function getCurrentTime_() {
16320      return this.player_.scrubbing() ? this.player_.getCache().currentTime : this.player_.currentTime();
16321    }
16322    /**
16323     * We want the seek bar to be full on ended
16324     * no matter what the actual internal values are. so we force it.
16325     *
16326     * @param {EventTarget~Event} [event]
16327     *        The `timeupdate` or `ended` event that caused this to run.
16328     *
16329     * @listens Player#ended
16330     */
16331    ;
16332
16333    _proto.handleEnded = function handleEnded(event) {
16334      this.update_(this.player_.duration(), 1);
16335    }
16336    /**
16337     * Get the percentage of media played so far.
16338     *
16339     * @return {number}
16340     *         The percentage of media played so far (0 to 1).
16341     */
16342    ;
16343
16344    _proto.getPercent = function getPercent() {
16345      var currentTime = this.getCurrentTime_();
16346      var percent;
16347      var liveTracker = this.player_.liveTracker;
16348
16349      if (liveTracker && liveTracker.isLive()) {
16350        percent = (currentTime - liveTracker.seekableStart()) / liveTracker.liveWindow(); // prevent the percent from changing at the live edge
16351
16352        if (liveTracker.atLiveEdge()) {
16353          percent = 1;
16354        }
16355      } else {
16356        percent = currentTime / this.player_.duration();
16357      }
16358
16359      return percent >= 1 ? 1 : percent || 0;
16360    }
16361    /**
16362     * Handle mouse down on seek bar
16363     *
16364     * @param {EventTarget~Event} event
16365     *        The `mousedown` event that caused this to run.
16366     *
16367     * @listens mousedown
16368     */
16369    ;
16370
16371    _proto.handleMouseDown = function handleMouseDown(event) {
16372      if (!isSingleLeftClick(event)) {
16373        return;
16374      } // Stop event propagation to prevent double fire in progress-control.js
16375
16376
16377      event.stopPropagation();
16378      this.player_.scrubbing(true);
16379      this.videoWasPlaying = !this.player_.paused();
16380      this.player_.pause();
16381
16382      _Slider.prototype.handleMouseDown.call(this, event);
16383    }
16384    /**
16385     * Handle mouse move on seek bar
16386     *
16387     * @param {EventTarget~Event} event
16388     *        The `mousemove` event that caused this to run.
16389     *
16390     * @listens mousemove
16391     */
16392    ;
16393
16394    _proto.handleMouseMove = function handleMouseMove(event) {
16395      if (!isSingleLeftClick(event)) {
16396        return;
16397      }
16398
16399      var newTime;
16400      var distance = this.calculateDistance(event);
16401      var liveTracker = this.player_.liveTracker;
16402
16403      if (!liveTracker || !liveTracker.isLive()) {
16404        newTime = distance * this.player_.duration(); // Don't let video end while scrubbing.
16405
16406        if (newTime === this.player_.duration()) {
16407          newTime = newTime - 0.1;
16408        }
16409      } else {
16410        var seekableStart = liveTracker.seekableStart();
16411        var seekableEnd = liveTracker.liveCurrentTime();
16412        newTime = seekableStart + distance * liveTracker.liveWindow(); // Don't let video end while scrubbing.
16413
16414        if (newTime >= seekableEnd) {
16415          newTime = seekableEnd;
16416        } // Compensate for precision differences so that currentTime is not less
16417        // than seekable start
16418
16419
16420        if (newTime <= seekableStart) {
16421          newTime = seekableStart + 0.1;
16422        } // On android seekableEnd can be Infinity sometimes,
16423        // this will cause newTime to be Infinity, which is
16424        // not a valid currentTime.
16425
16426
16427        if (newTime === Infinity) {
16428          return;
16429        }
16430      } // Set new time (tell player to seek to new time)
16431
16432
16433      this.player_.currentTime(newTime);
16434    };
16435
16436    _proto.enable = function enable() {
16437      _Slider.prototype.enable.call(this);
16438
16439      var mouseTimeDisplay = this.getChild('mouseTimeDisplay');
16440
16441      if (!mouseTimeDisplay) {
16442        return;
16443      }
16444
16445      mouseTimeDisplay.show();
16446    };
16447
16448    _proto.disable = function disable() {
16449      _Slider.prototype.disable.call(this);
16450
16451      var mouseTimeDisplay = this.getChild('mouseTimeDisplay');
16452
16453      if (!mouseTimeDisplay) {
16454        return;
16455      }
16456
16457      mouseTimeDisplay.hide();
16458    }
16459    /**
16460     * Handle mouse up on seek bar
16461     *
16462     * @param {EventTarget~Event} event
16463     *        The `mouseup` event that caused this to run.
16464     *
16465     * @listens mouseup
16466     */
16467    ;
16468
16469    _proto.handleMouseUp = function handleMouseUp(event) {
16470      _Slider.prototype.handleMouseUp.call(this, event); // Stop event propagation to prevent double fire in progress-control.js
16471
16472
16473      if (event) {
16474        event.stopPropagation();
16475      }
16476
16477      this.player_.scrubbing(false);
16478      /**
16479       * Trigger timeupdate because we're done seeking and the time has changed.
16480       * This is particularly useful for if the player is paused to time the time displays.
16481       *
16482       * @event Tech#timeupdate
16483       * @type {EventTarget~Event}
16484       */
16485
16486      this.player_.trigger({
16487        type: 'timeupdate',
16488        target: this,
16489        manuallyTriggered: true
16490      });
16491
16492      if (this.videoWasPlaying) {
16493        silencePromise(this.player_.play());
16494      }
16495    }
16496    /**
16497     * Move more quickly fast forward for keyboard-only users
16498     */
16499    ;
16500
16501    _proto.stepForward = function stepForward() {
16502      this.player_.currentTime(this.player_.currentTime() + STEP_SECONDS);
16503    }
16504    /**
16505     * Move more quickly rewind for keyboard-only users
16506     */
16507    ;
16508
16509    _proto.stepBack = function stepBack() {
16510      this.player_.currentTime(this.player_.currentTime() - STEP_SECONDS);
16511    }
16512    /**
16513     * Toggles the playback state of the player
16514     * This gets called when enter or space is used on the seekbar
16515     *
16516     * @param {EventTarget~Event} event
16517     *        The `keydown` event that caused this function to be called
16518     *
16519     */
16520    ;
16521
16522    _proto.handleAction = function handleAction(event) {
16523      if (this.player_.paused()) {
16524        this.player_.play();
16525      } else {
16526        this.player_.pause();
16527      }
16528    }
16529    /**
16530     * Called when this SeekBar has focus and a key gets pressed down.
16531     * Supports the following keys:
16532     *
16533     *   Space or Enter key fire a click event
16534     *   Home key moves to start of the timeline
16535     *   End key moves to end of the timeline
16536     *   Digit "0" through "9" keys move to 0%, 10% ... 80%, 90% of the timeline
16537     *   PageDown key moves back a larger step than ArrowDown
16538     *   PageUp key moves forward a large step
16539     *
16540     * @param {EventTarget~Event} event
16541     *        The `keydown` event that caused this function to be called.
16542     *
16543     * @listens keydown
16544     */
16545    ;
16546
16547    _proto.handleKeyDown = function handleKeyDown(event) {
16548      if (keycode.isEventKey(event, 'Space') || keycode.isEventKey(event, 'Enter')) {
16549        event.preventDefault();
16550        event.stopPropagation();
16551        this.handleAction(event);
16552      } else if (keycode.isEventKey(event, 'Home')) {
16553        event.preventDefault();
16554        event.stopPropagation();
16555        this.player_.currentTime(0);
16556      } else if (keycode.isEventKey(event, 'End')) {
16557        event.preventDefault();
16558        event.stopPropagation();
16559        this.player_.currentTime(this.player_.duration());
16560      } else if (/^[0-9]$/.test(keycode(event))) {
16561        event.preventDefault();
16562        event.stopPropagation();
16563        var gotoFraction = (keycode.codes[keycode(event)] - keycode.codes['0']) * 10.0 / 100.0;
16564        this.player_.currentTime(this.player_.duration() * gotoFraction);
16565      } else if (keycode.isEventKey(event, 'PgDn')) {
16566        event.preventDefault();
16567        event.stopPropagation();
16568        this.player_.currentTime(this.player_.currentTime() - STEP_SECONDS * PAGE_KEY_MULTIPLIER);
16569      } else if (keycode.isEventKey(event, 'PgUp')) {
16570        event.preventDefault();
16571        event.stopPropagation();
16572        this.player_.currentTime(this.player_.currentTime() + STEP_SECONDS * PAGE_KEY_MULTIPLIER);
16573      } else {
16574        // Pass keydown handling up for unsupported keys
16575        _Slider.prototype.handleKeyDown.call(this, event);
16576      }
16577    };
16578
16579    return SeekBar;
16580  }(Slider);
16581  /**
16582   * Default options for the `SeekBar`
16583   *
16584   * @type {Object}
16585   * @private
16586   */
16587
16588
16589  SeekBar.prototype.options_ = {
16590    children: ['loadProgressBar', 'playProgressBar'],
16591    barName: 'playProgressBar'
16592  }; // MouseTimeDisplay tooltips should not be added to a player on mobile devices
16593
16594  if (!IS_IOS && !IS_ANDROID) {
16595    SeekBar.prototype.options_.children.splice(1, 0, 'mouseTimeDisplay');
16596  }
16597
16598  Component.registerComponent('SeekBar', SeekBar);
16599
16600  /**
16601   * The Progress Control component contains the seek bar, load progress,
16602   * and play progress.
16603   *
16604   * @extends Component
16605   */
16606
16607  var ProgressControl =
16608  /*#__PURE__*/
16609  function (_Component) {
16610    _inheritsLoose(ProgressControl, _Component);
16611
16612    /**
16613     * Creates an instance of this class.
16614     *
16615     * @param {Player} player
16616     *        The `Player` that this class should be attached to.
16617     *
16618     * @param {Object} [options]
16619     *        The key/value store of player options.
16620     */
16621    function ProgressControl(player, options) {
16622      var _this;
16623
16624      _this = _Component.call(this, player, options) || this;
16625      _this.handleMouseMove = throttle(bind(_assertThisInitialized(_this), _this.handleMouseMove), UPDATE_REFRESH_INTERVAL);
16626      _this.throttledHandleMouseSeek = throttle(bind(_assertThisInitialized(_this), _this.handleMouseSeek), UPDATE_REFRESH_INTERVAL);
16627
16628      _this.enable();
16629
16630      return _this;
16631    }
16632    /**
16633     * Create the `Component`'s DOM element
16634     *
16635     * @return {Element}
16636     *         The element that was created.
16637     */
16638
16639
16640    var _proto = ProgressControl.prototype;
16641
16642    _proto.createEl = function createEl() {
16643      return _Component.prototype.createEl.call(this, 'div', {
16644        className: 'vjs-progress-control vjs-control'
16645      });
16646    }
16647    /**
16648     * When the mouse moves over the `ProgressControl`, the pointer position
16649     * gets passed down to the `MouseTimeDisplay` component.
16650     *
16651     * @param {EventTarget~Event} event
16652     *        The `mousemove` event that caused this function to run.
16653     *
16654     * @listen mousemove
16655     */
16656    ;
16657
16658    _proto.handleMouseMove = function handleMouseMove(event) {
16659      var seekBar = this.getChild('seekBar');
16660
16661      if (seekBar) {
16662        var mouseTimeDisplay = seekBar.getChild('mouseTimeDisplay');
16663        var seekBarEl = seekBar.el();
16664        var seekBarRect = getBoundingClientRect(seekBarEl);
16665        var seekBarPoint = getPointerPosition(seekBarEl, event).x; // The default skin has a gap on either side of the `SeekBar`. This means
16666        // that it's possible to trigger this behavior outside the boundaries of
16667        // the `SeekBar`. This ensures we stay within it at all times.
16668
16669        if (seekBarPoint > 1) {
16670          seekBarPoint = 1;
16671        } else if (seekBarPoint < 0) {
16672          seekBarPoint = 0;
16673        }
16674
16675        if (mouseTimeDisplay) {
16676          mouseTimeDisplay.update(seekBarRect, seekBarPoint);
16677        }
16678      }
16679    }
16680    /**
16681     * A throttled version of the {@link ProgressControl#handleMouseSeek} listener.
16682     *
16683     * @method ProgressControl#throttledHandleMouseSeek
16684     * @param {EventTarget~Event} event
16685     *        The `mousemove` event that caused this function to run.
16686     *
16687     * @listen mousemove
16688     * @listen touchmove
16689     */
16690
16691    /**
16692     * Handle `mousemove` or `touchmove` events on the `ProgressControl`.
16693     *
16694     * @param {EventTarget~Event} event
16695     *        `mousedown` or `touchstart` event that triggered this function
16696     *
16697     * @listens mousemove
16698     * @listens touchmove
16699     */
16700    ;
16701
16702    _proto.handleMouseSeek = function handleMouseSeek(event) {
16703      var seekBar = this.getChild('seekBar');
16704
16705      if (seekBar) {
16706        seekBar.handleMouseMove(event);
16707      }
16708    }
16709    /**
16710     * Are controls are currently enabled for this progress control.
16711     *
16712     * @return {boolean}
16713     *         true if controls are enabled, false otherwise
16714     */
16715    ;
16716
16717    _proto.enabled = function enabled() {
16718      return this.enabled_;
16719    }
16720    /**
16721     * Disable all controls on the progress control and its children
16722     */
16723    ;
16724
16725    _proto.disable = function disable() {
16726      this.children().forEach(function (child) {
16727        return child.disable && child.disable();
16728      });
16729
16730      if (!this.enabled()) {
16731        return;
16732      }
16733
16734      this.off(['mousedown', 'touchstart'], this.handleMouseDown);
16735      this.off(this.el_, 'mousemove', this.handleMouseMove);
16736      this.handleMouseUp();
16737      this.addClass('disabled');
16738      this.enabled_ = false;
16739    }
16740    /**
16741     * Enable all controls on the progress control and its children
16742     */
16743    ;
16744
16745    _proto.enable = function enable() {
16746      this.children().forEach(function (child) {
16747        return child.enable && child.enable();
16748      });
16749
16750      if (this.enabled()) {
16751        return;
16752      }
16753
16754      this.on(['mousedown', 'touchstart'], this.handleMouseDown);
16755      this.on(this.el_, 'mousemove', this.handleMouseMove);
16756      this.removeClass('disabled');
16757      this.enabled_ = true;
16758    }
16759    /**
16760     * Handle `mousedown` or `touchstart` events on the `ProgressControl`.
16761     *
16762     * @param {EventTarget~Event} event
16763     *        `mousedown` or `touchstart` event that triggered this function
16764     *
16765     * @listens mousedown
16766     * @listens touchstart
16767     */
16768    ;
16769
16770    _proto.handleMouseDown = function handleMouseDown(event) {
16771      var doc = this.el_.ownerDocument;
16772      var seekBar = this.getChild('seekBar');
16773
16774      if (seekBar) {
16775        seekBar.handleMouseDown(event);
16776      }
16777
16778      this.on(doc, 'mousemove', this.throttledHandleMouseSeek);
16779      this.on(doc, 'touchmove', this.throttledHandleMouseSeek);
16780      this.on(doc, 'mouseup', this.handleMouseUp);
16781      this.on(doc, 'touchend', this.handleMouseUp);
16782    }
16783    /**
16784     * Handle `mouseup` or `touchend` events on the `ProgressControl`.
16785     *
16786     * @param {EventTarget~Event} event
16787     *        `mouseup` or `touchend` event that triggered this function.
16788     *
16789     * @listens touchend
16790     * @listens mouseup
16791     */
16792    ;
16793
16794    _proto.handleMouseUp = function handleMouseUp(event) {
16795      var doc = this.el_.ownerDocument;
16796      var seekBar = this.getChild('seekBar');
16797
16798      if (seekBar) {
16799        seekBar.handleMouseUp(event);
16800      }
16801
16802      this.off(doc, 'mousemove', this.throttledHandleMouseSeek);
16803      this.off(doc, 'touchmove', this.throttledHandleMouseSeek);
16804      this.off(doc, 'mouseup', this.handleMouseUp);
16805      this.off(doc, 'touchend', this.handleMouseUp);
16806    };
16807
16808    return ProgressControl;
16809  }(Component);
16810  /**
16811   * Default options for `ProgressControl`
16812   *
16813   * @type {Object}
16814   * @private
16815   */
16816
16817
16818  ProgressControl.prototype.options_ = {
16819    children: ['seekBar']
16820  };
16821  Component.registerComponent('ProgressControl', ProgressControl);
16822
16823  /**
16824   * Toggle Picture-in-Picture mode
16825   *
16826   * @extends Button
16827   */
16828
16829  var PictureInPictureToggle =
16830  /*#__PURE__*/
16831  function (_Button) {
16832    _inheritsLoose(PictureInPictureToggle, _Button);
16833
16834    /**
16835     * Creates an instance of this class.
16836     *
16837     * @param {Player} player
16838     *        The `Player` that this class should be attached to.
16839     *
16840     * @param {Object} [options]
16841     *        The key/value store of player options.
16842     *
16843     * @listens Player#enterpictureinpicture
16844     * @listens Player#leavepictureinpicture
16845     */
16846    function PictureInPictureToggle(player, options) {
16847      var _this;
16848
16849      _this = _Button.call(this, player, options) || this;
16850
16851      _this.on(player, ['enterpictureinpicture', 'leavepictureinpicture'], _this.handlePictureInPictureChange); // TODO: Activate button on player loadedmetadata event.
16852      // TODO: Deactivate button on player emptied event.
16853      // TODO: Deactivate button if disablepictureinpicture attribute is present.
16854
16855
16856      if (!document.pictureInPictureEnabled) {
16857        _this.disable();
16858      }
16859
16860      return _this;
16861    }
16862    /**
16863     * Builds the default DOM `className`.
16864     *
16865     * @return {string}
16866     *         The DOM `className` for this object.
16867     */
16868
16869
16870    var _proto = PictureInPictureToggle.prototype;
16871
16872    _proto.buildCSSClass = function buildCSSClass() {
16873      return "vjs-picture-in-picture-control " + _Button.prototype.buildCSSClass.call(this);
16874    }
16875    /**
16876     * Handles enterpictureinpicture and leavepictureinpicture on the player and change control text accordingly.
16877     *
16878     * @param {EventTarget~Event} [event]
16879     *        The {@link Player#enterpictureinpicture} or {@link Player#leavepictureinpicture} event that caused this function to be
16880     *        called.
16881     *
16882     * @listens Player#enterpictureinpicture
16883     * @listens Player#leavepictureinpicture
16884     */
16885    ;
16886
16887    _proto.handlePictureInPictureChange = function handlePictureInPictureChange(event) {
16888      if (this.player_.isInPictureInPicture()) {
16889        this.controlText('Exit Picture-in-Picture');
16890      } else {
16891        this.controlText('Picture-in-Picture');
16892      }
16893    }
16894    /**
16895     * This gets called when an `PictureInPictureToggle` is "clicked". See
16896     * {@link ClickableComponent} for more detailed information on what a click can be.
16897     *
16898     * @param {EventTarget~Event} [event]
16899     *        The `keydown`, `tap`, or `click` event that caused this function to be
16900     *        called.
16901     *
16902     * @listens tap
16903     * @listens click
16904     */
16905    ;
16906
16907    _proto.handleClick = function handleClick(event) {
16908      if (!this.player_.isInPictureInPicture()) {
16909        this.player_.requestPictureInPicture();
16910      } else {
16911        this.player_.exitPictureInPicture();
16912      }
16913    };
16914
16915    return PictureInPictureToggle;
16916  }(Button);
16917  /**
16918   * The text that should display over the `PictureInPictureToggle`s controls. Added for localization.
16919   *
16920   * @type {string}
16921   * @private
16922   */
16923
16924
16925  PictureInPictureToggle.prototype.controlText_ = 'Picture-in-Picture';
16926  Component.registerComponent('PictureInPictureToggle', PictureInPictureToggle);
16927
16928  /**
16929   * Toggle fullscreen video
16930   *
16931   * @extends Button
16932   */
16933
16934  var FullscreenToggle =
16935  /*#__PURE__*/
16936  function (_Button) {
16937    _inheritsLoose(FullscreenToggle, _Button);
16938
16939    /**
16940     * Creates an instance of this class.
16941     *
16942     * @param {Player} player
16943     *        The `Player` that this class should be attached to.
16944     *
16945     * @param {Object} [options]
16946     *        The key/value store of player options.
16947     */
16948    function FullscreenToggle(player, options) {
16949      var _this;
16950
16951      _this = _Button.call(this, player, options) || this;
16952
16953      _this.on(player, 'fullscreenchange', _this.handleFullscreenChange);
16954
16955      if (document[player.fsApi_.fullscreenEnabled] === false) {
16956        _this.disable();
16957      }
16958
16959      return _this;
16960    }
16961    /**
16962     * Builds the default DOM `className`.
16963     *
16964     * @return {string}
16965     *         The DOM `className` for this object.
16966     */
16967
16968
16969    var _proto = FullscreenToggle.prototype;
16970
16971    _proto.buildCSSClass = function buildCSSClass() {
16972      return "vjs-fullscreen-control " + _Button.prototype.buildCSSClass.call(this);
16973    }
16974    /**
16975     * Handles fullscreenchange on the player and change control text accordingly.
16976     *
16977     * @param {EventTarget~Event} [event]
16978     *        The {@link Player#fullscreenchange} event that caused this function to be
16979     *        called.
16980     *
16981     * @listens Player#fullscreenchange
16982     */
16983    ;
16984
16985    _proto.handleFullscreenChange = function handleFullscreenChange(event) {
16986      if (this.player_.isFullscreen()) {
16987        this.controlText('Non-Fullscreen');
16988      } else {
16989        this.controlText('Fullscreen');
16990      }
16991    }
16992    /**
16993     * This gets called when an `FullscreenToggle` is "clicked". See
16994     * {@link ClickableComponent} for more detailed information on what a click can be.
16995     *
16996     * @param {EventTarget~Event} [event]
16997     *        The `keydown`, `tap`, or `click` event that caused this function to be
16998     *        called.
16999     *
17000     * @listens tap
17001     * @listens click
17002     */
17003    ;
17004
17005    _proto.handleClick = function handleClick(event) {
17006      if (!this.player_.isFullscreen()) {
17007        this.player_.requestFullscreen();
17008      } else {
17009        this.player_.exitFullscreen();
17010      }
17011    };
17012
17013    return FullscreenToggle;
17014  }(Button);
17015  /**
17016   * The text that should display over the `FullscreenToggle`s controls. Added for localization.
17017   *
17018   * @type {string}
17019   * @private
17020   */
17021
17022
17023  FullscreenToggle.prototype.controlText_ = 'Fullscreen';
17024  Component.registerComponent('FullscreenToggle', FullscreenToggle);
17025
17026  /**
17027   * Check if volume control is supported and if it isn't hide the
17028   * `Component` that was passed  using the `vjs-hidden` class.
17029   *
17030   * @param {Component} self
17031   *        The component that should be hidden if volume is unsupported
17032   *
17033   * @param {Player} player
17034   *        A reference to the player
17035   *
17036   * @private
17037   */
17038  var checkVolumeSupport = function checkVolumeSupport(self, player) {
17039    // hide volume controls when they're not supported by the current tech
17040    if (player.tech_ && !player.tech_.featuresVolumeControl) {
17041      self.addClass('vjs-hidden');
17042    }
17043
17044    self.on(player, 'loadstart', function () {
17045      if (!player.tech_.featuresVolumeControl) {
17046        self.addClass('vjs-hidden');
17047      } else {
17048        self.removeClass('vjs-hidden');
17049      }
17050    });
17051  };
17052
17053  /**
17054   * Shows volume level
17055   *
17056   * @extends Component
17057   */
17058
17059  var VolumeLevel =
17060  /*#__PURE__*/
17061  function (_Component) {
17062    _inheritsLoose(VolumeLevel, _Component);
17063
17064    function VolumeLevel() {
17065      return _Component.apply(this, arguments) || this;
17066    }
17067
17068    var _proto = VolumeLevel.prototype;
17069
17070    /**
17071     * Create the `Component`'s DOM element
17072     *
17073     * @return {Element}
17074     *         The element that was created.
17075     */
17076    _proto.createEl = function createEl() {
17077      return _Component.prototype.createEl.call(this, 'div', {
17078        className: 'vjs-volume-level',
17079        innerHTML: '<span class="vjs-control-text"></span>'
17080      });
17081    };
17082
17083    return VolumeLevel;
17084  }(Component);
17085
17086  Component.registerComponent('VolumeLevel', VolumeLevel);
17087
17088  /**
17089   * The bar that contains the volume level and can be clicked on to adjust the level
17090   *
17091   * @extends Slider
17092   */
17093
17094  var VolumeBar =
17095  /*#__PURE__*/
17096  function (_Slider) {
17097    _inheritsLoose(VolumeBar, _Slider);
17098
17099    /**
17100     * Creates an instance of this class.
17101     *
17102     * @param {Player} player
17103     *        The `Player` that this class should be attached to.
17104     *
17105     * @param {Object} [options]
17106     *        The key/value store of player options.
17107     */
17108    function VolumeBar(player, options) {
17109      var _this;
17110
17111      _this = _Slider.call(this, player, options) || this;
17112
17113      _this.on('slideractive', _this.updateLastVolume_);
17114
17115      _this.on(player, 'volumechange', _this.updateARIAAttributes);
17116
17117      player.ready(function () {
17118        return _this.updateARIAAttributes();
17119      });
17120      return _this;
17121    }
17122    /**
17123     * Create the `Component`'s DOM element
17124     *
17125     * @return {Element}
17126     *         The element that was created.
17127     */
17128
17129
17130    var _proto = VolumeBar.prototype;
17131
17132    _proto.createEl = function createEl() {
17133      return _Slider.prototype.createEl.call(this, 'div', {
17134        className: 'vjs-volume-bar vjs-slider-bar'
17135      }, {
17136        'aria-label': this.localize('Volume Level'),
17137        'aria-live': 'polite'
17138      });
17139    }
17140    /**
17141     * Handle mouse down on volume bar
17142     *
17143     * @param {EventTarget~Event} event
17144     *        The `mousedown` event that caused this to run.
17145     *
17146     * @listens mousedown
17147     */
17148    ;
17149
17150    _proto.handleMouseDown = function handleMouseDown(event) {
17151      if (!isSingleLeftClick(event)) {
17152        return;
17153      }
17154
17155      _Slider.prototype.handleMouseDown.call(this, event);
17156    }
17157    /**
17158     * Handle movement events on the {@link VolumeMenuButton}.
17159     *
17160     * @param {EventTarget~Event} event
17161     *        The event that caused this function to run.
17162     *
17163     * @listens mousemove
17164     */
17165    ;
17166
17167    _proto.handleMouseMove = function handleMouseMove(event) {
17168      if (!isSingleLeftClick(event)) {
17169        return;
17170      }
17171
17172      this.checkMuted();
17173      this.player_.volume(this.calculateDistance(event));
17174    }
17175    /**
17176     * If the player is muted unmute it.
17177     */
17178    ;
17179
17180    _proto.checkMuted = function checkMuted() {
17181      if (this.player_.muted()) {
17182        this.player_.muted(false);
17183      }
17184    }
17185    /**
17186     * Get percent of volume level
17187     *
17188     * @return {number}
17189     *         Volume level percent as a decimal number.
17190     */
17191    ;
17192
17193    _proto.getPercent = function getPercent() {
17194      if (this.player_.muted()) {
17195        return 0;
17196      }
17197
17198      return this.player_.volume();
17199    }
17200    /**
17201     * Increase volume level for keyboard users
17202     */
17203    ;
17204
17205    _proto.stepForward = function stepForward() {
17206      this.checkMuted();
17207      this.player_.volume(this.player_.volume() + 0.1);
17208    }
17209    /**
17210     * Decrease volume level for keyboard users
17211     */
17212    ;
17213
17214    _proto.stepBack = function stepBack() {
17215      this.checkMuted();
17216      this.player_.volume(this.player_.volume() - 0.1);
17217    }
17218    /**
17219     * Update ARIA accessibility attributes
17220     *
17221     * @param {EventTarget~Event} [event]
17222     *        The `volumechange` event that caused this function to run.
17223     *
17224     * @listens Player#volumechange
17225     */
17226    ;
17227
17228    _proto.updateARIAAttributes = function updateARIAAttributes(event) {
17229      var ariaValue = this.player_.muted() ? 0 : this.volumeAsPercentage_();
17230      this.el_.setAttribute('aria-valuenow', ariaValue);
17231      this.el_.setAttribute('aria-valuetext', ariaValue + '%');
17232    }
17233    /**
17234     * Returns the current value of the player volume as a percentage
17235     *
17236     * @private
17237     */
17238    ;
17239
17240    _proto.volumeAsPercentage_ = function volumeAsPercentage_() {
17241      return Math.round(this.player_.volume() * 100);
17242    }
17243    /**
17244     * When user starts dragging the VolumeBar, store the volume and listen for
17245     * the end of the drag. When the drag ends, if the volume was set to zero,
17246     * set lastVolume to the stored volume.
17247     *
17248     * @listens slideractive
17249     * @private
17250     */
17251    ;
17252
17253    _proto.updateLastVolume_ = function updateLastVolume_() {
17254      var _this2 = this;
17255
17256      var volumeBeforeDrag = this.player_.volume();
17257      this.one('sliderinactive', function () {
17258        if (_this2.player_.volume() === 0) {
17259          _this2.player_.lastVolume_(volumeBeforeDrag);
17260        }
17261      });
17262    };
17263
17264    return VolumeBar;
17265  }(Slider);
17266  /**
17267   * Default options for the `VolumeBar`
17268   *
17269   * @type {Object}
17270   * @private
17271   */
17272
17273
17274  VolumeBar.prototype.options_ = {
17275    children: ['volumeLevel'],
17276    barName: 'volumeLevel'
17277  };
17278  /**
17279   * Call the update event for this Slider when this event happens on the player.
17280   *
17281   * @type {string}
17282   */
17283
17284  VolumeBar.prototype.playerEvent = 'volumechange';
17285  Component.registerComponent('VolumeBar', VolumeBar);
17286
17287  /**
17288   * The component for controlling the volume level
17289   *
17290   * @extends Component
17291   */
17292
17293  var VolumeControl =
17294  /*#__PURE__*/
17295  function (_Component) {
17296    _inheritsLoose(VolumeControl, _Component);
17297
17298    /**
17299     * Creates an instance of this class.
17300     *
17301     * @param {Player} player
17302     *        The `Player` that this class should be attached to.
17303     *
17304     * @param {Object} [options={}]
17305     *        The key/value store of player options.
17306     */
17307    function VolumeControl(player, options) {
17308      var _this;
17309
17310      if (options === void 0) {
17311        options = {};
17312      }
17313
17314      options.vertical = options.vertical || false; // Pass the vertical option down to the VolumeBar if
17315      // the VolumeBar is turned on.
17316
17317      if (typeof options.volumeBar === 'undefined' || isPlain(options.volumeBar)) {
17318        options.volumeBar = options.volumeBar || {};
17319        options.volumeBar.vertical = options.vertical;
17320      }
17321
17322      _this = _Component.call(this, player, options) || this; // hide this control if volume support is missing
17323
17324      checkVolumeSupport(_assertThisInitialized(_this), player);
17325      _this.throttledHandleMouseMove = throttle(bind(_assertThisInitialized(_this), _this.handleMouseMove), UPDATE_REFRESH_INTERVAL);
17326
17327      _this.on('mousedown', _this.handleMouseDown);
17328
17329      _this.on('touchstart', _this.handleMouseDown); // while the slider is active (the mouse has been pressed down and
17330      // is dragging) or in focus we do not want to hide the VolumeBar
17331
17332
17333      _this.on(_this.volumeBar, ['focus', 'slideractive'], function () {
17334        _this.volumeBar.addClass('vjs-slider-active');
17335
17336        _this.addClass('vjs-slider-active');
17337
17338        _this.trigger('slideractive');
17339      });
17340
17341      _this.on(_this.volumeBar, ['blur', 'sliderinactive'], function () {
17342        _this.volumeBar.removeClass('vjs-slider-active');
17343
17344        _this.removeClass('vjs-slider-active');
17345
17346        _this.trigger('sliderinactive');
17347      });
17348
17349      return _this;
17350    }
17351    /**
17352     * Create the `Component`'s DOM element
17353     *
17354     * @return {Element}
17355     *         The element that was created.
17356     */
17357
17358
17359    var _proto = VolumeControl.prototype;
17360
17361    _proto.createEl = function createEl() {
17362      var orientationClass = 'vjs-volume-horizontal';
17363
17364      if (this.options_.vertical) {
17365        orientationClass = 'vjs-volume-vertical';
17366      }
17367
17368      return _Component.prototype.createEl.call(this, 'div', {
17369        className: "vjs-volume-control vjs-control " + orientationClass
17370      });
17371    }
17372    /**
17373     * Handle `mousedown` or `touchstart` events on the `VolumeControl`.
17374     *
17375     * @param {EventTarget~Event} event
17376     *        `mousedown` or `touchstart` event that triggered this function
17377     *
17378     * @listens mousedown
17379     * @listens touchstart
17380     */
17381    ;
17382
17383    _proto.handleMouseDown = function handleMouseDown(event) {
17384      var doc = this.el_.ownerDocument;
17385      this.on(doc, 'mousemove', this.throttledHandleMouseMove);
17386      this.on(doc, 'touchmove', this.throttledHandleMouseMove);
17387      this.on(doc, 'mouseup', this.handleMouseUp);
17388      this.on(doc, 'touchend', this.handleMouseUp);
17389    }
17390    /**
17391     * Handle `mouseup` or `touchend` events on the `VolumeControl`.
17392     *
17393     * @param {EventTarget~Event} event
17394     *        `mouseup` or `touchend` event that triggered this function.
17395     *
17396     * @listens touchend
17397     * @listens mouseup
17398     */
17399    ;
17400
17401    _proto.handleMouseUp = function handleMouseUp(event) {
17402      var doc = this.el_.ownerDocument;
17403      this.off(doc, 'mousemove', this.throttledHandleMouseMove);
17404      this.off(doc, 'touchmove', this.throttledHandleMouseMove);
17405      this.off(doc, 'mouseup', this.handleMouseUp);
17406      this.off(doc, 'touchend', this.handleMouseUp);
17407    }
17408    /**
17409     * Handle `mousedown` or `touchstart` events on the `VolumeControl`.
17410     *
17411     * @param {EventTarget~Event} event
17412     *        `mousedown` or `touchstart` event that triggered this function
17413     *
17414     * @listens mousedown
17415     * @listens touchstart
17416     */
17417    ;
17418
17419    _proto.handleMouseMove = function handleMouseMove(event) {
17420      this.volumeBar.handleMouseMove(event);
17421    };
17422
17423    return VolumeControl;
17424  }(Component);
17425  /**
17426   * Default options for the `VolumeControl`
17427   *
17428   * @type {Object}
17429   * @private
17430   */
17431
17432
17433  VolumeControl.prototype.options_ = {
17434    children: ['volumeBar']
17435  };
17436  Component.registerComponent('VolumeControl', VolumeControl);
17437
17438  /**
17439   * Check if muting volume is supported and if it isn't hide the mute toggle
17440   * button.
17441   *
17442   * @param {Component} self
17443   *        A reference to the mute toggle button
17444   *
17445   * @param {Player} player
17446   *        A reference to the player
17447   *
17448   * @private
17449   */
17450  var checkMuteSupport = function checkMuteSupport(self, player) {
17451    // hide mute toggle button if it's not supported by the current tech
17452    if (player.tech_ && !player.tech_.featuresMuteControl) {
17453      self.addClass('vjs-hidden');
17454    }
17455
17456    self.on(player, 'loadstart', function () {
17457      if (!player.tech_.featuresMuteControl) {
17458        self.addClass('vjs-hidden');
17459      } else {
17460        self.removeClass('vjs-hidden');
17461      }
17462    });
17463  };
17464
17465  /**
17466   * A button component for muting the audio.
17467   *
17468   * @extends Button
17469   */
17470
17471  var MuteToggle =
17472  /*#__PURE__*/
17473  function (_Button) {
17474    _inheritsLoose(MuteToggle, _Button);
17475
17476    /**
17477     * Creates an instance of this class.
17478     *
17479     * @param {Player} player
17480     *        The `Player` that this class should be attached to.
17481     *
17482     * @param {Object} [options]
17483     *        The key/value store of player options.
17484     */
17485    function MuteToggle(player, options) {
17486      var _this;
17487
17488      _this = _Button.call(this, player, options) || this; // hide this control if volume support is missing
17489
17490      checkMuteSupport(_assertThisInitialized(_this), player);
17491
17492      _this.on(player, ['loadstart', 'volumechange'], _this.update);
17493
17494      return _this;
17495    }
17496    /**
17497     * Builds the default DOM `className`.
17498     *
17499     * @return {string}
17500     *         The DOM `className` for this object.
17501     */
17502
17503
17504    var _proto = MuteToggle.prototype;
17505
17506    _proto.buildCSSClass = function buildCSSClass() {
17507      return "vjs-mute-control " + _Button.prototype.buildCSSClass.call(this);
17508    }
17509    /**
17510     * This gets called when an `MuteToggle` is "clicked". See
17511     * {@link ClickableComponent} for more detailed information on what a click can be.
17512     *
17513     * @param {EventTarget~Event} [event]
17514     *        The `keydown`, `tap`, or `click` event that caused this function to be
17515     *        called.
17516     *
17517     * @listens tap
17518     * @listens click
17519     */
17520    ;
17521
17522    _proto.handleClick = function handleClick(event) {
17523      var vol = this.player_.volume();
17524      var lastVolume = this.player_.lastVolume_();
17525
17526      if (vol === 0) {
17527        var volumeToSet = lastVolume < 0.1 ? 0.1 : lastVolume;
17528        this.player_.volume(volumeToSet);
17529        this.player_.muted(false);
17530      } else {
17531        this.player_.muted(this.player_.muted() ? false : true);
17532      }
17533    }
17534    /**
17535     * Update the `MuteToggle` button based on the state of `volume` and `muted`
17536     * on the player.
17537     *
17538     * @param {EventTarget~Event} [event]
17539     *        The {@link Player#loadstart} event if this function was called
17540     *        through an event.
17541     *
17542     * @listens Player#loadstart
17543     * @listens Player#volumechange
17544     */
17545    ;
17546
17547    _proto.update = function update(event) {
17548      this.updateIcon_();
17549      this.updateControlText_();
17550    }
17551    /**
17552     * Update the appearance of the `MuteToggle` icon.
17553     *
17554     * Possible states (given `level` variable below):
17555     * - 0: crossed out
17556     * - 1: zero bars of volume
17557     * - 2: one bar of volume
17558     * - 3: two bars of volume
17559     *
17560     * @private
17561     */
17562    ;
17563
17564    _proto.updateIcon_ = function updateIcon_() {
17565      var vol = this.player_.volume();
17566      var level = 3; // in iOS when a player is loaded with muted attribute
17567      // and volume is changed with a native mute button
17568      // we want to make sure muted state is updated
17569
17570      if (IS_IOS && this.player_.tech_ && this.player_.tech_.el_) {
17571        this.player_.muted(this.player_.tech_.el_.muted);
17572      }
17573
17574      if (vol === 0 || this.player_.muted()) {
17575        level = 0;
17576      } else if (vol < 0.33) {
17577        level = 1;
17578      } else if (vol < 0.67) {
17579        level = 2;
17580      } // TODO improve muted icon classes
17581
17582
17583      for (var i = 0; i < 4; i++) {
17584        removeClass(this.el_, "vjs-vol-" + i);
17585      }
17586
17587      addClass(this.el_, "vjs-vol-" + level);
17588    }
17589    /**
17590     * If `muted` has changed on the player, update the control text
17591     * (`title` attribute on `vjs-mute-control` element and content of
17592     * `vjs-control-text` element).
17593     *
17594     * @private
17595     */
17596    ;
17597
17598    _proto.updateControlText_ = function updateControlText_() {
17599      var soundOff = this.player_.muted() || this.player_.volume() === 0;
17600      var text = soundOff ? 'Unmute' : 'Mute';
17601
17602      if (this.controlText() !== text) {
17603        this.controlText(text);
17604      }
17605    };
17606
17607    return MuteToggle;
17608  }(Button);
17609  /**
17610   * The text that should display over the `MuteToggle`s controls. Added for localization.
17611   *
17612   * @type {string}
17613   * @private
17614   */
17615
17616
17617  MuteToggle.prototype.controlText_ = 'Mute';
17618  Component.registerComponent('MuteToggle', MuteToggle);
17619
17620  /**
17621   * A Component to contain the MuteToggle and VolumeControl so that
17622   * they can work together.
17623   *
17624   * @extends Component
17625   */
17626
17627  var VolumePanel =
17628  /*#__PURE__*/
17629  function (_Component) {
17630    _inheritsLoose(VolumePanel, _Component);
17631
17632    /**
17633     * Creates an instance of this class.
17634     *
17635     * @param {Player} player
17636     *        The `Player` that this class should be attached to.
17637     *
17638     * @param {Object} [options={}]
17639     *        The key/value store of player options.
17640     */
17641    function VolumePanel(player, options) {
17642      var _this;
17643
17644      if (options === void 0) {
17645        options = {};
17646      }
17647
17648      if (typeof options.inline !== 'undefined') {
17649        options.inline = options.inline;
17650      } else {
17651        options.inline = true;
17652      } // pass the inline option down to the VolumeControl as vertical if
17653      // the VolumeControl is on.
17654
17655
17656      if (typeof options.volumeControl === 'undefined' || isPlain(options.volumeControl)) {
17657        options.volumeControl = options.volumeControl || {};
17658        options.volumeControl.vertical = !options.inline;
17659      }
17660
17661      _this = _Component.call(this, player, options) || this;
17662
17663      _this.on(player, ['loadstart'], _this.volumePanelState_);
17664
17665      _this.on(_this.muteToggle, 'keyup', _this.handleKeyPress);
17666
17667      _this.on(_this.volumeControl, 'keyup', _this.handleVolumeControlKeyUp);
17668
17669      _this.on('keydown', _this.handleKeyPress);
17670
17671      _this.on('mouseover', _this.handleMouseOver);
17672
17673      _this.on('mouseout', _this.handleMouseOut); // while the slider is active (the mouse has been pressed down and
17674      // is dragging) we do not want to hide the VolumeBar
17675
17676
17677      _this.on(_this.volumeControl, ['slideractive'], _this.sliderActive_);
17678
17679      _this.on(_this.volumeControl, ['sliderinactive'], _this.sliderInactive_);
17680
17681      return _this;
17682    }
17683    /**
17684     * Add vjs-slider-active class to the VolumePanel
17685     *
17686     * @listens VolumeControl#slideractive
17687     * @private
17688     */
17689
17690
17691    var _proto = VolumePanel.prototype;
17692
17693    _proto.sliderActive_ = function sliderActive_() {
17694      this.addClass('vjs-slider-active');
17695    }
17696    /**
17697     * Removes vjs-slider-active class to the VolumePanel
17698     *
17699     * @listens VolumeControl#sliderinactive
17700     * @private
17701     */
17702    ;
17703
17704    _proto.sliderInactive_ = function sliderInactive_() {
17705      this.removeClass('vjs-slider-active');
17706    }
17707    /**
17708     * Adds vjs-hidden or vjs-mute-toggle-only to the VolumePanel
17709     * depending on MuteToggle and VolumeControl state
17710     *
17711     * @listens Player#loadstart
17712     * @private
17713     */
17714    ;
17715
17716    _proto.volumePanelState_ = function volumePanelState_() {
17717      // hide volume panel if neither volume control or mute toggle
17718      // are displayed
17719      if (this.volumeControl.hasClass('vjs-hidden') && this.muteToggle.hasClass('vjs-hidden')) {
17720        this.addClass('vjs-hidden');
17721      } // if only mute toggle is visible we don't want
17722      // volume panel expanding when hovered or active
17723
17724
17725      if (this.volumeControl.hasClass('vjs-hidden') && !this.muteToggle.hasClass('vjs-hidden')) {
17726        this.addClass('vjs-mute-toggle-only');
17727      }
17728    }
17729    /**
17730     * Create the `Component`'s DOM element
17731     *
17732     * @return {Element}
17733     *         The element that was created.
17734     */
17735    ;
17736
17737    _proto.createEl = function createEl() {
17738      var orientationClass = 'vjs-volume-panel-horizontal';
17739
17740      if (!this.options_.inline) {
17741        orientationClass = 'vjs-volume-panel-vertical';
17742      }
17743
17744      return _Component.prototype.createEl.call(this, 'div', {
17745        className: "vjs-volume-panel vjs-control " + orientationClass
17746      });
17747    }
17748    /**
17749     * Dispose of the `volume-panel` and all child components.
17750     */
17751    ;
17752
17753    _proto.dispose = function dispose() {
17754      this.handleMouseOut();
17755
17756      _Component.prototype.dispose.call(this);
17757    }
17758    /**
17759     * Handles `keyup` events on the `VolumeControl`, looking for ESC, which closes
17760     * the volume panel and sets focus on `MuteToggle`.
17761     *
17762     * @param {EventTarget~Event} event
17763     *        The `keyup` event that caused this function to be called.
17764     *
17765     * @listens keyup
17766     */
17767    ;
17768
17769    _proto.handleVolumeControlKeyUp = function handleVolumeControlKeyUp(event) {
17770      if (keycode.isEventKey(event, 'Esc')) {
17771        this.muteToggle.focus();
17772      }
17773    }
17774    /**
17775     * This gets called when a `VolumePanel` gains hover via a `mouseover` event.
17776     * Turns on listening for `mouseover` event. When they happen it
17777     * calls `this.handleMouseOver`.
17778     *
17779     * @param {EventTarget~Event} event
17780     *        The `mouseover` event that caused this function to be called.
17781     *
17782     * @listens mouseover
17783     */
17784    ;
17785
17786    _proto.handleMouseOver = function handleMouseOver(event) {
17787      this.addClass('vjs-hover');
17788      on(document, 'keyup', bind(this, this.handleKeyPress));
17789    }
17790    /**
17791     * This gets called when a `VolumePanel` gains hover via a `mouseout` event.
17792     * Turns on listening for `mouseout` event. When they happen it
17793     * calls `this.handleMouseOut`.
17794     *
17795     * @param {EventTarget~Event} event
17796     *        The `mouseout` event that caused this function to be called.
17797     *
17798     * @listens mouseout
17799     */
17800    ;
17801
17802    _proto.handleMouseOut = function handleMouseOut(event) {
17803      this.removeClass('vjs-hover');
17804      off(document, 'keyup', bind(this, this.handleKeyPress));
17805    }
17806    /**
17807     * Handles `keyup` event on the document or `keydown` event on the `VolumePanel`,
17808     * looking for ESC, which hides the `VolumeControl`.
17809     *
17810     * @param {EventTarget~Event} event
17811     *        The keypress that triggered this event.
17812     *
17813     * @listens keydown | keyup
17814     */
17815    ;
17816
17817    _proto.handleKeyPress = function handleKeyPress(event) {
17818      if (keycode.isEventKey(event, 'Esc')) {
17819        this.handleMouseOut();
17820      }
17821    };
17822
17823    return VolumePanel;
17824  }(Component);
17825  /**
17826   * Default options for the `VolumeControl`
17827   *
17828   * @type {Object}
17829   * @private
17830   */
17831
17832
17833  VolumePanel.prototype.options_ = {
17834    children: ['muteToggle', 'volumeControl']
17835  };
17836  Component.registerComponent('VolumePanel', VolumePanel);
17837
17838  /**
17839   * The Menu component is used to build popup menus, including subtitle and
17840   * captions selection menus.
17841   *
17842   * @extends Component
17843   */
17844
17845  var Menu =
17846  /*#__PURE__*/
17847  function (_Component) {
17848    _inheritsLoose(Menu, _Component);
17849
17850    /**
17851     * Create an instance of this class.
17852     *
17853     * @param {Player} player
17854     *        the player that this component should attach to
17855     *
17856     * @param {Object} [options]
17857     *        Object of option names and values
17858     *
17859     */
17860    function Menu(player, options) {
17861      var _this;
17862
17863      _this = _Component.call(this, player, options) || this;
17864
17865      if (options) {
17866        _this.menuButton_ = options.menuButton;
17867      }
17868
17869      _this.focusedChild_ = -1;
17870
17871      _this.on('keydown', _this.handleKeyDown); // All the menu item instances share the same blur handler provided by the menu container.
17872
17873
17874      _this.boundHandleBlur_ = bind(_assertThisInitialized(_this), _this.handleBlur);
17875      _this.boundHandleTapClick_ = bind(_assertThisInitialized(_this), _this.handleTapClick);
17876      return _this;
17877    }
17878    /**
17879     * Add event listeners to the {@link MenuItem}.
17880     *
17881     * @param {Object} component
17882     *        The instance of the `MenuItem` to add listeners to.
17883     *
17884     */
17885
17886
17887    var _proto = Menu.prototype;
17888
17889    _proto.addEventListenerForItem = function addEventListenerForItem(component) {
17890      if (!(component instanceof Component)) {
17891        return;
17892      }
17893
17894      this.on(component, 'blur', this.boundHandleBlur_);
17895      this.on(component, ['tap', 'click'], this.boundHandleTapClick_);
17896    }
17897    /**
17898     * Remove event listeners from the {@link MenuItem}.
17899     *
17900     * @param {Object} component
17901     *        The instance of the `MenuItem` to remove listeners.
17902     *
17903     */
17904    ;
17905
17906    _proto.removeEventListenerForItem = function removeEventListenerForItem(component) {
17907      if (!(component instanceof Component)) {
17908        return;
17909      }
17910
17911      this.off(component, 'blur', this.boundHandleBlur_);
17912      this.off(component, ['tap', 'click'], this.boundHandleTapClick_);
17913    }
17914    /**
17915     * This method will be called indirectly when the component has been added
17916     * before the component adds to the new menu instance by `addItem`.
17917     * In this case, the original menu instance will remove the component
17918     * by calling `removeChild`.
17919     *
17920     * @param {Object} component
17921     *        The instance of the `MenuItem`
17922     */
17923    ;
17924
17925    _proto.removeChild = function removeChild(component) {
17926      if (typeof component === 'string') {
17927        component = this.getChild(component);
17928      }
17929
17930      this.removeEventListenerForItem(component);
17931
17932      _Component.prototype.removeChild.call(this, component);
17933    }
17934    /**
17935     * Add a {@link MenuItem} to the menu.
17936     *
17937     * @param {Object|string} component
17938     *        The name or instance of the `MenuItem` to add.
17939     *
17940     */
17941    ;
17942
17943    _proto.addItem = function addItem(component) {
17944      var childComponent = this.addChild(component);
17945
17946      if (childComponent) {
17947        this.addEventListenerForItem(childComponent);
17948      }
17949    }
17950    /**
17951     * Create the `Menu`s DOM element.
17952     *
17953     * @return {Element}
17954     *         the element that was created
17955     */
17956    ;
17957
17958    _proto.createEl = function createEl$1() {
17959      var contentElType = this.options_.contentElType || 'ul';
17960      this.contentEl_ = createEl(contentElType, {
17961        className: 'vjs-menu-content'
17962      });
17963      this.contentEl_.setAttribute('role', 'menu');
17964
17965      var el = _Component.prototype.createEl.call(this, 'div', {
17966        append: this.contentEl_,
17967        className: 'vjs-menu'
17968      });
17969
17970      el.appendChild(this.contentEl_); // Prevent clicks from bubbling up. Needed for Menu Buttons,
17971      // where a click on the parent is significant
17972
17973      on(el, 'click', function (event) {
17974        event.preventDefault();
17975        event.stopImmediatePropagation();
17976      });
17977      return el;
17978    };
17979
17980    _proto.dispose = function dispose() {
17981      this.contentEl_ = null;
17982      this.boundHandleBlur_ = null;
17983      this.boundHandleTapClick_ = null;
17984
17985      _Component.prototype.dispose.call(this);
17986    }
17987    /**
17988     * Called when a `MenuItem` loses focus.
17989     *
17990     * @param {EventTarget~Event} event
17991     *        The `blur` event that caused this function to be called.
17992     *
17993     * @listens blur
17994     */
17995    ;
17996
17997    _proto.handleBlur = function handleBlur(event) {
17998      var relatedTarget = event.relatedTarget || document.activeElement; // Close menu popup when a user clicks outside the menu
17999
18000      if (!this.children().some(function (element) {
18001        return element.el() === relatedTarget;
18002      })) {
18003        var btn = this.menuButton_;
18004
18005        if (btn && btn.buttonPressed_ && relatedTarget !== btn.el().firstChild) {
18006          btn.unpressButton();
18007        }
18008      }
18009    }
18010    /**
18011     * Called when a `MenuItem` gets clicked or tapped.
18012     *
18013     * @param {EventTarget~Event} event
18014     *        The `click` or `tap` event that caused this function to be called.
18015     *
18016     * @listens click,tap
18017     */
18018    ;
18019
18020    _proto.handleTapClick = function handleTapClick(event) {
18021      // Unpress the associated MenuButton, and move focus back to it
18022      if (this.menuButton_) {
18023        this.menuButton_.unpressButton();
18024        var childComponents = this.children();
18025
18026        if (!Array.isArray(childComponents)) {
18027          return;
18028        }
18029
18030        var foundComponent = childComponents.filter(function (component) {
18031          return component.el() === event.target;
18032        })[0];
18033
18034        if (!foundComponent) {
18035          return;
18036        } // don't focus menu button if item is a caption settings item
18037        // because focus will move elsewhere
18038
18039
18040        if (foundComponent.name() !== 'CaptionSettingsMenuItem') {
18041          this.menuButton_.focus();
18042        }
18043      }
18044    }
18045    /**
18046     * Handle a `keydown` event on this menu. This listener is added in the constructor.
18047     *
18048     * @param {EventTarget~Event} event
18049     *        A `keydown` event that happened on the menu.
18050     *
18051     * @listens keydown
18052     */
18053    ;
18054
18055    _proto.handleKeyDown = function handleKeyDown(event) {
18056      // Left and Down Arrows
18057      if (keycode.isEventKey(event, 'Left') || keycode.isEventKey(event, 'Down')) {
18058        event.preventDefault();
18059        event.stopPropagation();
18060        this.stepForward(); // Up and Right Arrows
18061      } else if (keycode.isEventKey(event, 'Right') || keycode.isEventKey(event, 'Up')) {
18062        event.preventDefault();
18063        event.stopPropagation();
18064        this.stepBack();
18065      }
18066    }
18067    /**
18068     * Move to next (lower) menu item for keyboard users.
18069     */
18070    ;
18071
18072    _proto.stepForward = function stepForward() {
18073      var stepChild = 0;
18074
18075      if (this.focusedChild_ !== undefined) {
18076        stepChild = this.focusedChild_ + 1;
18077      }
18078
18079      this.focus(stepChild);
18080    }
18081    /**
18082     * Move to previous (higher) menu item for keyboard users.
18083     */
18084    ;
18085
18086    _proto.stepBack = function stepBack() {
18087      var stepChild = 0;
18088
18089      if (this.focusedChild_ !== undefined) {
18090        stepChild = this.focusedChild_ - 1;
18091      }
18092
18093      this.focus(stepChild);
18094    }
18095    /**
18096     * Set focus on a {@link MenuItem} in the `Menu`.
18097     *
18098     * @param {Object|string} [item=0]
18099     *        Index of child item set focus on.
18100     */
18101    ;
18102
18103    _proto.focus = function focus(item) {
18104      if (item === void 0) {
18105        item = 0;
18106      }
18107
18108      var children = this.children().slice();
18109      var haveTitle = children.length && children[0].className && /vjs-menu-title/.test(children[0].className);
18110
18111      if (haveTitle) {
18112        children.shift();
18113      }
18114
18115      if (children.length > 0) {
18116        if (item < 0) {
18117          item = 0;
18118        } else if (item >= children.length) {
18119          item = children.length - 1;
18120        }
18121
18122        this.focusedChild_ = item;
18123        children[item].el_.focus();
18124      }
18125    };
18126
18127    return Menu;
18128  }(Component);
18129
18130  Component.registerComponent('Menu', Menu);
18131
18132  /**
18133   * A `MenuButton` class for any popup {@link Menu}.
18134   *
18135   * @extends Component
18136   */
18137
18138  var MenuButton =
18139  /*#__PURE__*/
18140  function (_Component) {
18141    _inheritsLoose(MenuButton, _Component);
18142
18143    /**
18144     * Creates an instance of this class.
18145     *
18146     * @param {Player} player
18147     *        The `Player` that this class should be attached to.
18148     *
18149     * @param {Object} [options={}]
18150     *        The key/value store of player options.
18151     */
18152    function MenuButton(player, options) {
18153      var _this;
18154
18155      if (options === void 0) {
18156        options = {};
18157      }
18158
18159      _this = _Component.call(this, player, options) || this;
18160      _this.menuButton_ = new Button(player, options);
18161
18162      _this.menuButton_.controlText(_this.controlText_);
18163
18164      _this.menuButton_.el_.setAttribute('aria-haspopup', 'true'); // Add buildCSSClass values to the button, not the wrapper
18165
18166
18167      var buttonClass = Button.prototype.buildCSSClass();
18168      _this.menuButton_.el_.className = _this.buildCSSClass() + ' ' + buttonClass;
18169
18170      _this.menuButton_.removeClass('vjs-control');
18171
18172      _this.addChild(_this.menuButton_);
18173
18174      _this.update();
18175
18176      _this.enabled_ = true;
18177
18178      _this.on(_this.menuButton_, 'tap', _this.handleClick);
18179
18180      _this.on(_this.menuButton_, 'click', _this.handleClick);
18181
18182      _this.on(_this.menuButton_, 'keydown', _this.handleKeyDown);
18183
18184      _this.on(_this.menuButton_, 'mouseenter', function () {
18185        _this.addClass('vjs-hover');
18186
18187        _this.menu.show();
18188
18189        on(document, 'keyup', bind(_assertThisInitialized(_this), _this.handleMenuKeyUp));
18190      });
18191
18192      _this.on('mouseleave', _this.handleMouseLeave);
18193
18194      _this.on('keydown', _this.handleSubmenuKeyDown);
18195
18196      return _this;
18197    }
18198    /**
18199     * Update the menu based on the current state of its items.
18200     */
18201
18202
18203    var _proto = MenuButton.prototype;
18204
18205    _proto.update = function update() {
18206      var menu = this.createMenu();
18207
18208      if (this.menu) {
18209        this.menu.dispose();
18210        this.removeChild(this.menu);
18211      }
18212
18213      this.menu = menu;
18214      this.addChild(menu);
18215      /**
18216       * Track the state of the menu button
18217       *
18218       * @type {Boolean}
18219       * @private
18220       */
18221
18222      this.buttonPressed_ = false;
18223      this.menuButton_.el_.setAttribute('aria-expanded', 'false');
18224
18225      if (this.items && this.items.length <= this.hideThreshold_) {
18226        this.hide();
18227      } else {
18228        this.show();
18229      }
18230    }
18231    /**
18232     * Create the menu and add all items to it.
18233     *
18234     * @return {Menu}
18235     *         The constructed menu
18236     */
18237    ;
18238
18239    _proto.createMenu = function createMenu() {
18240      var menu = new Menu(this.player_, {
18241        menuButton: this
18242      });
18243      /**
18244       * Hide the menu if the number of items is less than or equal to this threshold. This defaults
18245       * to 0 and whenever we add items which can be hidden to the menu we'll increment it. We list
18246       * it here because every time we run `createMenu` we need to reset the value.
18247       *
18248       * @protected
18249       * @type {Number}
18250       */
18251
18252      this.hideThreshold_ = 0; // Add a title list item to the top
18253
18254      if (this.options_.title) {
18255        var titleEl = createEl('li', {
18256          className: 'vjs-menu-title',
18257          innerHTML: toTitleCase(this.options_.title),
18258          tabIndex: -1
18259        });
18260        this.hideThreshold_ += 1;
18261        var titleComponent = new Component(this.player_, {
18262          el: titleEl
18263        });
18264        menu.addItem(titleComponent);
18265      }
18266
18267      this.items = this.createItems();
18268
18269      if (this.items) {
18270        // Add menu items to the menu
18271        for (var i = 0; i < this.items.length; i++) {
18272          menu.addItem(this.items[i]);
18273        }
18274      }
18275
18276      return menu;
18277    }
18278    /**
18279     * Create the list of menu items. Specific to each subclass.
18280     *
18281     * @abstract
18282     */
18283    ;
18284
18285    _proto.createItems = function createItems() {}
18286    /**
18287     * Create the `MenuButtons`s DOM element.
18288     *
18289     * @return {Element}
18290     *         The element that gets created.
18291     */
18292    ;
18293
18294    _proto.createEl = function createEl() {
18295      return _Component.prototype.createEl.call(this, 'div', {
18296        className: this.buildWrapperCSSClass()
18297      }, {});
18298    }
18299    /**
18300     * Allow sub components to stack CSS class names for the wrapper element
18301     *
18302     * @return {string}
18303     *         The constructed wrapper DOM `className`
18304     */
18305    ;
18306
18307    _proto.buildWrapperCSSClass = function buildWrapperCSSClass() {
18308      var menuButtonClass = 'vjs-menu-button'; // If the inline option is passed, we want to use different styles altogether.
18309
18310      if (this.options_.inline === true) {
18311        menuButtonClass += '-inline';
18312      } else {
18313        menuButtonClass += '-popup';
18314      } // TODO: Fix the CSS so that this isn't necessary
18315
18316
18317      var buttonClass = Button.prototype.buildCSSClass();
18318      return "vjs-menu-button " + menuButtonClass + " " + buttonClass + " " + _Component.prototype.buildCSSClass.call(this);
18319    }
18320    /**
18321     * Builds the default DOM `className`.
18322     *
18323     * @return {string}
18324     *         The DOM `className` for this object.
18325     */
18326    ;
18327
18328    _proto.buildCSSClass = function buildCSSClass() {
18329      var menuButtonClass = 'vjs-menu-button'; // If the inline option is passed, we want to use different styles altogether.
18330
18331      if (this.options_.inline === true) {
18332        menuButtonClass += '-inline';
18333      } else {
18334        menuButtonClass += '-popup';
18335      }
18336
18337      return "vjs-menu-button " + menuButtonClass + " " + _Component.prototype.buildCSSClass.call(this);
18338    }
18339    /**
18340     * Get or set the localized control text that will be used for accessibility.
18341     *
18342     * > NOTE: This will come from the internal `menuButton_` element.
18343     *
18344     * @param {string} [text]
18345     *        Control text for element.
18346     *
18347     * @param {Element} [el=this.menuButton_.el()]
18348     *        Element to set the title on.
18349     *
18350     * @return {string}
18351     *         - The control text when getting
18352     */
18353    ;
18354
18355    _proto.controlText = function controlText(text, el) {
18356      if (el === void 0) {
18357        el = this.menuButton_.el();
18358      }
18359
18360      return this.menuButton_.controlText(text, el);
18361    }
18362    /**
18363     * Dispose of the `menu-button` and all child components.
18364     */
18365    ;
18366
18367    _proto.dispose = function dispose() {
18368      this.handleMouseLeave();
18369
18370      _Component.prototype.dispose.call(this);
18371    }
18372    /**
18373     * Handle a click on a `MenuButton`.
18374     * See {@link ClickableComponent#handleClick} for instances where this is called.
18375     *
18376     * @param {EventTarget~Event} event
18377     *        The `keydown`, `tap`, or `click` event that caused this function to be
18378     *        called.
18379     *
18380     * @listens tap
18381     * @listens click
18382     */
18383    ;
18384
18385    _proto.handleClick = function handleClick(event) {
18386      if (this.buttonPressed_) {
18387        this.unpressButton();
18388      } else {
18389        this.pressButton();
18390      }
18391    }
18392    /**
18393     * Handle `mouseleave` for `MenuButton`.
18394     *
18395     * @param {EventTarget~Event} event
18396     *        The `mouseleave` event that caused this function to be called.
18397     *
18398     * @listens mouseleave
18399     */
18400    ;
18401
18402    _proto.handleMouseLeave = function handleMouseLeave(event) {
18403      this.removeClass('vjs-hover');
18404      off(document, 'keyup', bind(this, this.handleMenuKeyUp));
18405    }
18406    /**
18407     * Set the focus to the actual button, not to this element
18408     */
18409    ;
18410
18411    _proto.focus = function focus() {
18412      this.menuButton_.focus();
18413    }
18414    /**
18415     * Remove the focus from the actual button, not this element
18416     */
18417    ;
18418
18419    _proto.blur = function blur() {
18420      this.menuButton_.blur();
18421    }
18422    /**
18423     * Handle tab, escape, down arrow, and up arrow keys for `MenuButton`. See
18424     * {@link ClickableComponent#handleKeyDown} for instances where this is called.
18425     *
18426     * @param {EventTarget~Event} event
18427     *        The `keydown` event that caused this function to be called.
18428     *
18429     * @listens keydown
18430     */
18431    ;
18432
18433    _proto.handleKeyDown = function handleKeyDown(event) {
18434      // Escape or Tab unpress the 'button'
18435      if (keycode.isEventKey(event, 'Esc') || keycode.isEventKey(event, 'Tab')) {
18436        if (this.buttonPressed_) {
18437          this.unpressButton();
18438        } // Don't preventDefault for Tab key - we still want to lose focus
18439
18440
18441        if (!keycode.isEventKey(event, 'Tab')) {
18442          event.preventDefault(); // Set focus back to the menu button's button
18443
18444          this.menuButton_.focus();
18445        } // Up Arrow or Down Arrow also 'press' the button to open the menu
18446
18447      } else if (keycode.isEventKey(event, 'Up') || keycode.isEventKey(event, 'Down')) {
18448        if (!this.buttonPressed_) {
18449          event.preventDefault();
18450          this.pressButton();
18451        }
18452      }
18453    }
18454    /**
18455     * Handle a `keyup` event on a `MenuButton`. The listener for this is added in
18456     * the constructor.
18457     *
18458     * @param {EventTarget~Event} event
18459     *        Key press event
18460     *
18461     * @listens keyup
18462     */
18463    ;
18464
18465    _proto.handleMenuKeyUp = function handleMenuKeyUp(event) {
18466      // Escape hides popup menu
18467      if (keycode.isEventKey(event, 'Esc') || keycode.isEventKey(event, 'Tab')) {
18468        this.removeClass('vjs-hover');
18469      }
18470    }
18471    /**
18472     * This method name now delegates to `handleSubmenuKeyDown`. This means
18473     * anyone calling `handleSubmenuKeyPress` will not see their method calls
18474     * stop working.
18475     *
18476     * @param {EventTarget~Event} event
18477     *        The event that caused this function to be called.
18478     */
18479    ;
18480
18481    _proto.handleSubmenuKeyPress = function handleSubmenuKeyPress(event) {
18482      this.handleSubmenuKeyDown(event);
18483    }
18484    /**
18485     * Handle a `keydown` event on a sub-menu. The listener for this is added in
18486     * the constructor.
18487     *
18488     * @param {EventTarget~Event} event
18489     *        Key press event
18490     *
18491     * @listens keydown
18492     */
18493    ;
18494
18495    _proto.handleSubmenuKeyDown = function handleSubmenuKeyDown(event) {
18496      // Escape or Tab unpress the 'button'
18497      if (keycode.isEventKey(event, 'Esc') || keycode.isEventKey(event, 'Tab')) {
18498        if (this.buttonPressed_) {
18499          this.unpressButton();
18500        } // Don't preventDefault for Tab key - we still want to lose focus
18501
18502
18503        if (!keycode.isEventKey(event, 'Tab')) {
18504          event.preventDefault(); // Set focus back to the menu button's button
18505
18506          this.menuButton_.focus();
18507        }
18508      }
18509    }
18510    /**
18511     * Put the current `MenuButton` into a pressed state.
18512     */
18513    ;
18514
18515    _proto.pressButton = function pressButton() {
18516      if (this.enabled_) {
18517        this.buttonPressed_ = true;
18518        this.menu.show();
18519        this.menu.lockShowing();
18520        this.menuButton_.el_.setAttribute('aria-expanded', 'true'); // set the focus into the submenu, except on iOS where it is resulting in
18521        // undesired scrolling behavior when the player is in an iframe
18522
18523        if (IS_IOS && isInFrame()) {
18524          // Return early so that the menu isn't focused
18525          return;
18526        }
18527
18528        this.menu.focus();
18529      }
18530    }
18531    /**
18532     * Take the current `MenuButton` out of a pressed state.
18533     */
18534    ;
18535
18536    _proto.unpressButton = function unpressButton() {
18537      if (this.enabled_) {
18538        this.buttonPressed_ = false;
18539        this.menu.unlockShowing();
18540        this.menu.hide();
18541        this.menuButton_.el_.setAttribute('aria-expanded', 'false');
18542      }
18543    }
18544    /**
18545     * Disable the `MenuButton`. Don't allow it to be clicked.
18546     */
18547    ;
18548
18549    _proto.disable = function disable() {
18550      this.unpressButton();
18551      this.enabled_ = false;
18552      this.addClass('vjs-disabled');
18553      this.menuButton_.disable();
18554    }
18555    /**
18556     * Enable the `MenuButton`. Allow it to be clicked.
18557     */
18558    ;
18559
18560    _proto.enable = function enable() {
18561      this.enabled_ = true;
18562      this.removeClass('vjs-disabled');
18563      this.menuButton_.enable();
18564    };
18565
18566    return MenuButton;
18567  }(Component);
18568
18569  Component.registerComponent('MenuButton', MenuButton);
18570
18571  /**
18572   * The base class for buttons that toggle specific  track types (e.g. subtitles).
18573   *
18574   * @extends MenuButton
18575   */
18576
18577  var TrackButton =
18578  /*#__PURE__*/
18579  function (_MenuButton) {
18580    _inheritsLoose(TrackButton, _MenuButton);
18581
18582    /**
18583     * Creates an instance of this class.
18584     *
18585     * @param {Player} player
18586     *        The `Player` that this class should be attached to.
18587     *
18588     * @param {Object} [options]
18589     *        The key/value store of player options.
18590     */
18591    function TrackButton(player, options) {
18592      var _this;
18593
18594      var tracks = options.tracks;
18595      _this = _MenuButton.call(this, player, options) || this;
18596
18597      if (_this.items.length <= 1) {
18598        _this.hide();
18599      }
18600
18601      if (!tracks) {
18602        return _assertThisInitialized(_this);
18603      }
18604
18605      var updateHandler = bind(_assertThisInitialized(_this), _this.update);
18606      tracks.addEventListener('removetrack', updateHandler);
18607      tracks.addEventListener('addtrack', updateHandler);
18608
18609      _this.player_.on('ready', updateHandler);
18610
18611      _this.player_.on('dispose', function () {
18612        tracks.removeEventListener('removetrack', updateHandler);
18613        tracks.removeEventListener('addtrack', updateHandler);
18614      });
18615
18616      return _this;
18617    }
18618
18619    return TrackButton;
18620  }(MenuButton);
18621
18622  Component.registerComponent('TrackButton', TrackButton);
18623
18624  /**
18625   * @file menu-keys.js
18626   */
18627
18628  /**
18629    * All keys used for operation of a menu (`MenuButton`, `Menu`, and `MenuItem`)
18630    * Note that 'Enter' and 'Space' are not included here (otherwise they would
18631    * prevent the `MenuButton` and `MenuItem` from being keyboard-clickable)
18632    * @typedef MenuKeys
18633    * @array
18634    */
18635  var MenuKeys = ['Tab', 'Esc', 'Up', 'Down', 'Right', 'Left'];
18636
18637  /**
18638   * The component for a menu item. `<li>`
18639   *
18640   * @extends ClickableComponent
18641   */
18642
18643  var MenuItem =
18644  /*#__PURE__*/
18645  function (_ClickableComponent) {
18646    _inheritsLoose(MenuItem, _ClickableComponent);
18647
18648    /**
18649     * Creates an instance of the this class.
18650     *
18651     * @param {Player} player
18652     *        The `Player` that this class should be attached to.
18653     *
18654     * @param {Object} [options={}]
18655     *        The key/value store of player options.
18656     *
18657     */
18658    function MenuItem(player, options) {
18659      var _this;
18660
18661      _this = _ClickableComponent.call(this, player, options) || this;
18662      _this.selectable = options.selectable;
18663      _this.isSelected_ = options.selected || false;
18664      _this.multiSelectable = options.multiSelectable;
18665
18666      _this.selected(_this.isSelected_);
18667
18668      if (_this.selectable) {
18669        if (_this.multiSelectable) {
18670          _this.el_.setAttribute('role', 'menuitemcheckbox');
18671        } else {
18672          _this.el_.setAttribute('role', 'menuitemradio');
18673        }
18674      } else {
18675        _this.el_.setAttribute('role', 'menuitem');
18676      }
18677
18678      return _this;
18679    }
18680    /**
18681     * Create the `MenuItem's DOM element
18682     *
18683     * @param {string} [type=li]
18684     *        Element's node type, not actually used, always set to `li`.
18685     *
18686     * @param {Object} [props={}]
18687     *        An object of properties that should be set on the element
18688     *
18689     * @param {Object} [attrs={}]
18690     *        An object of attributes that should be set on the element
18691     *
18692     * @return {Element}
18693     *         The element that gets created.
18694     */
18695
18696
18697    var _proto = MenuItem.prototype;
18698
18699    _proto.createEl = function createEl(type, props, attrs) {
18700      // The control is textual, not just an icon
18701      this.nonIconControl = true;
18702      return _ClickableComponent.prototype.createEl.call(this, 'li', assign({
18703        className: 'vjs-menu-item',
18704        innerHTML: "<span class=\"vjs-menu-item-text\">" + this.localize(this.options_.label) + "</span>",
18705        tabIndex: -1
18706      }, props), attrs);
18707    }
18708    /**
18709     * Ignore keys which are used by the menu, but pass any other ones up. See
18710     * {@link ClickableComponent#handleKeyDown} for instances where this is called.
18711     *
18712     * @param {EventTarget~Event} event
18713     *        The `keydown` event that caused this function to be called.
18714     *
18715     * @listens keydown
18716     */
18717    ;
18718
18719    _proto.handleKeyDown = function handleKeyDown(event) {
18720      if (!MenuKeys.some(function (key) {
18721        return keycode.isEventKey(event, key);
18722      })) {
18723        // Pass keydown handling up for unused keys
18724        _ClickableComponent.prototype.handleKeyDown.call(this, event);
18725      }
18726    }
18727    /**
18728     * Any click on a `MenuItem` puts it into the selected state.
18729     * See {@link ClickableComponent#handleClick} for instances where this is called.
18730     *
18731     * @param {EventTarget~Event} event
18732     *        The `keydown`, `tap`, or `click` event that caused this function to be
18733     *        called.
18734     *
18735     * @listens tap
18736     * @listens click
18737     */
18738    ;
18739
18740    _proto.handleClick = function handleClick(event) {
18741      this.selected(true);
18742    }
18743    /**
18744     * Set the state for this menu item as selected or not.
18745     *
18746     * @param {boolean} selected
18747     *        if the menu item is selected or not
18748     */
18749    ;
18750
18751    _proto.selected = function selected(_selected) {
18752      if (this.selectable) {
18753        if (_selected) {
18754          this.addClass('vjs-selected');
18755          this.el_.setAttribute('aria-checked', 'true'); // aria-checked isn't fully supported by browsers/screen readers,
18756          // so indicate selected state to screen reader in the control text.
18757
18758          this.controlText(', selected');
18759          this.isSelected_ = true;
18760        } else {
18761          this.removeClass('vjs-selected');
18762          this.el_.setAttribute('aria-checked', 'false'); // Indicate un-selected state to screen reader
18763
18764          this.controlText('');
18765          this.isSelected_ = false;
18766        }
18767      }
18768    };
18769
18770    return MenuItem;
18771  }(ClickableComponent);
18772
18773  Component.registerComponent('MenuItem', MenuItem);
18774
18775  /**
18776   * The specific menu item type for selecting a language within a text track kind
18777   *
18778   * @extends MenuItem
18779   */
18780
18781  var TextTrackMenuItem =
18782  /*#__PURE__*/
18783  function (_MenuItem) {
18784    _inheritsLoose(TextTrackMenuItem, _MenuItem);
18785
18786    /**
18787     * Creates an instance of this class.
18788     *
18789     * @param {Player} player
18790     *        The `Player` that this class should be attached to.
18791     *
18792     * @param {Object} [options]
18793     *        The key/value store of player options.
18794     */
18795    function TextTrackMenuItem(player, options) {
18796      var _this;
18797
18798      var track = options.track;
18799      var tracks = player.textTracks(); // Modify options for parent MenuItem class's init.
18800
18801      options.label = track.label || track.language || 'Unknown';
18802      options.selected = track.mode === 'showing';
18803      _this = _MenuItem.call(this, player, options) || this;
18804      _this.track = track; // Determine the relevant kind(s) of tracks for this component and filter
18805      // out empty kinds.
18806
18807      _this.kinds = (options.kinds || [options.kind || _this.track.kind]).filter(Boolean);
18808
18809      var changeHandler = function changeHandler() {
18810        for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) {
18811          args[_key] = arguments[_key];
18812        }
18813
18814        _this.handleTracksChange.apply(_assertThisInitialized(_this), args);
18815      };
18816
18817      var selectedLanguageChangeHandler = function selectedLanguageChangeHandler() {
18818        for (var _len2 = arguments.length, args = new Array(_len2), _key2 = 0; _key2 < _len2; _key2++) {
18819          args[_key2] = arguments[_key2];
18820        }
18821
18822        _this.handleSelectedLanguageChange.apply(_assertThisInitialized(_this), args);
18823      };
18824
18825      player.on(['loadstart', 'texttrackchange'], changeHandler);
18826      tracks.addEventListener('change', changeHandler);
18827      tracks.addEventListener('selectedlanguagechange', selectedLanguageChangeHandler);
18828
18829      _this.on('dispose', function () {
18830        player.off(['loadstart', 'texttrackchange'], changeHandler);
18831        tracks.removeEventListener('change', changeHandler);
18832        tracks.removeEventListener('selectedlanguagechange', selectedLanguageChangeHandler);
18833      }); // iOS7 doesn't dispatch change events to TextTrackLists when an
18834      // associated track's mode changes. Without something like
18835      // Object.observe() (also not present on iOS7), it's not
18836      // possible to detect changes to the mode attribute and polyfill
18837      // the change event. As a poor substitute, we manually dispatch
18838      // change events whenever the controls modify the mode.
18839
18840
18841      if (tracks.onchange === undefined) {
18842        var event;
18843
18844        _this.on(['tap', 'click'], function () {
18845          if (typeof window$1.Event !== 'object') {
18846            // Android 2.3 throws an Illegal Constructor error for window.Event
18847            try {
18848              event = new window$1.Event('change');
18849            } catch (err) {// continue regardless of error
18850            }
18851          }
18852
18853          if (!event) {
18854            event = document.createEvent('Event');
18855            event.initEvent('change', true, true);
18856          }
18857
18858          tracks.dispatchEvent(event);
18859        });
18860      } // set the default state based on current tracks
18861
18862
18863      _this.handleTracksChange();
18864
18865      return _this;
18866    }
18867    /**
18868     * This gets called when an `TextTrackMenuItem` is "clicked". See
18869     * {@link ClickableComponent} for more detailed information on what a click can be.
18870     *
18871     * @param {EventTarget~Event} event
18872     *        The `keydown`, `tap`, or `click` event that caused this function to be
18873     *        called.
18874     *
18875     * @listens tap
18876     * @listens click
18877     */
18878
18879
18880    var _proto = TextTrackMenuItem.prototype;
18881
18882    _proto.handleClick = function handleClick(event) {
18883      var referenceTrack = this.track;
18884      var tracks = this.player_.textTracks();
18885
18886      _MenuItem.prototype.handleClick.call(this, event);
18887
18888      if (!tracks) {
18889        return;
18890      }
18891
18892      for (var i = 0; i < tracks.length; i++) {
18893        var track = tracks[i]; // If the track from the text tracks list is not of the right kind,
18894        // skip it. We do not want to affect tracks of incompatible kind(s).
18895
18896        if (this.kinds.indexOf(track.kind) === -1) {
18897          continue;
18898        } // If this text track is the component's track and it is not showing,
18899        // set it to showing.
18900
18901
18902        if (track === referenceTrack) {
18903          if (track.mode !== 'showing') {
18904            track.mode = 'showing';
18905          } // If this text track is not the component's track and it is not
18906          // disabled, set it to disabled.
18907
18908        } else if (track.mode !== 'disabled') {
18909          track.mode = 'disabled';
18910        }
18911      }
18912    }
18913    /**
18914     * Handle text track list change
18915     *
18916     * @param {EventTarget~Event} event
18917     *        The `change` event that caused this function to be called.
18918     *
18919     * @listens TextTrackList#change
18920     */
18921    ;
18922
18923    _proto.handleTracksChange = function handleTracksChange(event) {
18924      var shouldBeSelected = this.track.mode === 'showing'; // Prevent redundant selected() calls because they may cause
18925      // screen readers to read the appended control text unnecessarily
18926
18927      if (shouldBeSelected !== this.isSelected_) {
18928        this.selected(shouldBeSelected);
18929      }
18930    };
18931
18932    _proto.handleSelectedLanguageChange = function handleSelectedLanguageChange(event) {
18933      if (this.track.mode === 'showing') {
18934        var selectedLanguage = this.player_.cache_.selectedLanguage; // Don't replace the kind of track across the same language
18935
18936        if (selectedLanguage && selectedLanguage.enabled && selectedLanguage.language === this.track.language && selectedLanguage.kind !== this.track.kind) {
18937          return;
18938        }
18939
18940        this.player_.cache_.selectedLanguage = {
18941          enabled: true,
18942          language: this.track.language,
18943          kind: this.track.kind
18944        };
18945      }
18946    };
18947
18948    _proto.dispose = function dispose() {
18949      // remove reference to track object on dispose
18950      this.track = null;
18951
18952      _MenuItem.prototype.dispose.call(this);
18953    };
18954
18955    return TextTrackMenuItem;
18956  }(MenuItem);
18957
18958  Component.registerComponent('TextTrackMenuItem', TextTrackMenuItem);
18959
18960  /**
18961   * A special menu item for turning of a specific type of text track
18962   *
18963   * @extends TextTrackMenuItem
18964   */
18965
18966  var OffTextTrackMenuItem =
18967  /*#__PURE__*/
18968  function (_TextTrackMenuItem) {
18969    _inheritsLoose(OffTextTrackMenuItem, _TextTrackMenuItem);
18970
18971    /**
18972     * Creates an instance of this class.
18973     *
18974     * @param {Player} player
18975     *        The `Player` that this class should be attached to.
18976     *
18977     * @param {Object} [options]
18978     *        The key/value store of player options.
18979     */
18980    function OffTextTrackMenuItem(player, options) {
18981      // Create pseudo track info
18982      // Requires options['kind']
18983      options.track = {
18984        player: player,
18985        // it is no longer necessary to store `kind` or `kinds` on the track itself
18986        // since they are now stored in the `kinds` property of all instances of
18987        // TextTrackMenuItem, but this will remain for backwards compatibility
18988        kind: options.kind,
18989        kinds: options.kinds,
18990        "default": false,
18991        mode: 'disabled'
18992      };
18993
18994      if (!options.kinds) {
18995        options.kinds = [options.kind];
18996      }
18997
18998      if (options.label) {
18999        options.track.label = options.label;
19000      } else {
19001        options.track.label = options.kinds.join(' and ') + ' off';
19002      } // MenuItem is selectable
19003
19004
19005      options.selectable = true; // MenuItem is NOT multiSelectable (i.e. only one can be marked "selected" at a time)
19006
19007      options.multiSelectable = false;
19008      return _TextTrackMenuItem.call(this, player, options) || this;
19009    }
19010    /**
19011     * Handle text track change
19012     *
19013     * @param {EventTarget~Event} event
19014     *        The event that caused this function to run
19015     */
19016
19017
19018    var _proto = OffTextTrackMenuItem.prototype;
19019
19020    _proto.handleTracksChange = function handleTracksChange(event) {
19021      var tracks = this.player().textTracks();
19022      var shouldBeSelected = true;
19023
19024      for (var i = 0, l = tracks.length; i < l; i++) {
19025        var track = tracks[i];
19026
19027        if (this.options_.kinds.indexOf(track.kind) > -1 && track.mode === 'showing') {
19028          shouldBeSelected = false;
19029          break;
19030        }
19031      } // Prevent redundant selected() calls because they may cause
19032      // screen readers to read the appended control text unnecessarily
19033
19034
19035      if (shouldBeSelected !== this.isSelected_) {
19036        this.selected(shouldBeSelected);
19037      }
19038    };
19039
19040    _proto.handleSelectedLanguageChange = function handleSelectedLanguageChange(event) {
19041      var tracks = this.player().textTracks();
19042      var allHidden = true;
19043
19044      for (var i = 0, l = tracks.length; i < l; i++) {
19045        var track = tracks[i];
19046
19047        if (['captions', 'descriptions', 'subtitles'].indexOf(track.kind) > -1 && track.mode === 'showing') {
19048          allHidden = false;
19049          break;
19050        }
19051      }
19052
19053      if (allHidden) {
19054        this.player_.cache_.selectedLanguage = {
19055          enabled: false
19056        };
19057      }
19058    };
19059
19060    return OffTextTrackMenuItem;
19061  }(TextTrackMenuItem);
19062
19063  Component.registerComponent('OffTextTrackMenuItem', OffTextTrackMenuItem);
19064
19065  /**
19066   * The base class for buttons that toggle specific text track types (e.g. subtitles)
19067   *
19068   * @extends MenuButton
19069   */
19070
19071  var TextTrackButton =
19072  /*#__PURE__*/
19073  function (_TrackButton) {
19074    _inheritsLoose(TextTrackButton, _TrackButton);
19075
19076    /**
19077     * Creates an instance of this class.
19078     *
19079     * @param {Player} player
19080     *        The `Player` that this class should be attached to.
19081     *
19082     * @param {Object} [options={}]
19083     *        The key/value store of player options.
19084     */
19085    function TextTrackButton(player, options) {
19086      if (options === void 0) {
19087        options = {};
19088      }
19089
19090      options.tracks = player.textTracks();
19091      return _TrackButton.call(this, player, options) || this;
19092    }
19093    /**
19094     * Create a menu item for each text track
19095     *
19096     * @param {TextTrackMenuItem[]} [items=[]]
19097     *        Existing array of items to use during creation
19098     *
19099     * @return {TextTrackMenuItem[]}
19100     *         Array of menu items that were created
19101     */
19102
19103
19104    var _proto = TextTrackButton.prototype;
19105
19106    _proto.createItems = function createItems(items, TrackMenuItem) {
19107      if (items === void 0) {
19108        items = [];
19109      }
19110
19111      if (TrackMenuItem === void 0) {
19112        TrackMenuItem = TextTrackMenuItem;
19113      }
19114
19115      // Label is an override for the [track] off label
19116      // USed to localise captions/subtitles
19117      var label;
19118
19119      if (this.label_) {
19120        label = this.label_ + " off";
19121      } // Add an OFF menu item to turn all tracks off
19122
19123
19124      items.push(new OffTextTrackMenuItem(this.player_, {
19125        kinds: this.kinds_,
19126        kind: this.kind_,
19127        label: label
19128      }));
19129      this.hideThreshold_ += 1;
19130      var tracks = this.player_.textTracks();
19131
19132      if (!Array.isArray(this.kinds_)) {
19133        this.kinds_ = [this.kind_];
19134      }
19135
19136      for (var i = 0; i < tracks.length; i++) {
19137        var track = tracks[i]; // only add tracks that are of an appropriate kind and have a label
19138
19139        if (this.kinds_.indexOf(track.kind) > -1) {
19140          var item = new TrackMenuItem(this.player_, {
19141            track: track,
19142            kinds: this.kinds_,
19143            kind: this.kind_,
19144            // MenuItem is selectable
19145            selectable: true,
19146            // MenuItem is NOT multiSelectable (i.e. only one can be marked "selected" at a time)
19147            multiSelectable: false
19148          });
19149          item.addClass("vjs-" + track.kind + "-menu-item");
19150          items.push(item);
19151        }
19152      }
19153
19154      return items;
19155    };
19156
19157    return TextTrackButton;
19158  }(TrackButton);
19159
19160  Component.registerComponent('TextTrackButton', TextTrackButton);
19161
19162  /**
19163   * The chapter track menu item
19164   *
19165   * @extends MenuItem
19166   */
19167
19168  var ChaptersTrackMenuItem =
19169  /*#__PURE__*/
19170  function (_MenuItem) {
19171    _inheritsLoose(ChaptersTrackMenuItem, _MenuItem);
19172
19173    /**
19174     * Creates an instance of this class.
19175     *
19176     * @param {Player} player
19177     *        The `Player` that this class should be attached to.
19178     *
19179     * @param {Object} [options]
19180     *        The key/value store of player options.
19181     */
19182    function ChaptersTrackMenuItem(player, options) {
19183      var _this;
19184
19185      var track = options.track;
19186      var cue = options.cue;
19187      var currentTime = player.currentTime(); // Modify options for parent MenuItem class's init.
19188
19189      options.selectable = true;
19190      options.multiSelectable = false;
19191      options.label = cue.text;
19192      options.selected = cue.startTime <= currentTime && currentTime < cue.endTime;
19193      _this = _MenuItem.call(this, player, options) || this;
19194      _this.track = track;
19195      _this.cue = cue;
19196      track.addEventListener('cuechange', bind(_assertThisInitialized(_this), _this.update));
19197      return _this;
19198    }
19199    /**
19200     * This gets called when an `ChaptersTrackMenuItem` is "clicked". See
19201     * {@link ClickableComponent} for more detailed information on what a click can be.
19202     *
19203     * @param {EventTarget~Event} [event]
19204     *        The `keydown`, `tap`, or `click` event that caused this function to be
19205     *        called.
19206     *
19207     * @listens tap
19208     * @listens click
19209     */
19210
19211
19212    var _proto = ChaptersTrackMenuItem.prototype;
19213
19214    _proto.handleClick = function handleClick(event) {
19215      _MenuItem.prototype.handleClick.call(this);
19216
19217      this.player_.currentTime(this.cue.startTime);
19218      this.update(this.cue.startTime);
19219    }
19220    /**
19221     * Update chapter menu item
19222     *
19223     * @param {EventTarget~Event} [event]
19224     *        The `cuechange` event that caused this function to run.
19225     *
19226     * @listens TextTrack#cuechange
19227     */
19228    ;
19229
19230    _proto.update = function update(event) {
19231      var cue = this.cue;
19232      var currentTime = this.player_.currentTime(); // vjs.log(currentTime, cue.startTime);
19233
19234      this.selected(cue.startTime <= currentTime && currentTime < cue.endTime);
19235    };
19236
19237    return ChaptersTrackMenuItem;
19238  }(MenuItem);
19239
19240  Component.registerComponent('ChaptersTrackMenuItem', ChaptersTrackMenuItem);
19241
19242  /**
19243   * The button component for toggling and selecting chapters
19244   * Chapters act much differently than other text tracks
19245   * Cues are navigation vs. other tracks of alternative languages
19246   *
19247   * @extends TextTrackButton
19248   */
19249
19250  var ChaptersButton =
19251  /*#__PURE__*/
19252  function (_TextTrackButton) {
19253    _inheritsLoose(ChaptersButton, _TextTrackButton);
19254
19255    /**
19256     * Creates an instance of this class.
19257     *
19258     * @param {Player} player
19259     *        The `Player` that this class should be attached to.
19260     *
19261     * @param {Object} [options]
19262     *        The key/value store of player options.
19263     *
19264     * @param {Component~ReadyCallback} [ready]
19265     *        The function to call when this function is ready.
19266     */
19267    function ChaptersButton(player, options, ready) {
19268      return _TextTrackButton.call(this, player, options, ready) || this;
19269    }
19270    /**
19271     * Builds the default DOM `className`.
19272     *
19273     * @return {string}
19274     *         The DOM `className` for this object.
19275     */
19276
19277
19278    var _proto = ChaptersButton.prototype;
19279
19280    _proto.buildCSSClass = function buildCSSClass() {
19281      return "vjs-chapters-button " + _TextTrackButton.prototype.buildCSSClass.call(this);
19282    };
19283
19284    _proto.buildWrapperCSSClass = function buildWrapperCSSClass() {
19285      return "vjs-chapters-button " + _TextTrackButton.prototype.buildWrapperCSSClass.call(this);
19286    }
19287    /**
19288     * Update the menu based on the current state of its items.
19289     *
19290     * @param {EventTarget~Event} [event]
19291     *        An event that triggered this function to run.
19292     *
19293     * @listens TextTrackList#addtrack
19294     * @listens TextTrackList#removetrack
19295     * @listens TextTrackList#change
19296     */
19297    ;
19298
19299    _proto.update = function update(event) {
19300      if (!this.track_ || event && (event.type === 'addtrack' || event.type === 'removetrack')) {
vendor: 1,553 bytes, lines 19301-19354
19301        this.setTrack(this.findChaptersTrack());
19302      }
19303
19304      _TextTrackButton.prototype.update.call(this);
19305    }
19306    /**
19307     * Set the currently selected track for the chapters button.
19308     *
19309     * @param {TextTrack} track
19310     *        The new track to select. Nothing will change if this is the currently selected
19311     *        track.
19312     */
19313    ;
19314
19315    _proto.setTrack = function setTrack(track) {
19316      if (this.track_ === track) {
19317        return;
19318      }
19319
19320      if (!this.updateHandler_) {
19321        this.updateHandler_ = this.update.bind(this);
19322      } // here this.track_ refers to the old track instance
19323
19324
19325      if (this.track_) {
19326        var remoteTextTrackEl = this.player_.remoteTextTrackEls().getTrackElementByTrack_(this.track_);
19327
19328        if (remoteTextTrackEl) {
19329          remoteTextTrackEl.removeEventListener('load', this.updateHandler_);
19330        }
19331
19332        this.track_ = null;
19333      }
19334
19335      this.track_ = track; // here this.track_ refers to the new track instance
19336
19337      if (this.track_) {
19338        this.track_.mode = 'hidden';
19339
19340        var _remoteTextTrackEl = this.player_.remoteTextTrackEls().getTrackElementByTrack_(this.track_);
19341
19342        if (_remoteTextTrackEl) {
19343          _remoteTextTrackEl.addEventListener('load', this.updateHandler_);
19344        }
19345      }
19346    }
19347    /**
19348     * Find the track object that is currently in use by this ChaptersButton
19349     *
19350     * @return {TextTrack|undefined}
19351     *         The current track or undefined if none was found.
19352     */
19353    ;
19354
vendor: 22,466 bytes, lines 19355-20145
19355    _proto.findChaptersTrack = function findChaptersTrack() {
19356      var tracks = this.player_.textTracks() || [];
19357
19358      for (var i = tracks.length - 1; i >= 0; i--) {
19359        // We will always choose the last track as our chaptersTrack
19360        var track = tracks[i];
19361
19362        if (track.kind === this.kind_) {
19363          return track;
19364        }
19365      }
19366    }
19367    /**
19368     * Get the caption for the ChaptersButton based on the track label. This will also
19369     * use the current tracks localized kind as a fallback if a label does not exist.
19370     *
19371     * @return {string}
19372     *         The tracks current label or the localized track kind.
19373     */
19374    ;
19375
19376    _proto.getMenuCaption = function getMenuCaption() {
19377      if (this.track_ && this.track_.label) {
19378        return this.track_.label;
19379      }
19380
19381      return this.localize(toTitleCase(this.kind_));
19382    }
19383    /**
19384     * Create menu from chapter track
19385     *
19386     * @return {Menu}
19387     *         New menu for the chapter buttons
19388     */
19389    ;
19390
19391    _proto.createMenu = function createMenu() {
19392      this.options_.title = this.getMenuCaption();
19393      return _TextTrackButton.prototype.createMenu.call(this);
19394    }
19395    /**
19396     * Create a menu item for each text track
19397     *
19398     * @return {TextTrackMenuItem[]}
19399     *         Array of menu items
19400     */
19401    ;
19402
19403    _proto.createItems = function createItems() {
19404      var items = [];
19405
19406      if (!this.track_) {
19407        return items;
19408      }
19409
19410      var cues = this.track_.cues;
19411
19412      if (!cues) {
19413        return items;
19414      }
19415
19416      for (var i = 0, l = cues.length; i < l; i++) {
19417        var cue = cues[i];
19418        var mi = new ChaptersTrackMenuItem(this.player_, {
19419          track: this.track_,
19420          cue: cue
19421        });
19422        items.push(mi);
19423      }
19424
19425      return items;
19426    };
19427
19428    return ChaptersButton;
19429  }(TextTrackButton);
19430  /**
19431   * `kind` of TextTrack to look for to associate it with this menu.
19432   *
19433   * @type {string}
19434   * @private
19435   */
19436
19437
19438  ChaptersButton.prototype.kind_ = 'chapters';
19439  /**
19440   * The text that should display over the `ChaptersButton`s controls. Added for localization.
19441   *
19442   * @type {string}
19443   * @private
19444   */
19445
19446  ChaptersButton.prototype.controlText_ = 'Chapters';
19447  Component.registerComponent('ChaptersButton', ChaptersButton);
19448
19449  /**
19450   * The button component for toggling and selecting descriptions
19451   *
19452   * @extends TextTrackButton
19453   */
19454
19455  var DescriptionsButton =
19456  /*#__PURE__*/
19457  function (_TextTrackButton) {
19458    _inheritsLoose(DescriptionsButton, _TextTrackButton);
19459
19460    /**
19461     * Creates an instance of this class.
19462     *
19463     * @param {Player} player
19464     *        The `Player` that this class should be attached to.
19465     *
19466     * @param {Object} [options]
19467     *        The key/value store of player options.
19468     *
19469     * @param {Component~ReadyCallback} [ready]
19470     *        The function to call when this component is ready.
19471     */
19472    function DescriptionsButton(player, options, ready) {
19473      var _this;
19474
19475      _this = _TextTrackButton.call(this, player, options, ready) || this;
19476      var tracks = player.textTracks();
19477      var changeHandler = bind(_assertThisInitialized(_this), _this.handleTracksChange);
19478      tracks.addEventListener('change', changeHandler);
19479
19480      _this.on('dispose', function () {
19481        tracks.removeEventListener('change', changeHandler);
19482      });
19483
19484      return _this;
19485    }
19486    /**
19487     * Handle text track change
19488     *
19489     * @param {EventTarget~Event} event
19490     *        The event that caused this function to run
19491     *
19492     * @listens TextTrackList#change
19493     */
19494
19495
19496    var _proto = DescriptionsButton.prototype;
19497
19498    _proto.handleTracksChange = function handleTracksChange(event) {
19499      var tracks = this.player().textTracks();
19500      var disabled = false; // Check whether a track of a different kind is showing
19501
19502      for (var i = 0, l = tracks.length; i < l; i++) {
19503        var track = tracks[i];
19504
19505        if (track.kind !== this.kind_ && track.mode === 'showing') {
19506          disabled = true;
19507          break;
19508        }
19509      } // If another track is showing, disable this menu button
19510
19511
19512      if (disabled) {
19513        this.disable();
19514      } else {
19515        this.enable();
19516      }
19517    }
19518    /**
19519     * Builds the default DOM `className`.
19520     *
19521     * @return {string}
19522     *         The DOM `className` for this object.
19523     */
19524    ;
19525
19526    _proto.buildCSSClass = function buildCSSClass() {
19527      return "vjs-descriptions-button " + _TextTrackButton.prototype.buildCSSClass.call(this);
19528    };
19529
19530    _proto.buildWrapperCSSClass = function buildWrapperCSSClass() {
19531      return "vjs-descriptions-button " + _TextTrackButton.prototype.buildWrapperCSSClass.call(this);
19532    };
19533
19534    return DescriptionsButton;
19535  }(TextTrackButton);
19536  /**
19537   * `kind` of TextTrack to look for to associate it with this menu.
19538   *
19539   * @type {string}
19540   * @private
19541   */
19542
19543
19544  DescriptionsButton.prototype.kind_ = 'descriptions';
19545  /**
19546   * The text that should display over the `DescriptionsButton`s controls. Added for localization.
19547   *
19548   * @type {string}
19549   * @private
19550   */
19551
19552  DescriptionsButton.prototype.controlText_ = 'Descriptions';
19553  Component.registerComponent('DescriptionsButton', DescriptionsButton);
19554
19555  /**
19556   * The button component for toggling and selecting subtitles
19557   *
19558   * @extends TextTrackButton
19559   */
19560
19561  var SubtitlesButton =
19562  /*#__PURE__*/
19563  function (_TextTrackButton) {
19564    _inheritsLoose(SubtitlesButton, _TextTrackButton);
19565
19566    /**
19567     * Creates an instance of this class.
19568     *
19569     * @param {Player} player
19570     *        The `Player` that this class should be attached to.
19571     *
19572     * @param {Object} [options]
19573     *        The key/value store of player options.
19574     *
19575     * @param {Component~ReadyCallback} [ready]
19576     *        The function to call when this component is ready.
19577     */
19578    function SubtitlesButton(player, options, ready) {
19579      return _TextTrackButton.call(this, player, options, ready) || this;
19580    }
19581    /**
19582     * Builds the default DOM `className`.
19583     *
19584     * @return {string}
19585     *         The DOM `className` for this object.
19586     */
19587
19588
19589    var _proto = SubtitlesButton.prototype;
19590
19591    _proto.buildCSSClass = function buildCSSClass() {
19592      return "vjs-subtitles-button " + _TextTrackButton.prototype.buildCSSClass.call(this);
19593    };
19594
19595    _proto.buildWrapperCSSClass = function buildWrapperCSSClass() {
19596      return "vjs-subtitles-button " + _TextTrackButton.prototype.buildWrapperCSSClass.call(this);
19597    };
19598
19599    return SubtitlesButton;
19600  }(TextTrackButton);
19601  /**
19602   * `kind` of TextTrack to look for to associate it with this menu.
19603   *
19604   * @type {string}
19605   * @private
19606   */
19607
19608
19609  SubtitlesButton.prototype.kind_ = 'subtitles';
19610  /**
19611   * The text that should display over the `SubtitlesButton`s controls. Added for localization.
19612   *
19613   * @type {string}
19614   * @private
19615   */
19616
19617  SubtitlesButton.prototype.controlText_ = 'Subtitles';
19618  Component.registerComponent('SubtitlesButton', SubtitlesButton);
19619
19620  /**
19621   * The menu item for caption track settings menu
19622   *
19623   * @extends TextTrackMenuItem
19624   */
19625
19626  var CaptionSettingsMenuItem =
19627  /*#__PURE__*/
19628  function (_TextTrackMenuItem) {
19629    _inheritsLoose(CaptionSettingsMenuItem, _TextTrackMenuItem);
19630
19631    /**
19632     * Creates an instance of this class.
19633     *
19634     * @param {Player} player
19635     *        The `Player` that this class should be attached to.
19636     *
19637     * @param {Object} [options]
19638     *        The key/value store of player options.
19639     */
19640    function CaptionSettingsMenuItem(player, options) {
19641      var _this;
19642
19643      options.track = {
19644        player: player,
19645        kind: options.kind,
19646        label: options.kind + ' settings',
19647        selectable: false,
19648        "default": false,
19649        mode: 'disabled'
19650      }; // CaptionSettingsMenuItem has no concept of 'selected'
19651
19652      options.selectable = false;
19653      options.name = 'CaptionSettingsMenuItem';
19654      _this = _TextTrackMenuItem.call(this, player, options) || this;
19655
19656      _this.addClass('vjs-texttrack-settings');
19657
19658      _this.controlText(', opens ' + options.kind + ' settings dialog');
19659
19660      return _this;
19661    }
19662    /**
19663     * This gets called when an `CaptionSettingsMenuItem` is "clicked". See
19664     * {@link ClickableComponent} for more detailed information on what a click can be.
19665     *
19666     * @param {EventTarget~Event} [event]
19667     *        The `keydown`, `tap`, or `click` event that caused this function to be
19668     *        called.
19669     *
19670     * @listens tap
19671     * @listens click
19672     */
19673
19674
19675    var _proto = CaptionSettingsMenuItem.prototype;
19676
19677    _proto.handleClick = function handleClick(event) {
19678      this.player().getChild('textTrackSettings').open();
19679    };
19680
19681    return CaptionSettingsMenuItem;
19682  }(TextTrackMenuItem);
19683
19684  Component.registerComponent('CaptionSettingsMenuItem', CaptionSettingsMenuItem);
19685
19686  /**
19687   * The button component for toggling and selecting captions
19688   *
19689   * @extends TextTrackButton
19690   */
19691
19692  var CaptionsButton =
19693  /*#__PURE__*/
19694  function (_TextTrackButton) {
19695    _inheritsLoose(CaptionsButton, _TextTrackButton);
19696
19697    /**
19698     * Creates an instance of this class.
19699     *
19700     * @param {Player} player
19701     *        The `Player` that this class should be attached to.
19702     *
19703     * @param {Object} [options]
19704     *        The key/value store of player options.
19705     *
19706     * @param {Component~ReadyCallback} [ready]
19707     *        The function to call when this component is ready.
19708     */
19709    function CaptionsButton(player, options, ready) {
19710      return _TextTrackButton.call(this, player, options, ready) || this;
19711    }
19712    /**
19713     * Builds the default DOM `className`.
19714     *
19715     * @return {string}
19716     *         The DOM `className` for this object.
19717     */
19718
19719
19720    var _proto = CaptionsButton.prototype;
19721
19722    _proto.buildCSSClass = function buildCSSClass() {
19723      return "vjs-captions-button " + _TextTrackButton.prototype.buildCSSClass.call(this);
19724    };
19725
19726    _proto.buildWrapperCSSClass = function buildWrapperCSSClass() {
19727      return "vjs-captions-button " + _TextTrackButton.prototype.buildWrapperCSSClass.call(this);
19728    }
19729    /**
19730     * Create caption menu items
19731     *
19732     * @return {CaptionSettingsMenuItem[]}
19733     *         The array of current menu items.
19734     */
19735    ;
19736
19737    _proto.createItems = function createItems() {
19738      var items = [];
19739
19740      if (!(this.player().tech_ && this.player().tech_.featuresNativeTextTracks) && this.player().getChild('textTrackSettings')) {
19741        items.push(new CaptionSettingsMenuItem(this.player_, {
19742          kind: this.kind_
19743        }));
19744        this.hideThreshold_ += 1;
19745      }
19746
19747      return _TextTrackButton.prototype.createItems.call(this, items);
19748    };
19749
19750    return CaptionsButton;
19751  }(TextTrackButton);
19752  /**
19753   * `kind` of TextTrack to look for to associate it with this menu.
19754   *
19755   * @type {string}
19756   * @private
19757   */
19758
19759
19760  CaptionsButton.prototype.kind_ = 'captions';
19761  /**
19762   * The text that should display over the `CaptionsButton`s controls. Added for localization.
19763   *
19764   * @type {string}
19765   * @private
19766   */
19767
19768  CaptionsButton.prototype.controlText_ = 'Captions';
19769  Component.registerComponent('CaptionsButton', CaptionsButton);
19770
19771  /**
19772   * SubsCapsMenuItem has an [cc] icon to distinguish captions from subtitles
19773   * in the SubsCapsMenu.
19774   *
19775   * @extends TextTrackMenuItem
19776   */
19777
19778  var SubsCapsMenuItem =
19779  /*#__PURE__*/
19780  function (_TextTrackMenuItem) {
19781    _inheritsLoose(SubsCapsMenuItem, _TextTrackMenuItem);
19782
19783    function SubsCapsMenuItem() {
19784      return _TextTrackMenuItem.apply(this, arguments) || this;
19785    }
19786
19787    var _proto = SubsCapsMenuItem.prototype;
19788
19789    _proto.createEl = function createEl(type, props, attrs) {
19790      var innerHTML = "<span class=\"vjs-menu-item-text\">" + this.localize(this.options_.label);
19791
19792      if (this.options_.track.kind === 'captions') {
19793        innerHTML += "\n        <span aria-hidden=\"true\" class=\"vjs-icon-placeholder\"></span>\n        <span class=\"vjs-control-text\"> " + this.localize('Captions') + "</span>\n      ";
19794      }
19795
19796      innerHTML += '</span>';
19797
19798      var el = _TextTrackMenuItem.prototype.createEl.call(this, type, assign({
19799        innerHTML: innerHTML
19800      }, props), attrs);
19801
19802      return el;
19803    };
19804
19805    return SubsCapsMenuItem;
19806  }(TextTrackMenuItem);
19807
19808  Component.registerComponent('SubsCapsMenuItem', SubsCapsMenuItem);
19809
19810  /**
19811   * The button component for toggling and selecting captions and/or subtitles
19812   *
19813   * @extends TextTrackButton
19814   */
19815
19816  var SubsCapsButton =
19817  /*#__PURE__*/
19818  function (_TextTrackButton) {
19819    _inheritsLoose(SubsCapsButton, _TextTrackButton);
19820
19821    function SubsCapsButton(player, options) {
19822      var _this;
19823
19824      if (options === void 0) {
19825        options = {};
19826      }
19827
19828      _this = _TextTrackButton.call(this, player, options) || this; // Although North America uses "captions" in most cases for
19829      // "captions and subtitles" other locales use "subtitles"
19830
19831      _this.label_ = 'subtitles';
19832
19833      if (['en', 'en-us', 'en-ca', 'fr-ca'].indexOf(_this.player_.language_) > -1) {
19834        _this.label_ = 'captions';
19835      }
19836
19837      _this.menuButton_.controlText(toTitleCase(_this.label_));
19838
19839      return _this;
19840    }
19841    /**
19842     * Builds the default DOM `className`.
19843     *
19844     * @return {string}
19845     *         The DOM `className` for this object.
19846     */
19847
19848
19849    var _proto = SubsCapsButton.prototype;
19850
19851    _proto.buildCSSClass = function buildCSSClass() {
19852      return "vjs-subs-caps-button " + _TextTrackButton.prototype.buildCSSClass.call(this);
19853    };
19854
19855    _proto.buildWrapperCSSClass = function buildWrapperCSSClass() {
19856      return "vjs-subs-caps-button " + _TextTrackButton.prototype.buildWrapperCSSClass.call(this);
19857    }
19858    /**
19859     * Create caption/subtitles menu items
19860     *
19861     * @return {CaptionSettingsMenuItem[]}
19862     *         The array of current menu items.
19863     */
19864    ;
19865
19866    _proto.createItems = function createItems() {
19867      var items = [];
19868
19869      if (!(this.player().tech_ && this.player().tech_.featuresNativeTextTracks) && this.player().getChild('textTrackSettings')) {
19870        items.push(new CaptionSettingsMenuItem(this.player_, {
19871          kind: this.label_
19872        }));
19873        this.hideThreshold_ += 1;
19874      }
19875
19876      items = _TextTrackButton.prototype.createItems.call(this, items, SubsCapsMenuItem);
19877      return items;
19878    };
19879
19880    return SubsCapsButton;
19881  }(TextTrackButton);
19882  /**
19883   * `kind`s of TextTrack to look for to associate it with this menu.
19884   *
19885   * @type {array}
19886   * @private
19887   */
19888
19889
19890  SubsCapsButton.prototype.kinds_ = ['captions', 'subtitles'];
19891  /**
19892   * The text that should display over the `SubsCapsButton`s controls.
19893   *
19894   *
19895   * @type {string}
19896   * @private
19897   */
19898
19899  SubsCapsButton.prototype.controlText_ = 'Subtitles';
19900  Component.registerComponent('SubsCapsButton', SubsCapsButton);
19901
19902  /**
19903   * An {@link AudioTrack} {@link MenuItem}
19904   *
19905   * @extends MenuItem
19906   */
19907
19908  var AudioTrackMenuItem =
19909  /*#__PURE__*/
19910  function (_MenuItem) {
19911    _inheritsLoose(AudioTrackMenuItem, _MenuItem);
19912
19913    /**
19914     * Creates an instance of this class.
19915     *
19916     * @param {Player} player
19917     *        The `Player` that this class should be attached to.
19918     *
19919     * @param {Object} [options]
19920     *        The key/value store of player options.
19921     */
19922    function AudioTrackMenuItem(player, options) {
19923      var _this;
19924
19925      var track = options.track;
19926      var tracks = player.audioTracks(); // Modify options for parent MenuItem class's init.
19927
19928      options.label = track.label || track.language || 'Unknown';
19929      options.selected = track.enabled;
19930      _this = _MenuItem.call(this, player, options) || this;
19931      _this.track = track;
19932
19933      _this.addClass("vjs-" + track.kind + "-menu-item");
19934
19935      var changeHandler = function changeHandler() {
19936        for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) {
19937          args[_key] = arguments[_key];
19938        }
19939
19940        _this.handleTracksChange.apply(_assertThisInitialized(_this), args);
19941      };
19942
19943      tracks.addEventListener('change', changeHandler);
19944
19945      _this.on('dispose', function () {
19946        tracks.removeEventListener('change', changeHandler);
19947      });
19948
19949      return _this;
19950    }
19951
19952    var _proto = AudioTrackMenuItem.prototype;
19953
19954    _proto.createEl = function createEl(type, props, attrs) {
19955      var innerHTML = "<span class=\"vjs-menu-item-text\">" + this.localize(this.options_.label);
19956
19957      if (this.options_.track.kind === 'main-desc') {
19958        innerHTML += "\n        <span aria-hidden=\"true\" class=\"vjs-icon-placeholder\"></span>\n        <span class=\"vjs-control-text\"> " + this.localize('Descriptions') + "</span>\n      ";
19959      }
19960
19961      innerHTML += '</span>';
19962
19963      var el = _MenuItem.prototype.createEl.call(this, type, assign({
19964        innerHTML: innerHTML
19965      }, props), attrs);
19966
19967      return el;
19968    }
19969    /**
19970     * This gets called when an `AudioTrackMenuItem is "clicked". See {@link ClickableComponent}
19971     * for more detailed information on what a click can be.
19972     *
19973     * @param {EventTarget~Event} [event]
19974     *        The `keydown`, `tap`, or `click` event that caused this function to be
19975     *        called.
19976     *
19977     * @listens tap
19978     * @listens click
19979     */
19980    ;
19981
19982    _proto.handleClick = function handleClick(event) {
19983      var tracks = this.player_.audioTracks();
19984
19985      _MenuItem.prototype.handleClick.call(this, event);
19986
19987      for (var i = 0; i < tracks.length; i++) {
19988        var track = tracks[i];
19989        track.enabled = track === this.track;
19990      }
19991    }
19992    /**
19993     * Handle any {@link AudioTrack} change.
19994     *
19995     * @param {EventTarget~Event} [event]
19996     *        The {@link AudioTrackList#change} event that caused this to run.
19997     *
19998     * @listens AudioTrackList#change
19999     */
20000    ;
20001
20002    _proto.handleTracksChange = function handleTracksChange(event) {
20003      this.selected(this.track.enabled);
20004    };
20005
20006    return AudioTrackMenuItem;
20007  }(MenuItem);
20008
20009  Component.registerComponent('AudioTrackMenuItem', AudioTrackMenuItem);
20010
20011  /**
20012   * The base class for buttons that toggle specific {@link AudioTrack} types.
20013   *
20014   * @extends TrackButton
20015   */
20016
20017  var AudioTrackButton =
20018  /*#__PURE__*/
20019  function (_TrackButton) {
20020    _inheritsLoose(AudioTrackButton, _TrackButton);
20021
20022    /**
20023     * Creates an instance of this class.
20024     *
20025     * @param {Player} player
20026     *        The `Player` that this class should be attached to.
20027     *
20028     * @param {Object} [options={}]
20029     *        The key/value store of player options.
20030     */
20031    function AudioTrackButton(player, options) {
20032      if (options === void 0) {
20033        options = {};
20034      }
20035
20036      options.tracks = player.audioTracks();
20037      return _TrackButton.call(this, player, options) || this;
20038    }
20039    /**
20040     * Builds the default DOM `className`.
20041     *
20042     * @return {string}
20043     *         The DOM `className` for this object.
20044     */
20045
20046
20047    var _proto = AudioTrackButton.prototype;
20048
20049    _proto.buildCSSClass = function buildCSSClass() {
20050      return "vjs-audio-button " + _TrackButton.prototype.buildCSSClass.call(this);
20051    };
20052
20053    _proto.buildWrapperCSSClass = function buildWrapperCSSClass() {
20054      return "vjs-audio-button " + _TrackButton.prototype.buildWrapperCSSClass.call(this);
20055    }
20056    /**
20057     * Create a menu item for each audio track
20058     *
20059     * @param {AudioTrackMenuItem[]} [items=[]]
20060     *        An array of existing menu items to use.
20061     *
20062     * @return {AudioTrackMenuItem[]}
20063     *         An array of menu items
20064     */
20065    ;
20066
20067    _proto.createItems = function createItems(items) {
20068      if (items === void 0) {
20069        items = [];
20070      }
20071
20072      // if there's only one audio track, there no point in showing it
20073      this.hideThreshold_ = 1;
20074      var tracks = this.player_.audioTracks();
20075
20076      for (var i = 0; i < tracks.length; i++) {
20077        var track = tracks[i];
20078        items.push(new AudioTrackMenuItem(this.player_, {
20079          track: track,
20080          // MenuItem is selectable
20081          selectable: true,
20082          // MenuItem is NOT multiSelectable (i.e. only one can be marked "selected" at a time)
20083          multiSelectable: false
20084        }));
20085      }
20086
20087      return items;
20088    };
20089
20090    return AudioTrackButton;
20091  }(TrackButton);
20092  /**
20093   * The text that should display over the `AudioTrackButton`s controls. Added for localization.
20094   *
20095   * @type {string}
20096   * @private
20097   */
20098
20099
20100  AudioTrackButton.prototype.controlText_ = 'Audio Track';
20101  Component.registerComponent('AudioTrackButton', AudioTrackButton);
20102
20103  /**
20104   * The specific menu item type for selecting a playback rate.
20105   *
20106   * @extends MenuItem
20107   */
20108
20109  var PlaybackRateMenuItem =
20110  /*#__PURE__*/
20111  function (_MenuItem) {
20112    _inheritsLoose(PlaybackRateMenuItem, _MenuItem);
20113
20114    /**
20115     * Creates an instance of this class.
20116     *
20117     * @param {Player} player
20118     *        The `Player` that this class should be attached to.
20119     *
20120     * @param {Object} [options]
20121     *        The key/value store of player options.
20122     */
20123    function PlaybackRateMenuItem(player, options) {
20124      var _this;
20125
20126      var label = options.rate;
20127      var rate = parseFloat(label, 10); // Modify options for parent MenuItem class's init.
20128
20129      options.label = label;
20130      options.selected = rate === 1;
20131      options.selectable = true;
20132      options.multiSelectable = false;
20133      _this = _MenuItem.call(this, player, options) || this;
20134      _this.label = label;
20135      _this.rate = rate;
20136
20137      _this.on(player, 'ratechange', _this.update);
20138
20139      return _this;
20140    }
20141    /**
20142     * This gets called when an `PlaybackRateMenuItem` is "clicked". See
20143     * {@link ClickableComponent} for more detailed information on what a click can be.
20144     *
20145     * @param {EventTarget~Event} [event]
20146     *        The `keydown`, `tap`, or `click` event that caused this function to be
20147     *        called.
20148     *
20149     * @listens tap
20150     * @listens click
20151     */
20152
20153
20154    var _proto = PlaybackRateMenuItem.prototype;
20155
20156    _proto.handleClick = function handleClick(event) {
20157      _MenuItem.prototype.handleClick.call(this);
20158
20159      this.player().playbackRate(this.rate);
20160    }
20161    /**
20162     * Update the PlaybackRateMenuItem when the playbackrate changes.
20163     *
20164     * @param {EventTarget~Event} [event]
20165     *        The `ratechange` event that caused this function to run.
20166     *
20167     * @listens Player#ratechange
20168     */
20169    ;
20170
20171    _proto.update = function update(event) {
20172      this.selected(this.player().playbackRate() === this.rate);
20173    };
20174
20175    return PlaybackRateMenuItem;
20176  }(MenuItem);
20177  /**
20178   * The text that should display over the `PlaybackRateMenuItem`s controls. Added for localization.
20179   *
20180   * @type {string}
20181   * @private
20182   */
20183
20184
20185  PlaybackRateMenuItem.prototype.contentElType = 'button';
20186  Component.registerComponent('PlaybackRateMenuItem', PlaybackRateMenuItem);
20187
20188  /**
20189   * The component for controlling the playback rate.
20190   *
20191   * @extends MenuButton
20192   */
20193
20194  var PlaybackRateMenuButton =
20195  /*#__PURE__*/
20196  function (_MenuButton) {
20197    _inheritsLoose(PlaybackRateMenuButton, _MenuButton);
20198
20199    /**
20200     * Creates an instance of this class.
20201     *
20202     * @param {Player} player
20203     *        The `Player` that this class should be attached to.
20204     *
20205     * @param {Object} [options]
20206     *        The key/value store of player options.
20207     */
20208    function PlaybackRateMenuButton(player, options) {
20209      var _this;
20210
20211      _this = _MenuButton.call(this, player, options) || this;
20212
20213      _this.updateVisibility();
20214
20215      _this.updateLabel();
20216
20217      _this.on(player, 'loadstart', _this.updateVisibility);
20218
20219      _this.on(player, 'ratechange', _this.updateLabel);
20220
20221      return _this;
20222    }
20223    /**
20224     * Create the `Component`'s DOM element
20225     *
20226     * @return {Element}
20227     *         The element that was created.
20228     */
20229
20230
20231    var _proto = PlaybackRateMenuButton.prototype;
20232
20233    _proto.createEl = function createEl$1() {
20234      var el = _MenuButton.prototype.createEl.call(this);
20235
20236      this.labelEl_ = createEl('div', {
20237        className: 'vjs-playback-rate-value',
20238        innerHTML: '1x'
20239      });
20240      el.appendChild(this.labelEl_);
20241      return el;
20242    };
20243
20244    _proto.dispose = function dispose() {
20245      this.labelEl_ = null;
20246
20247      _MenuButton.prototype.dispose.call(this);
20248    }
20249    /**
20250     * Builds the default DOM `className`.
20251     *
20252     * @return {string}
20253     *         The DOM `className` for this object.
20254     */
20255    ;
20256
20257    _proto.buildCSSClass = function buildCSSClass() {
20258      return "vjs-playback-rate " + _MenuButton.prototype.buildCSSClass.call(this);
20259    };
20260
20261    _proto.buildWrapperCSSClass = function buildWrapperCSSClass() {
20262      return "vjs-playback-rate " + _MenuButton.prototype.buildWrapperCSSClass.call(this);
20263    }
20264    /**
20265     * Create the playback rate menu
20266     *
20267     * @return {Menu}
20268     *         Menu object populated with {@link PlaybackRateMenuItem}s
20269     */
20270    ;
20271
20272    _proto.createMenu = function createMenu() {
20273      var menu = new Menu(this.player());
20274      var rates = this.playbackRates();
20275
20276      if (rates) {
20277        for (var i = rates.length - 1; i >= 0; i--) {
20278          menu.addChild(new PlaybackRateMenuItem(this.player(), {
20279            rate: rates[i] + 'x'
20280          }));
20281        }
20282      }
20283
20284      return menu;
20285    }
20286    /**
20287     * Updates ARIA accessibility attributes
20288     */
20289    ;
20290
20291    _proto.updateARIAAttributes = function updateARIAAttributes() {
20292      // Current playback rate
20293      this.el().setAttribute('aria-valuenow', this.player().playbackRate());
20294    }
20295    /**
20296     * This gets called when an `PlaybackRateMenuButton` is "clicked". See
20297     * {@link ClickableComponent} for more detailed information on what a click can be.
20298     *
20299     * @param {EventTarget~Event} [event]
vendor: 32,734 bytes, lines 20300-21353
20300     *        The `keydown`, `tap`, or `click` event that caused this function to be
20301     *        called.
20302     *
20303     * @listens tap
20304     * @listens click
20305     */
20306    ;
20307
20308    _proto.handleClick = function handleClick(event) {
20309      // select next rate option
20310      var currentRate = this.player().playbackRate();
20311      var rates = this.playbackRates(); // this will select first one if the last one currently selected
20312
20313      var newRate = rates[0];
20314
20315      for (var i = 0; i < rates.length; i++) {
20316        if (rates[i] > currentRate) {
20317          newRate = rates[i];
20318          break;
20319        }
20320      }
20321
20322      this.player().playbackRate(newRate);
20323    }
20324    /**
20325     * Get possible playback rates
20326     *
20327     * @return {Array}
20328     *         All possible playback rates
20329     */
20330    ;
20331
20332    _proto.playbackRates = function playbackRates() {
20333      return this.options_.playbackRates || this.options_.playerOptions && this.options_.playerOptions.playbackRates;
20334    }
20335    /**
20336     * Get whether playback rates is supported by the tech
20337     * and an array of playback rates exists
20338     *
20339     * @return {boolean}
20340     *         Whether changing playback rate is supported
20341     */
20342    ;
20343
20344    _proto.playbackRateSupported = function playbackRateSupported() {
20345      return this.player().tech_ && this.player().tech_.featuresPlaybackRate && this.playbackRates() && this.playbackRates().length > 0;
20346    }
20347    /**
20348     * Hide playback rate controls when they're no playback rate options to select
20349     *
20350     * @param {EventTarget~Event} [event]
20351     *        The event that caused this function to run.
20352     *
20353     * @listens Player#loadstart
20354     */
20355    ;
20356
20357    _proto.updateVisibility = function updateVisibility(event) {
20358      if (this.playbackRateSupported()) {
20359        this.removeClass('vjs-hidden');
20360      } else {
20361        this.addClass('vjs-hidden');
20362      }
20363    }
20364    /**
20365     * Update button label when rate changed
20366     *
20367     * @param {EventTarget~Event} [event]
20368     *        The event that caused this function to run.
20369     *
20370     * @listens Player#ratechange
20371     */
20372    ;
20373
20374    _proto.updateLabel = function updateLabel(event) {
20375      if (this.playbackRateSupported()) {
20376        this.labelEl_.innerHTML = this.player().playbackRate() + 'x';
20377      }
20378    };
20379
20380    return PlaybackRateMenuButton;
20381  }(MenuButton);
20382  /**
20383   * The text that should display over the `FullscreenToggle`s controls. Added for localization.
20384   *
20385   * @type {string}
20386   * @private
20387   */
20388
20389
20390  PlaybackRateMenuButton.prototype.controlText_ = 'Playback Rate';
20391  Component.registerComponent('PlaybackRateMenuButton', PlaybackRateMenuButton);
20392
20393  /**
20394   * Just an empty spacer element that can be used as an append point for plugins, etc.
20395   * Also can be used to create space between elements when necessary.
20396   *
20397   * @extends Component
20398   */
20399
20400  var Spacer =
20401  /*#__PURE__*/
20402  function (_Component) {
20403    _inheritsLoose(Spacer, _Component);
20404
20405    function Spacer() {
20406      return _Component.apply(this, arguments) || this;
20407    }
20408
20409    var _proto = Spacer.prototype;
20410
20411    /**
20412    * Builds the default DOM `className`.
20413    *
20414    * @return {string}
20415    *         The DOM `className` for this object.
20416    */
20417    _proto.buildCSSClass = function buildCSSClass() {
20418      return "vjs-spacer " + _Component.prototype.buildCSSClass.call(this);
20419    }
20420    /**
20421     * Create the `Component`'s DOM element
20422     *
20423     * @return {Element}
20424     *         The element that was created.
20425     */
20426    ;
20427
20428    _proto.createEl = function createEl() {
20429      return _Component.prototype.createEl.call(this, 'div', {
20430        className: this.buildCSSClass()
20431      });
20432    };
20433
20434    return Spacer;
20435  }(Component);
20436
20437  Component.registerComponent('Spacer', Spacer);
20438
20439  /**
20440   * Spacer specifically meant to be used as an insertion point for new plugins, etc.
20441   *
20442   * @extends Spacer
20443   */
20444
20445  var CustomControlSpacer =
20446  /*#__PURE__*/
20447  function (_Spacer) {
20448    _inheritsLoose(CustomControlSpacer, _Spacer);
20449
20450    function CustomControlSpacer() {
20451      return _Spacer.apply(this, arguments) || this;
20452    }
20453
20454    var _proto = CustomControlSpacer.prototype;
20455
20456    /**
20457     * Builds the default DOM `className`.
20458     *
20459     * @return {string}
20460     *         The DOM `className` for this object.
20461     */
20462    _proto.buildCSSClass = function buildCSSClass() {
20463      return "vjs-custom-control-spacer " + _Spacer.prototype.buildCSSClass.call(this);
20464    }
20465    /**
20466     * Create the `Component`'s DOM element
20467     *
20468     * @return {Element}
20469     *         The element that was created.
20470     */
20471    ;
20472
20473    _proto.createEl = function createEl() {
20474      var el = _Spacer.prototype.createEl.call(this, {
20475        className: this.buildCSSClass()
20476      }); // No-flex/table-cell mode requires there be some content
20477      // in the cell to fill the remaining space of the table.
20478
20479
20480      el.innerHTML = "\xA0";
20481      return el;
20482    };
20483
20484    return CustomControlSpacer;
20485  }(Spacer);
20486
20487  Component.registerComponent('CustomControlSpacer', CustomControlSpacer);
20488
20489  /**
20490   * Container of main controls.
20491   *
20492   * @extends Component
20493   */
20494
20495  var ControlBar =
20496  /*#__PURE__*/
20497  function (_Component) {
20498    _inheritsLoose(ControlBar, _Component);
20499
20500    function ControlBar() {
20501      return _Component.apply(this, arguments) || this;
20502    }
20503
20504    var _proto = ControlBar.prototype;
20505
20506    /**
20507     * Create the `Component`'s DOM element
20508     *
20509     * @return {Element}
20510     *         The element that was created.
20511     */
20512    _proto.createEl = function createEl() {
20513      return _Component.prototype.createEl.call(this, 'div', {
20514        className: 'vjs-control-bar',
20515        dir: 'ltr'
20516      });
20517    };
20518
20519    return ControlBar;
20520  }(Component);
20521  /**
20522   * Default options for `ControlBar`
20523   *
20524   * @type {Object}
20525   * @private
20526   */
20527
20528
20529  ControlBar.prototype.options_ = {
20530    children: ['playToggle', 'volumePanel', 'currentTimeDisplay', 'timeDivider', 'durationDisplay', 'progressControl', 'liveDisplay', 'seekToLive', 'remainingTimeDisplay', 'customControlSpacer', 'playbackRateMenuButton', 'chaptersButton', 'descriptionsButton', 'subsCapsButton', 'audioTrackButton', 'fullscreenToggle']
20531  };
20532
20533  if ('exitPictureInPicture' in document) {
20534    ControlBar.prototype.options_.children.splice(ControlBar.prototype.options_.children.length - 1, 0, 'pictureInPictureToggle');
20535  }
20536
20537  Component.registerComponent('ControlBar', ControlBar);
20538
20539  /**
20540   * A display that indicates an error has occurred. This means that the video
20541   * is unplayable.
20542   *
20543   * @extends ModalDialog
20544   */
20545
20546  var ErrorDisplay =
20547  /*#__PURE__*/
20548  function (_ModalDialog) {
20549    _inheritsLoose(ErrorDisplay, _ModalDialog);
20550
20551    /**
20552     * Creates an instance of this class.
20553     *
20554     * @param  {Player} player
20555     *         The `Player` that this class should be attached to.
20556     *
20557     * @param  {Object} [options]
20558     *         The key/value store of player options.
20559     */
20560    function ErrorDisplay(player, options) {
20561      var _this;
20562
20563      _this = _ModalDialog.call(this, player, options) || this;
20564
20565      _this.on(player, 'error', _this.open);
20566
20567      return _this;
20568    }
20569    /**
20570     * Builds the default DOM `className`.
20571     *
20572     * @return {string}
20573     *         The DOM `className` for this object.
20574     *
20575     * @deprecated Since version 5.
20576     */
20577
20578
20579    var _proto = ErrorDisplay.prototype;
20580
20581    _proto.buildCSSClass = function buildCSSClass() {
20582      return "vjs-error-display " + _ModalDialog.prototype.buildCSSClass.call(this);
20583    }
20584    /**
20585     * Gets the localized error message based on the `Player`s error.
20586     *
20587     * @return {string}
20588     *         The `Player`s error message localized or an empty string.
20589     */
20590    ;
20591
20592    _proto.content = function content() {
20593      var error = this.player().error();
20594      return error ? this.localize(error.message) : '';
20595    };
20596
20597    return ErrorDisplay;
20598  }(ModalDialog);
20599  /**
20600   * The default options for an `ErrorDisplay`.
20601   *
20602   * @private
20603   */
20604
20605
20606  ErrorDisplay.prototype.options_ = mergeOptions(ModalDialog.prototype.options_, {
20607    pauseOnOpen: false,
20608    fillAlways: true,
20609    temporary: false,
20610    uncloseable: true
20611  });
20612  Component.registerComponent('ErrorDisplay', ErrorDisplay);
20613
20614  var LOCAL_STORAGE_KEY = 'vjs-text-track-settings';
20615  var COLOR_BLACK = ['#000', 'Black'];
20616  var COLOR_BLUE = ['#00F', 'Blue'];
20617  var COLOR_CYAN = ['#0FF', 'Cyan'];
20618  var COLOR_GREEN = ['#0F0', 'Green'];
20619  var COLOR_MAGENTA = ['#F0F', 'Magenta'];
20620  var COLOR_RED = ['#F00', 'Red'];
20621  var COLOR_WHITE = ['#FFF', 'White'];
20622  var COLOR_YELLOW = ['#FF0', 'Yellow'];
20623  var OPACITY_OPAQUE = ['1', 'Opaque'];
20624  var OPACITY_SEMI = ['0.5', 'Semi-Transparent'];
20625  var OPACITY_TRANS = ['0', 'Transparent']; // Configuration for the various <select> elements in the DOM of this component.
20626  //
20627  // Possible keys include:
20628  //
20629  // `default`:
20630  //   The default option index. Only needs to be provided if not zero.
20631  // `parser`:
20632  //   A function which is used to parse the value from the selected option in
20633  //   a customized way.
20634  // `selector`:
20635  //   The selector used to find the associated <select> element.
20636
20637  var selectConfigs = {
20638    backgroundColor: {
20639      selector: '.vjs-bg-color > select',
20640      id: 'captions-background-color-%s',
20641      label: 'Color',
20642      options: [COLOR_BLACK, COLOR_WHITE, COLOR_RED, COLOR_GREEN, COLOR_BLUE, COLOR_YELLOW, COLOR_MAGENTA, COLOR_CYAN]
20643    },
20644    backgroundOpacity: {
20645      selector: '.vjs-bg-opacity > select',
20646      id: 'captions-background-opacity-%s',
20647      label: 'Transparency',
20648      options: [OPACITY_OPAQUE, OPACITY_SEMI, OPACITY_TRANS]
20649    },
20650    color: {
20651      selector: '.vjs-fg-color > select',
20652      id: 'captions-foreground-color-%s',
20653      label: 'Color',
20654      options: [COLOR_WHITE, COLOR_BLACK, COLOR_RED, COLOR_GREEN, COLOR_BLUE, COLOR_YELLOW, COLOR_MAGENTA, COLOR_CYAN]
20655    },
20656    edgeStyle: {
20657      selector: '.vjs-edge-style > select',
20658      id: '%s',
20659      label: 'Text Edge Style',
20660      options: [['none', 'None'], ['raised', 'Raised'], ['depressed', 'Depressed'], ['uniform', 'Uniform'], ['dropshadow', 'Dropshadow']]
20661    },
20662    fontFamily: {
20663      selector: '.vjs-font-family > select',
20664      id: 'captions-font-family-%s',
20665      label: 'Font Family',
20666      options: [['proportionalSansSerif', 'Proportional Sans-Serif'], ['monospaceSansSerif', 'Monospace Sans-Serif'], ['proportionalSerif', 'Proportional Serif'], ['monospaceSerif', 'Monospace Serif'], ['casual', 'Casual'], ['script', 'Script'], ['small-caps', 'Small Caps']]
20667    },
20668    fontPercent: {
20669      selector: '.vjs-font-percent > select',
20670      id: 'captions-font-size-%s',
20671      label: 'Font Size',
20672      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%']],
20673      "default": 2,
20674      parser: function parser(v) {
20675        return v === '1.00' ? null : Number(v);
20676      }
20677    },
20678    textOpacity: {
20679      selector: '.vjs-text-opacity > select',
20680      id: 'captions-foreground-opacity-%s',
20681      label: 'Transparency',
20682      options: [OPACITY_OPAQUE, OPACITY_SEMI]
20683    },
20684    // Options for this object are defined below.
20685    windowColor: {
20686      selector: '.vjs-window-color > select',
20687      id: 'captions-window-color-%s',
20688      label: 'Color'
20689    },
20690    // Options for this object are defined below.
20691    windowOpacity: {
20692      selector: '.vjs-window-opacity > select',
20693      id: 'captions-window-opacity-%s',
20694      label: 'Transparency',
20695      options: [OPACITY_TRANS, OPACITY_SEMI, OPACITY_OPAQUE]
20696    }
20697  };
20698  selectConfigs.windowColor.options = selectConfigs.backgroundColor.options;
20699  /**
20700   * Get the actual value of an option.
20701   *
20702   * @param  {string} value
20703   *         The value to get
20704   *
20705   * @param  {Function} [parser]
20706   *         Optional function to adjust the value.
20707   *
20708   * @return {Mixed}
20709   *         - Will be `undefined` if no value exists
20710   *         - Will be `undefined` if the given value is "none".
20711   *         - Will be the actual value otherwise.
20712   *
20713   * @private
20714   */
20715
20716  function parseOptionValue(value, parser) {
20717    if (parser) {
20718      value = parser(value);
20719    }
20720
20721    if (value && value !== 'none') {
20722      return value;
20723    }
20724  }
20725  /**
20726   * Gets the value of the selected <option> element within a <select> element.
20727   *
20728   * @param  {Element} el
20729   *         the element to look in
20730   *
20731   * @param  {Function} [parser]
20732   *         Optional function to adjust the value.
20733   *
20734   * @return {Mixed}
20735   *         - Will be `undefined` if no value exists
20736   *         - Will be `undefined` if the given value is "none".
20737   *         - Will be the actual value otherwise.
20738   *
20739   * @private
20740   */
20741
20742
20743  function getSelectedOptionValue(el, parser) {
20744    var value = el.options[el.options.selectedIndex].value;
20745    return parseOptionValue(value, parser);
20746  }
20747  /**
20748   * Sets the selected <option> element within a <select> element based on a
20749   * given value.
20750   *
20751   * @param {Element} el
20752   *        The element to look in.
20753   *
20754   * @param {string} value
20755   *        the property to look on.
20756   *
20757   * @param {Function} [parser]
20758   *        Optional function to adjust the value before comparing.
20759   *
20760   * @private
20761   */
20762
20763
20764  function setSelectedOption(el, value, parser) {
20765    if (!value) {
20766      return;
20767    }
20768
20769    for (var i = 0; i < el.options.length; i++) {
20770      if (parseOptionValue(el.options[i].value, parser) === value) {
20771        el.selectedIndex = i;
20772        break;
20773      }
20774    }
20775  }
20776  /**
20777   * Manipulate Text Tracks settings.
20778   *
20779   * @extends ModalDialog
20780   */
20781
20782
20783  var TextTrackSettings =
20784  /*#__PURE__*/
20785  function (_ModalDialog) {
20786    _inheritsLoose(TextTrackSettings, _ModalDialog);
20787
20788    /**
20789     * Creates an instance of this class.
20790     *
20791     * @param {Player} player
20792     *         The `Player` that this class should be attached to.
20793     *
20794     * @param {Object} [options]
20795     *         The key/value store of player options.
20796     */
20797    function TextTrackSettings(player, options) {
20798      var _this;
20799
20800      options.temporary = false;
20801      _this = _ModalDialog.call(this, player, options) || this;
20802      _this.updateDisplay = bind(_assertThisInitialized(_this), _this.updateDisplay); // fill the modal and pretend we have opened it
20803
20804      _this.fill();
20805
20806      _this.hasBeenOpened_ = _this.hasBeenFilled_ = true;
20807      _this.endDialog = createEl('p', {
20808        className: 'vjs-control-text',
20809        textContent: _this.localize('End of dialog window.')
20810      });
20811
20812      _this.el().appendChild(_this.endDialog);
20813
20814      _this.setDefaults(); // Grab `persistTextTrackSettings` from the player options if not passed in child options
20815
20816
20817      if (options.persistTextTrackSettings === undefined) {
20818        _this.options_.persistTextTrackSettings = _this.options_.playerOptions.persistTextTrackSettings;
20819      }
20820
20821      _this.on(_this.$('.vjs-done-button'), 'click', function () {
20822        _this.saveSettings();
20823
20824        _this.close();
20825      });
20826
20827      _this.on(_this.$('.vjs-default-button'), 'click', function () {
20828        _this.setDefaults();
20829
20830        _this.updateDisplay();
20831      });
20832
20833      each(selectConfigs, function (config) {
20834        _this.on(_this.$(config.selector), 'change', _this.updateDisplay);
20835      });
20836
20837      if (_this.options_.persistTextTrackSettings) {
20838        _this.restoreSettings();
20839      }
20840
20841      return _this;
20842    }
20843
20844    var _proto = TextTrackSettings.prototype;
20845
20846    _proto.dispose = function dispose() {
20847      this.endDialog = null;
20848
20849      _ModalDialog.prototype.dispose.call(this);
20850    }
20851    /**
20852     * Create a <select> element with configured options.
20853     *
20854     * @param {string} key
20855     *        Configuration key to use during creation.
20856     *
20857     * @return {string}
20858     *         An HTML string.
20859     *
20860     * @private
20861     */
20862    ;
20863
20864    _proto.createElSelect_ = function createElSelect_(key, legendId, type) {
20865      var _this2 = this;
20866
20867      if (legendId === void 0) {
20868        legendId = '';
20869      }
20870
20871      if (type === void 0) {
20872        type = 'label';
20873      }
20874
20875      var config = selectConfigs[key];
20876      var id = config.id.replace('%s', this.id_);
20877      var selectLabelledbyIds = [legendId, id].join(' ').trim();
20878      return ["<" + type + " id=\"" + id + "\" class=\"" + (type === 'label' ? 'vjs-label' : '') + "\">", this.localize(config.label), "</" + type + ">", "<select aria-labelledby=\"" + selectLabelledbyIds + "\">"].concat(config.options.map(function (o) {
20879        var optionId = id + '-' + o[1].replace(/\W+/g, '');
20880        return ["<option id=\"" + optionId + "\" value=\"" + o[0] + "\" ", "aria-labelledby=\"" + selectLabelledbyIds + " " + optionId + "\">", _this2.localize(o[1]), '</option>'].join('');
20881      })).concat('</select>').join('');
20882    }
20883    /**
20884     * Create foreground color element for the component
20885     *
20886     * @return {string}
20887     *         An HTML string.
20888     *
20889     * @private
20890     */
20891    ;
20892
20893    _proto.createElFgColor_ = function createElFgColor_() {
20894      var legendId = "captions-text-legend-" + this.id_;
20895      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('');
20896    }
20897    /**
20898     * Create background color element for the component
20899     *
20900     * @return {string}
20901     *         An HTML string.
20902     *
20903     * @private
20904     */
20905    ;
20906
20907    _proto.createElBgColor_ = function createElBgColor_() {
20908      var legendId = "captions-background-" + this.id_;
20909      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('');
20910    }
20911    /**
20912     * Create window color element for the component
20913     *
20914     * @return {string}
20915     *         An HTML string.
20916     *
20917     * @private
20918     */
20919    ;
20920
20921    _proto.createElWinColor_ = function createElWinColor_() {
20922      var legendId = "captions-window-" + this.id_;
20923      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('');
20924    }
20925    /**
20926     * Create color elements for the component
20927     *
20928     * @return {Element}
20929     *         The element that was created
20930     *
20931     * @private
20932     */
20933    ;
20934
20935    _proto.createElColors_ = function createElColors_() {
20936      return createEl('div', {
20937        className: 'vjs-track-settings-colors',
20938        innerHTML: [this.createElFgColor_(), this.createElBgColor_(), this.createElWinColor_()].join('')
20939      });
20940    }
20941    /**
20942     * Create font elements for the component
20943     *
20944     * @return {Element}
20945     *         The element that was created.
20946     *
20947     * @private
20948     */
20949    ;
20950
20951    _proto.createElFont_ = function createElFont_() {
20952      return createEl('div', {
20953        className: 'vjs-track-settings-font',
20954        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('')
20955      });
20956    }
20957    /**
20958     * Create controls for the component
20959     *
20960     * @return {Element}
20961     *         The element that was created.
20962     *
20963     * @private
20964     */
20965    ;
20966
20967    _proto.createElControls_ = function createElControls_() {
20968      var defaultsDescription = this.localize('restore all settings to the default values');
20969      return createEl('div', {
20970        className: 'vjs-track-settings-controls',
20971        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('')
20972      });
20973    };
20974
20975    _proto.content = function content() {
20976      return [this.createElColors_(), this.createElFont_(), this.createElControls_()];
20977    };
20978
20979    _proto.label = function label() {
20980      return this.localize('Caption Settings Dialog');
20981    };
20982
20983    _proto.description = function description() {
20984      return this.localize('Beginning of dialog window. Escape will cancel and close the window.');
20985    };
20986
20987    _proto.buildCSSClass = function buildCSSClass() {
20988      return _ModalDialog.prototype.buildCSSClass.call(this) + ' vjs-text-track-settings';
20989    }
20990    /**
20991     * Gets an object of text track settings (or null).
20992     *
20993     * @return {Object}
20994     *         An object with config values parsed from the DOM or localStorage.
20995     */
20996    ;
20997
20998    _proto.getValues = function getValues() {
20999      var _this3 = this;
21000
21001      return reduce(selectConfigs, function (accum, config, key) {
21002        var value = getSelectedOptionValue(_this3.$(config.selector), config.parser);
21003
21004        if (value !== undefined) {
21005          accum[key] = value;
21006        }
21007
21008        return accum;
21009      }, {});
21010    }
21011    /**
21012     * Sets text track settings from an object of values.
21013     *
21014     * @param {Object} values
21015     *        An object with config values parsed from the DOM or localStorage.
21016     */
21017    ;
21018
21019    _proto.setValues = function setValues(values) {
21020      var _this4 = this;
21021
21022      each(selectConfigs, function (config, key) {
21023        setSelectedOption(_this4.$(config.selector), values[key], config.parser);
21024      });
21025    }
21026    /**
21027     * Sets all `<select>` elements to their default values.
21028     */
21029    ;
21030
21031    _proto.setDefaults = function setDefaults() {
21032      var _this5 = this;
21033
21034      each(selectConfigs, function (config) {
21035        var index = config.hasOwnProperty('default') ? config["default"] : 0;
21036        _this5.$(config.selector).selectedIndex = index;
21037      });
21038    }
21039    /**
21040     * Restore texttrack settings from localStorage
21041     */
21042    ;
21043
21044    _proto.restoreSettings = function restoreSettings() {
21045      var values;
21046
21047      try {
21048        values = JSON.parse(window$1.localStorage.getItem(LOCAL_STORAGE_KEY));
21049      } catch (err) {
21050        log.warn(err);
21051      }
21052
21053      if (values) {
21054        this.setValues(values);
21055      }
21056    }
21057    /**
21058     * Save text track settings to localStorage
21059     */
21060    ;
21061
21062    _proto.saveSettings = function saveSettings() {
21063      if (!this.options_.persistTextTrackSettings) {
21064        return;
21065      }
21066
21067      var values = this.getValues();
21068
21069      try {
21070        if (Object.keys(values).length) {
21071          window$1.localStorage.setItem(LOCAL_STORAGE_KEY, JSON.stringify(values));
21072        } else {
21073          window$1.localStorage.removeItem(LOCAL_STORAGE_KEY);
21074        }
21075      } catch (err) {
21076        log.warn(err);
21077      }
21078    }
21079    /**
21080     * Update display of text track settings
21081     */
21082    ;
21083
21084    _proto.updateDisplay = function updateDisplay() {
21085      var ttDisplay = this.player_.getChild('textTrackDisplay');
21086
21087      if (ttDisplay) {
21088        ttDisplay.updateDisplay();
21089      }
21090    }
21091    /**
21092     * conditionally blur the element and refocus the captions button
21093     *
21094     * @private
21095     */
21096    ;
21097
21098    _proto.conditionalBlur_ = function conditionalBlur_() {
21099      this.previouslyActiveEl_ = null;
21100      var cb = this.player_.controlBar;
21101      var subsCapsBtn = cb && cb.subsCapsButton;
21102      var ccBtn = cb && cb.captionsButton;
21103
21104      if (subsCapsBtn) {
21105        subsCapsBtn.focus();
21106      } else if (ccBtn) {
21107        ccBtn.focus();
21108      }
21109    };
21110
21111    return TextTrackSettings;
21112  }(ModalDialog);
21113
21114  Component.registerComponent('TextTrackSettings', TextTrackSettings);
21115
21116  /**
21117   * A Resize Manager. It is in charge of triggering `playerresize` on the player in the right conditions.
21118   *
21119   * 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}.
21120   *
21121   * If the ResizeObserver is available natively, it will be used. A polyfill can be passed in as an option.
21122   * If a `playerresize` event is not needed, the ResizeManager component can be removed from the player, see the example below.
21123   * @example <caption>How to disable the resize manager</caption>
21124   * const player = videojs('#vid', {
21125   *   resizeManager: false
21126   * });
21127   *
21128   * @see {@link https://wicg.github.io/ResizeObserver/|ResizeObserver specification}
21129   *
21130   * @extends Component
21131   */
21132
21133  var ResizeManager =
21134  /*#__PURE__*/
21135  function (_Component) {
21136    _inheritsLoose(ResizeManager, _Component);
21137
21138    /**
21139     * Create the ResizeManager.
21140     *
21141     * @param {Object} player
21142     *        The `Player` that this class should be attached to.
21143     *
21144     * @param {Object} [options]
21145     *        The key/value store of ResizeManager options.
21146     *
21147     * @param {Object} [options.ResizeObserver]
21148     *        A polyfill for ResizeObserver can be passed in here.
21149     *        If this is set to null it will ignore the native ResizeObserver and fall back to the iframe fallback.
21150     */
21151    function ResizeManager(player, options) {
21152      var _this;
21153
21154      var RESIZE_OBSERVER_AVAILABLE = options.ResizeObserver || window$1.ResizeObserver; // if `null` was passed, we want to disable the ResizeObserver
21155
21156      if (options.ResizeObserver === null) {
21157        RESIZE_OBSERVER_AVAILABLE = false;
21158      } // Only create an element when ResizeObserver isn't available
21159
21160
21161      var options_ = mergeOptions({
21162        createEl: !RESIZE_OBSERVER_AVAILABLE,
21163        reportTouchActivity: false
21164      }, options);
21165      _this = _Component.call(this, player, options_) || this;
21166      _this.ResizeObserver = options.ResizeObserver || window$1.ResizeObserver;
21167      _this.loadListener_ = null;
21168      _this.resizeObserver_ = null;
21169      _this.debouncedHandler_ = debounce(function () {
21170        _this.resizeHandler();
21171      }, 100, false, _assertThisInitialized(_this));
21172
21173      if (RESIZE_OBSERVER_AVAILABLE) {
21174        _this.resizeObserver_ = new _this.ResizeObserver(_this.debouncedHandler_);
21175
21176        _this.resizeObserver_.observe(player.el());
21177      } else {
21178        _this.loadListener_ = function () {
21179          if (!_this.el_ || !_this.el_.contentWindow) {
21180            return;
21181          }
21182
21183          var debouncedHandler_ = _this.debouncedHandler_;
21184
21185          var unloadListener_ = _this.unloadListener_ = function () {
21186            off(this, 'resize', debouncedHandler_);
21187            off(this, 'unload', unloadListener_);
21188            unloadListener_ = null;
21189          }; // safari and edge can unload the iframe before resizemanager dispose
21190          // we have to dispose of event handlers correctly before that happens
21191
21192
21193          on(_this.el_.contentWindow, 'unload', unloadListener_);
21194          on(_this.el_.contentWindow, 'resize', debouncedHandler_);
21195        };
21196
21197        _this.one('load', _this.loadListener_);
21198      }
21199
21200      return _this;
21201    }
21202
21203    var _proto = ResizeManager.prototype;
21204
21205    _proto.createEl = function createEl() {
21206      return _Component.prototype.createEl.call(this, 'iframe', {
21207        className: 'vjs-resize-manager',
21208        tabIndex: -1
21209      }, {
21210        'aria-hidden': 'true'
21211      });
21212    }
21213    /**
21214     * Called when a resize is triggered on the iframe or a resize is observed via the ResizeObserver
21215     *
21216     * @fires Player#playerresize
21217     */
21218    ;
21219
21220    _proto.resizeHandler = function resizeHandler() {
21221      /**
21222       * Called when the player size has changed
21223       *
21224       * @event Player#playerresize
21225       * @type {EventTarget~Event}
21226       */
21227      // make sure player is still around to trigger
21228      // prevents this from causing an error after dispose
21229      if (!this.player_ || !this.player_.trigger) {
21230        return;
21231      }
21232
21233      this.player_.trigger('playerresize');
21234    };
21235
21236    _proto.dispose = function dispose() {
21237      if (this.debouncedHandler_) {
21238        this.debouncedHandler_.cancel();
21239      }
21240
21241      if (this.resizeObserver_) {
21242        if (this.player_.el()) {
21243          this.resizeObserver_.unobserve(this.player_.el());
21244        }
21245
21246        this.resizeObserver_.disconnect();
21247      }
21248
21249      if (this.loadListener_) {
21250        this.off('load', this.loadListener_);
21251      }
21252
21253      if (this.el_ && this.el_.contentWindow && this.unloadListener_) {
21254        this.unloadListener_.call(this.el_.contentWindow);
21255      }
21256
21257      this.ResizeObserver = null;
21258      this.resizeObserver = null;
21259      this.debouncedHandler_ = null;
21260      this.loadListener_ = null;
21261
21262      _Component.prototype.dispose.call(this);
21263    };
21264
21265    return ResizeManager;
21266  }(Component);
21267
21268  Component.registerComponent('ResizeManager', ResizeManager);
21269
21270  /**
21271   * Computes the median of an array.
21272   *
21273   * @param {number[]} arr
21274   *        Input array of numbers.
21275   *
21276   * @return {number}
21277   *        Median value.
21278   */
21279  var median = function median(arr) {
21280    var mid = Math.floor(arr.length / 2);
21281    var sortedList = [].concat(arr).sort(function (a, b) {
21282      return a - b;
21283    });
21284    return arr.length % 2 !== 0 ? sortedList[mid] : (sortedList[mid - 1] + sortedList[mid]) / 2;
21285  };
21286
21287  /* track when we are at the live edge, and other helpers for live playback */
21288
21289  var LiveTracker =
21290  /*#__PURE__*/
21291  function (_Component) {
21292    _inheritsLoose(LiveTracker, _Component);
21293
21294    function LiveTracker(player, options) {
21295      var _this;
21296
21297      // LiveTracker does not need an element
21298      var options_ = mergeOptions({
21299        createEl: false
21300      }, options);
21301      _this = _Component.call(this, player, options_) || this;
21302
21303      _this.reset_();
21304
21305      _this.on(_this.player_, 'durationchange', _this.handleDurationchange); // we don't need to track live playback if the document is hidden,
21306      // also, tracking when the document is hidden can
21307      // cause the CPU to spike and eventually crash the page on IE11.
21308
21309
21310      if (IE_VERSION && 'hidden' in document && 'visibilityState' in document) {
21311        _this.on(document, 'visibilitychange', _this.handleVisibilityChange);
21312      }
21313
21314      return _this;
21315    }
21316
21317    var _proto = LiveTracker.prototype;
21318
21319    _proto.handleVisibilityChange = function handleVisibilityChange() {
21320      if (this.player_.duration() !== Infinity) {
21321        return;
21322      }
21323
21324      if (document.hidden) {
21325        this.stopTracking();
21326      } else {
21327        this.startTracking();
21328      }
21329    };
21330
21331    _proto.isBehind_ = function isBehind_() {
21332      // don't report that we are behind until a timeupdate has been seen
21333      if (!this.timeupdateSeen_) {
21334        return false;
21335      }
21336
21337      var liveCurrentTime = this.liveCurrentTime();
21338      var currentTime = this.player_.currentTime(); // the live edge window is the amount of seconds away from live
21339      // that a player can be, but still be considered live.
21340      // we add 0.07 because the live tracking happens every 30ms
21341      // and we want some wiggle room for short segment live playback
21342
21343      var liveEdgeWindow = this.seekableIncrement_ * 2 + 0.07; // on Android liveCurrentTime can bee Infinity, because seekableEnd
21344      // can be Infinity, so we handle that case.
21345
21346      return liveCurrentTime !== Infinity && liveCurrentTime - liveEdgeWindow >= currentTime;
21347    } // all the functionality for tracking when seek end changes
21348    // and for tracking how far past seek end we should be
21349    ;
21350
21351    _proto.trackLive_ = function trackLive_() {
21352      this.pastSeekEnd_ = this.pastSeekEnd_;
21353      var seekable = this.player_.seekable();
21353 // skip undefined seekable
21354
21355      if (!seekable || !seekable.length) {
21356        return;
21357      }
21358
21359      var newSeekEnd = this.seekableEnd(); // we can only tell if we are behind live, when seekable changes
21360      // once we detect that seekable has changed we check the new seek
21361      // end against current time, with a fudge value of half a second.
21362
21363      if (newSeekEnd !== this.lastSeekEnd_) {
21364        if (this.lastSeekEnd_) {
21365          // we try to get the best fit value for the seeking increment
21366          // variable from the last 12 values.
21367          this.seekableIncrementList_ = this.seekableIncrementList_.slice(-11);
21368          this.seekableIncrementList_.push(Math.abs(newSeekEnd - this.lastSeekEnd_));
21369
21370          if (this.seekableIncrementList_.length > 3) {
21371            this.seekableIncrement_ = median(this.seekableIncrementList_);
21372          }
21373        }
21374
21375        this.pastSeekEnd_ = 0;
21376        this.lastSeekEnd_ = newSeekEnd;
21377        this.trigger('seekableendchange');
21378      }
21379
21380      this.pastSeekEnd_ = this.pastSeekEnd() + 0.03;
21381
21382      if (this.isBehind_() !== this.behindLiveEdge()) {
21383        this.behindLiveEdge_ = this.isBehind_();
21384        this.trigger('liveedgechange');
21385      }
21386    }
21387    /**
21388     * handle a durationchange event on the player
21389     * and start/stop tracking accordingly.
21390     */
21391    ;
21392
21393    _proto.handleDurationchange = function handleDurationchange() {
21394      if (this.player_.duration() === Infinity) {
21395        this.startTracking();
21396      } else {
21397        this.stopTracking();
21398      }
21399    }
21400    /**
21401     * start tracking live playback
21402     */
21403    ;
21404
21405    _proto.startTracking = function startTracking() {
21406      var _this2 = this;
21407
21408      if (this.isTracking()) {
21409        return;
21410      } // If we haven't seen a timeupdate, we need to check whether playback
21411      // began before this component started tracking. This can happen commonly
21412      // when using autoplay.
21413
21414
21415      if (!this.timeupdateSeen_) {
21416        this.timeupdateSeen_ = this.player_.hasStarted();
21417      }
21418
21419      this.trackingInterval_ = this.setInterval(this.trackLive_, 30);
21420      this.trackLive_();
21421      this.on(this.player_, 'play', this.trackLive_);
21422      this.on(this.player_, 'pause', this.trackLive_); // this is to prevent showing that we are not live
21423      // before a video starts to play
21424
21425      if (!this.timeupdateSeen_) {
21426        this.one(this.player_, 'play', this.handlePlay);
21427
21428        this.handleTimeupdate = function () {
21429          _this2.timeupdateSeen_ = true;
21430          _this2.handleTimeupdate = null;
21431        };
21432
21433        this.one(this.player_, 'timeupdate', this.handleTimeupdate);
21434      }
21435    };
21436
21437    _proto.handlePlay = function handlePlay() {
21438      this.one(this.player_, 'timeupdate', this.seekToLiveEdge);
21439    }
21440    /**
21441     * Stop tracking, and set all internal variables to
21442     * their initial value.
21443     */
21444    ;
21445
21446    _proto.reset_ = function reset_() {
21447      this.pastSeekEnd_ = 0;
21448      this.lastSeekEnd_ = null;
21449      this.behindLiveEdge_ = null;
21450      this.timeupdateSeen_ = false;
21451      this.clearInterval(this.trackingInterval_);
21452      this.trackingInterval_ = null;
21453      this.seekableIncrement_ = 12;
21454      this.seekableIncrementList_ = [];
21455      this.off(this.player_, 'play', this.trackLive_);
21456      this.off(this.player_, 'pause', this.trackLive_);
21457      this.off(this.player_, 'play', this.handlePlay);
21458      this.off(this.player_, 'timeupdate', this.seekToLiveEdge);
21459
21460      if (this.handleTimeupdate) {
21461        this.off(this.player_, 'timeupdate', this.handleTimeupdate);
21462        this.handleTimeupdate = null;
21463      }
21464    }
21465    /**
21466     * stop tracking live playback
21467     */
21468    ;
21469
21470    _proto.stopTracking = function stopTracking() {
21471      if (!this.isTracking()) {
21472        return;
21473      }
21474
21475      this.reset_();
21476    }
21477    /**
21478     * A helper to get the player seekable end
21479     * so that we don't have to null check everywhere
21480     */
21481    ;
21482
21483    _proto.seekableEnd = function seekableEnd() {
21484      var seekable = this.player_.seekable();
21485      var seekableEnds = [];
21486      var i = seekable ? seekable.length : 0;
21487
21488      while (i--) {
21489        seekableEnds.push(seekable.end(i));
21490      } // grab the furthest seekable end after sorting, or if there are none
21491      // default to Infinity
21492
21493
21494      return seekableEnds.length ? seekableEnds.sort()[seekableEnds.length - 1] : Infinity;
21495    }
21496    /**
21497     * A helper to get the player seekable start
21498     * so that we don't have to null check everywhere
21499     */
21500    ;
21501
21502    _proto.seekableStart = function seekableStart() {
21503      var seekable = this.player_.seekable();
21504      var seekableStarts = [];
21505      var i = seekable ? seekable.length : 0;
21506
21507      while (i--) {
21508        seekableStarts.push(seekable.start(i));
21509      } // grab the first seekable start after sorting, or if there are none
21510      // default to 0
21511
21512
21513      return seekableStarts.length ? seekableStarts.sort()[0] : 0;
21514    }
21515    /**
21516     * Get the live time window
21517     */
21518    ;
21519
21520    _proto.liveWindow = function liveWindow() {
21521      var liveCurrentTime = this.liveCurrentTime();
21522
21523      if (liveCurrentTime === Infinity) {
21524        return Infinity;
21525      }
21526
21527      return liveCurrentTime - this.seekableStart();
21528    }
21529    /**
21530     * Determines if the player is live, only checks if this component
21531     * is tracking live playback or not
21532     */
21533    ;
21534
21535    _proto.isLive = function isLive() {
21536      return this.isTracking();
21537    }
21538    /**
21539     * Determines if currentTime is at the live edge and won't fall behind
21540     * on each seekableendchange
21541     */
21542    ;
21543
21544    _proto.atLiveEdge = function atLiveEdge() {
21545      return !this.behindLiveEdge();
21546    }
21547    /**
21548     * get what we expect the live current time to be
21549     */
21550    ;
21551
21552    _proto.liveCurrentTime = function liveCurrentTime() {
21553      return this.pastSeekEnd() + this.seekableEnd();
21554    }
21555    /**
21556     * Returns how far past seek end we expect current time to be
21557     */
21558    ;
21559
21560    _proto.pastSeekEnd = function pastSeekEnd() {
21561      return this.pastSeekEnd_;
21562    }
21563    /**
21564     * If we are currently behind the live edge, aka currentTime will be
21565     * behind on a seekableendchange
21566     */
21567    ;
21568
21569    _proto.behindLiveEdge = function behindLiveEdge() {
21570      return this.behindLiveEdge_;
21571    };
21572
21573    _proto.isTracking = function isTracking() {
21574      return typeof this.trackingInterval_ === 'number';
21575    }
21576    /**
21577     * Seek to the live edge if we are behind the live edge
21578     */
21579    ;
21580
21581    _proto.seekToLiveEdge = function seekToLiveEdge() {
21582      if (this.atLiveEdge()) {
21583        return;
21584      }
21585
21586      this.player_.currentTime(this.liveCurrentTime());
21587
21588      if (this.player_.paused()) {
21589        this.player_.play();
21590      }
21591    };
21592
21593    _proto.dispose = function dispose() {
21594      this.stopTracking();
21595
21596      _Component.prototype.dispose.call(this);
21597    };
21598
21599    return LiveTracker;
21600  }(Component);
21601
21602  Component.registerComponent('LiveTracker', LiveTracker);
21603
21604  /**
21605   * This function is used to fire a sourceset when there is something
21606   * similar to `mediaEl.load()` being called. It will try to find the source via
21607   * the `src` attribute and then the `<source>` elements. It will then fire `sourceset`
21608   * with the source that was found or empty string if we cannot know. If it cannot
21609   * find a source then `sourceset` will not be fired.
21610   *
21611   * @param {Html5} tech
21612   *        The tech object that sourceset was setup on
21613   *
21614   * @return {boolean}
21615   *         returns false if the sourceset was not fired and true otherwise.
21616   */
21617
21618  var sourcesetLoad = function sourcesetLoad(tech) {
21619    var el = tech.el(); // if `el.src` is set, that source will be loaded.
21620
21621    if (el.hasAttribute('src')) {
21622      tech.triggerSourceset(el.src);
21623      return true;
21624    }
21625    /**
21626     * Since there isn't a src property on the media element, source elements will be used for
21627     * implementing the source selection algorithm. This happens asynchronously and
21628     * for most cases were there is more than one source we cannot tell what source will
21629     * be loaded, without re-implementing the source selection algorithm. At this time we are not
21630     * going to do that. There are three special cases that we do handle here though:
21631     *
21632     * 1. If there are no sources, do not fire `sourceset`.
21633     * 2. If there is only one `<source>` with a `src` property/attribute that is our `src`
21634     * 3. If there is more than one `<source>` but all of them have the same `src` url.
21635     *    That will be our src.
21636     */
21637
21638
21639    var sources = tech.$$('source');
21640    var srcUrls = [];
21641    var src = ''; // if there are no sources, do not fire sourceset
21642
21643    if (!sources.length) {
21644      return false;
21645    }
vendor: 21,813 bytes, lines 21645-22286
21645 // only count valid/non-duplicate source elements
21646
21647
21648    for (var i = 0; i < sources.length; i++) {
21649      var url = sources[i].src;
21650
21651      if (url && srcUrls.indexOf(url) === -1) {
21652        srcUrls.push(url);
21653      }
21654    } // there were no valid sources
21655
21656
21657    if (!srcUrls.length) {
21658      return false;
21659    } // there is only one valid source element url
21660    // use that
21661
21662
21663    if (srcUrls.length === 1) {
21664      src = srcUrls[0];
21665    }
21666
21667    tech.triggerSourceset(src);
21668    return true;
21669  };
21670  /**
21671   * our implementation of an `innerHTML` descriptor for browsers
21672   * that do not have one.
21673   */
21674
21675
21676  var innerHTMLDescriptorPolyfill = Object.defineProperty({}, 'innerHTML', {
21677    get: function get() {
21678      return this.cloneNode(true).innerHTML;
21679    },
21680    set: function set(v) {
21681      // make a dummy node to use innerHTML on
21682      var dummy = document.createElement(this.nodeName.toLowerCase()); // set innerHTML to the value provided
21683
21684      dummy.innerHTML = v; // make a document fragment to hold the nodes from dummy
21685
21686      var docFrag = document.createDocumentFragment(); // copy all of the nodes created by the innerHTML on dummy
21687      // to the document fragment
21688
21689      while (dummy.childNodes.length) {
21690        docFrag.appendChild(dummy.childNodes[0]);
21691      } // remove content
21692
21693
21694      this.innerText = ''; // now we add all of that html in one by appending the
21695      // document fragment. This is how innerHTML does it.
21696
21697      window$1.Element.prototype.appendChild.call(this, docFrag); // then return the result that innerHTML's setter would
21698
21699      return this.innerHTML;
21700    }
21701  });
21702  /**
21703   * Get a property descriptor given a list of priorities and the
21704   * property to get.
21705   */
21706
21707  var getDescriptor = function getDescriptor(priority, prop) {
21708    var descriptor = {};
21709
21710    for (var i = 0; i < priority.length; i++) {
21711      descriptor = Object.getOwnPropertyDescriptor(priority[i], prop);
21712
21713      if (descriptor && descriptor.set && descriptor.get) {
21714        break;
21715      }
21716    }
21717
21718    descriptor.enumerable = true;
21719    descriptor.configurable = true;
21720    return descriptor;
21721  };
21722
21723  var getInnerHTMLDescriptor = function getInnerHTMLDescriptor(tech) {
21724    return getDescriptor([tech.el(), window$1.HTMLMediaElement.prototype, window$1.Element.prototype, innerHTMLDescriptorPolyfill], 'innerHTML');
21725  };
21726  /**
21727   * Patches browser internal functions so that we can tell synchronously
21728   * if a `<source>` was appended to the media element. For some reason this
21729   * causes a `sourceset` if the the media element is ready and has no source.
21730   * This happens when:
21731   * - The page has just loaded and the media element does not have a source.
21732   * - The media element was emptied of all sources, then `load()` was called.
21733   *
21734   * It does this by patching the following functions/properties when they are supported:
21735   *
21736   * - `append()` - can be used to add a `<source>` element to the media element
21737   * - `appendChild()` - can be used to add a `<source>` element to the media element
21738   * - `insertAdjacentHTML()` -  can be used to add a `<source>` element to the media element
21739   * - `innerHTML` -  can be used to add a `<source>` element to the media element
21740   *
21741   * @param {Html5} tech
21742   *        The tech object that sourceset is being setup on.
21743   */
21744
21745
21746  var firstSourceWatch = function firstSourceWatch(tech) {
21747    var el = tech.el(); // make sure firstSourceWatch isn't setup twice.
21748
21749    if (el.resetSourceWatch_) {
21750      return;
21751    }
21752
21753    var old = {};
21754    var innerDescriptor = getInnerHTMLDescriptor(tech);
21755
21756    var appendWrapper = function appendWrapper(appendFn) {
21757      return function () {
21758        for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) {
21759          args[_key] = arguments[_key];
21760        }
21761
21762        var retval = appendFn.apply(el, args);
21763        sourcesetLoad(tech);
21764        return retval;
21765      };
21766    };
21767
21768    ['append', 'appendChild', 'insertAdjacentHTML'].forEach(function (k) {
21769      if (!el[k]) {
21770        return;
21771      } // store the old function
21772
21773
21774      old[k] = el[k]; // call the old function with a sourceset if a source
21775      // was loaded
21776
21777      el[k] = appendWrapper(old[k]);
21778    });
21779    Object.defineProperty(el, 'innerHTML', mergeOptions(innerDescriptor, {
21780      set: appendWrapper(innerDescriptor.set)
21781    }));
21782
21783    el.resetSourceWatch_ = function () {
21784      el.resetSourceWatch_ = null;
21785      Object.keys(old).forEach(function (k) {
21786        el[k] = old[k];
21787      });
21788      Object.defineProperty(el, 'innerHTML', innerDescriptor);
21789    }; // on the first sourceset, we need to revert our changes
21790
21791
21792    tech.one('sourceset', el.resetSourceWatch_);
21793  };
21794  /**
21795   * our implementation of a `src` descriptor for browsers
21796   * that do not have one.
21797   */
21798
21799
21800  var srcDescriptorPolyfill = Object.defineProperty({}, 'src', {
21801    get: function get() {
21802      if (this.hasAttribute('src')) {
21803        return getAbsoluteURL(window$1.Element.prototype.getAttribute.call(this, 'src'));
21804      }
21805
21806      return '';
21807    },
21808    set: function set(v) {
21809      window$1.Element.prototype.setAttribute.call(this, 'src', v);
21810      return v;
21811    }
21812  });
21813
21814  var getSrcDescriptor = function getSrcDescriptor(tech) {
21815    return getDescriptor([tech.el(), window$1.HTMLMediaElement.prototype, srcDescriptorPolyfill], 'src');
21816  };
21817  /**
21818   * setup `sourceset` handling on the `Html5` tech. This function
21819   * patches the following element properties/functions:
21820   *
21821   * - `src` - to determine when `src` is set
21822   * - `setAttribute()` - to determine when `src` is set
21823   * - `load()` - this re-triggers the source selection algorithm, and can
21824   *              cause a sourceset.
21825   *
21826   * If there is no source when we are adding `sourceset` support or during a `load()`
21827   * we also patch the functions listed in `firstSourceWatch`.
21828   *
21829   * @param {Html5} tech
21830   *        The tech to patch
21831   */
21832
21833
21834  var setupSourceset = function setupSourceset(tech) {
21835    if (!tech.featuresSourceset) {
21836      return;
21837    }
21838
21839    var el = tech.el(); // make sure sourceset isn't setup twice.
21840
21841    if (el.resetSourceset_) {
21842      return;
21843    }
21844
21845    var srcDescriptor = getSrcDescriptor(tech);
21846    var oldSetAttribute = el.setAttribute;
21847    var oldLoad = el.load;
21848    Object.defineProperty(el, 'src', mergeOptions(srcDescriptor, {
21849      set: function set(v) {
21850        var retval = srcDescriptor.set.call(el, v); // we use the getter here to get the actual value set on src
21851
21852        tech.triggerSourceset(el.src);
21853        return retval;
21854      }
21855    }));
21856
21857    el.setAttribute = function (n, v) {
21858      var retval = oldSetAttribute.call(el, n, v);
21859
21860      if (/src/i.test(n)) {
21861        tech.triggerSourceset(el.src);
21862      }
21863
21864      return retval;
21865    };
21866
21867    el.load = function () {
21868      var retval = oldLoad.call(el); // if load was called, but there was no source to fire
21869      // sourceset on. We have to watch for a source append
21870      // as that can trigger a `sourceset` when the media element
21871      // has no source
21872
21873      if (!sourcesetLoad(tech)) {
21874        tech.triggerSourceset('');
21875        firstSourceWatch(tech);
21876      }
21877
21878      return retval;
21879    };
21880
21881    if (el.currentSrc) {
21882      tech.triggerSourceset(el.currentSrc);
21883    } else if (!sourcesetLoad(tech)) {
21884      firstSourceWatch(tech);
21885    }
21886
21887    el.resetSourceset_ = function () {
21888      el.resetSourceset_ = null;
21889      el.load = oldLoad;
21890      el.setAttribute = oldSetAttribute;
21891      Object.defineProperty(el, 'src', srcDescriptor);
21892
21893      if (el.resetSourceWatch_) {
21894        el.resetSourceWatch_();
21895      }
21896    };
21897  };
21898
21899  /**
21900   * HTML5 Media Controller - Wrapper for HTML5 Media API
21901   *
21902   * @mixes Tech~SourceHandlerAdditions
21903   * @extends Tech
21904   */
21905
21906  var Html5 =
21907  /*#__PURE__*/
21908  function (_Tech) {
21909    _inheritsLoose(Html5, _Tech);
21910
21911    /**
21912    * Create an instance of this Tech.
21913    *
21914    * @param {Object} [options]
21915    *        The key/value store of player options.
21916    *
21917    * @param {Component~ReadyCallback} ready
21918    *        Callback function to call when the `HTML5` Tech is ready.
21919    */
21920    function Html5(options, ready) {
21921      var _this;
21922
21923      _this = _Tech.call(this, options, ready) || this;
21924      var source = options.source;
21925      var crossoriginTracks = false; // Set the source if one is provided
21926      // 1) Check if the source is new (if not, we want to keep the original so playback isn't interrupted)
21927      // 2) Check to see if the network state of the tag was failed at init, and if so, reset the source
21928      // anyway so the error gets fired.
21929
21930      if (source && (_this.el_.currentSrc !== source.src || options.tag && options.tag.initNetworkState_ === 3)) {
21931        _this.setSource(source);
21932      } else {
21933        _this.handleLateInit_(_this.el_);
21934      } // setup sourceset after late sourceset/init
21935
21936
21937      if (options.enableSourceset) {
21938        _this.setupSourcesetHandling_();
21939      }
21940
21941      if (_this.el_.hasChildNodes()) {
21942        var nodes = _this.el_.childNodes;
21943        var nodesLength = nodes.length;
21944        var removeNodes = [];
21945
21946        while (nodesLength--) {
21947          var node = nodes[nodesLength];
21948          var nodeName = node.nodeName.toLowerCase();
21949
21950          if (nodeName === 'track') {
21951            if (!_this.featuresNativeTextTracks) {
21952              // Empty video tag tracks so the built-in player doesn't use them also.
21953              // This may not be fast enough to stop HTML5 browsers from reading the tags
21954              // so we'll need to turn off any default tracks if we're manually doing
21955              // captions and subtitles. videoElement.textTracks
21956              removeNodes.push(node);
21957            } else {
21958              // store HTMLTrackElement and TextTrack to remote list
21959              _this.remoteTextTrackEls().addTrackElement_(node);
21960
21961              _this.remoteTextTracks().addTrack(node.track);
21962
21963              _this.textTracks().addTrack(node.track);
21964
21965              if (!crossoriginTracks && !_this.el_.hasAttribute('crossorigin') && isCrossOrigin(node.src)) {
21966                crossoriginTracks = true;
21967              }
21968            }
21969          }
21970        }
21971
21972        for (var i = 0; i < removeNodes.length; i++) {
21973          _this.el_.removeChild(removeNodes[i]);
21974        }
21975      }
21976
21977      _this.proxyNativeTracks_();
21978
21979      if (_this.featuresNativeTextTracks && crossoriginTracks) {
21980        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.');
21981      } // prevent iOS Safari from disabling metadata text tracks during native playback
21982
21983
21984      _this.restoreMetadataTracksInIOSNativePlayer_(); // Determine if native controls should be used
21985      // Our goal should be to get the custom controls on mobile solid everywhere
21986      // so we can remove this all together. Right now this will block custom
21987      // controls on touch enabled laptops like the Chrome Pixel
21988
21989
21990      if ((TOUCH_ENABLED || IS_IPHONE || IS_NATIVE_ANDROID) && options.nativeControlsForTouch === true) {
21991        _this.setControls(true);
21992      } // on iOS, we want to proxy `webkitbeginfullscreen` and `webkitendfullscreen`
21993      // into a `fullscreenchange` event
21994
21995
21996      _this.proxyWebkitFullscreen_();
21997
21998      _this.triggerReady();
21999
22000      return _this;
22001    }
22002    /**
22003     * Dispose of `HTML5` media element and remove all tracks.
22004     */
22005
22006
22007    var _proto = Html5.prototype;
22008
22009    _proto.dispose = function dispose() {
22010      if (this.el_ && this.el_.resetSourceset_) {
22011        this.el_.resetSourceset_();
22012      }
22013
22014      Html5.disposeMediaElement(this.el_);
22015      this.options_ = null; // tech will handle clearing of the emulated track list
22016
22017      _Tech.prototype.dispose.call(this);
22018    }
22019    /**
22020     * Modify the media element so that we can detect when
22021     * the source is changed. Fires `sourceset` just after the source has changed
22022     */
22023    ;
22024
22025    _proto.setupSourcesetHandling_ = function setupSourcesetHandling_() {
22026      setupSourceset(this);
22027    }
22028    /**
22029     * When a captions track is enabled in the iOS Safari native player, all other
22030     * tracks are disabled (including metadata tracks), which nulls all of their
22031     * associated cue points. This will restore metadata tracks to their pre-fullscreen
22032     * state in those cases so that cue points are not needlessly lost.
22033     *
22034     * @private
22035     */
22036    ;
22037
22038    _proto.restoreMetadataTracksInIOSNativePlayer_ = function restoreMetadataTracksInIOSNativePlayer_() {
22039      var textTracks = this.textTracks();
22040      var metadataTracksPreFullscreenState; // captures a snapshot of every metadata track's current state
22041
22042      var takeMetadataTrackSnapshot = function takeMetadataTrackSnapshot() {
22043        metadataTracksPreFullscreenState = [];
22044
22045        for (var i = 0; i < textTracks.length; i++) {
22046          var track = textTracks[i];
22047
22048          if (track.kind === 'metadata') {
22049            metadataTracksPreFullscreenState.push({
22050              track: track,
22051              storedMode: track.mode
22052            });
22053          }
22054        }
22055      }; // snapshot each metadata track's initial state, and update the snapshot
22056      // each time there is a track 'change' event
22057
22058
22059      takeMetadataTrackSnapshot();
22060      textTracks.addEventListener('change', takeMetadataTrackSnapshot);
22061      this.on('dispose', function () {
22062        return textTracks.removeEventListener('change', takeMetadataTrackSnapshot);
22063      });
22064
22065      var restoreTrackMode = function restoreTrackMode() {
22066        for (var i = 0; i < metadataTracksPreFullscreenState.length; i++) {
22067          var storedTrack = metadataTracksPreFullscreenState[i];
22068
22069          if (storedTrack.track.mode === 'disabled' && storedTrack.track.mode !== storedTrack.storedMode) {
22070            storedTrack.track.mode = storedTrack.storedMode;
22071          }
22072        } // we only want this handler to be executed on the first 'change' event
22073
22074
22075        textTracks.removeEventListener('change', restoreTrackMode);
22076      }; // when we enter fullscreen playback, stop updating the snapshot and
22077      // restore all track modes to their pre-fullscreen state
22078
22079
22080      this.on('webkitbeginfullscreen', function () {
22081        textTracks.removeEventListener('change', takeMetadataTrackSnapshot); // remove the listener before adding it just in case it wasn't previously removed
22082
22083        textTracks.removeEventListener('change', restoreTrackMode);
22084        textTracks.addEventListener('change', restoreTrackMode);
22085      }); // start updating the snapshot again after leaving fullscreen
22086
22087      this.on('webkitendfullscreen', function () {
22088        // remove the listener before adding it just in case it wasn't previously removed
22089        textTracks.removeEventListener('change', takeMetadataTrackSnapshot);
22090        textTracks.addEventListener('change', takeMetadataTrackSnapshot); // remove the restoreTrackMode handler in case it wasn't triggered during fullscreen playback
22091
22092        textTracks.removeEventListener('change', restoreTrackMode);
22093      });
22094    }
22095    /**
22096     * Attempt to force override of tracks for the given type
22097     *
22098     * @param {string} type - Track type to override, possible values include 'Audio',
22099     * 'Video', and 'Text'.
22100     * @param {boolean} override - If set to true native audio/video will be overridden,
22101     * otherwise native audio/video will potentially be used.
22102     * @private
22103     */
22104    ;
22105
22106    _proto.overrideNative_ = function overrideNative_(type, override) {
22107      var _this2 = this;
22108
22109      // If there is no behavioral change don't add/remove listeners
22110      if (override !== this["featuresNative" + type + "Tracks"]) {
22111        return;
22112      }
22113
22114      var lowerCaseType = type.toLowerCase();
22115
22116      if (this[lowerCaseType + "TracksListeners_"]) {
22117        Object.keys(this[lowerCaseType + "TracksListeners_"]).forEach(function (eventName) {
22118          var elTracks = _this2.el()[lowerCaseType + "Tracks"];
22119
22120          elTracks.removeEventListener(eventName, _this2[lowerCaseType + "TracksListeners_"][eventName]);
22121        });
22122      }
22123
22124      this["featuresNative" + type + "Tracks"] = !override;
22125      this[lowerCaseType + "TracksListeners_"] = null;
22126      this.proxyNativeTracksForType_(lowerCaseType);
22127    }
22128    /**
22129     * Attempt to force override of native audio tracks.
22130     *
22131     * @param {boolean} override - If set to true native audio will be overridden,
22132     * otherwise native audio will potentially be used.
22133     */
22134    ;
22135
22136    _proto.overrideNativeAudioTracks = function overrideNativeAudioTracks(override) {
22137      this.overrideNative_('Audio', override);
22138    }
22139    /**
22140     * Attempt to force override of native video tracks.
22141     *
22142     * @param {boolean} override - If set to true native video will be overridden,
22143     * otherwise native video will potentially be used.
22144     */
22145    ;
22146
22147    _proto.overrideNativeVideoTracks = function overrideNativeVideoTracks(override) {
22148      this.overrideNative_('Video', override);
22149    }
22150    /**
22151     * Proxy native track list events for the given type to our track
22152     * lists if the browser we are playing in supports that type of track list.
22153     *
22154     * @param {string} name - Track type; values include 'audio', 'video', and 'text'
22155     * @private
22156     */
22157    ;
22158
22159    _proto.proxyNativeTracksForType_ = function proxyNativeTracksForType_(name) {
22160      var _this3 = this;
22161
22162      var props = NORMAL[name];
22163      var elTracks = this.el()[props.getterName];
22164      var techTracks = this[props.getterName]();
22165
22166      if (!this["featuresNative" + props.capitalName + "Tracks"] || !elTracks || !elTracks.addEventListener) {
22167        return;
22168      }
22169
22170      var listeners = {
22171        change: function change(e) {
22172          techTracks.trigger({
22173            type: 'change',
22174            target: techTracks,
22175            currentTarget: techTracks,
22176            srcElement: techTracks
22177          });
22178        },
22179        addtrack: function addtrack(e) {
22180          techTracks.addTrack(e.track);
22181        },
22182        removetrack: function removetrack(e) {
22183          techTracks.removeTrack(e.track);
22184        }
22185      };
22186
22187      var removeOldTracks = function removeOldTracks() {
22188        var removeTracks = [];
22189
22190        for (var i = 0; i < techTracks.length; i++) {
22191          var found = false;
22192
22193          for (var j = 0; j < elTracks.length; j++) {
22194            if (elTracks[j] === techTracks[i]) {
22195              found = true;
22196              break;
22197            }
22198          }
22199
22200          if (!found) {
22201            removeTracks.push(techTracks[i]);
22202          }
22203        }
22204
22205        while (removeTracks.length) {
22206          techTracks.removeTrack(removeTracks.shift());
22207        }
22208      };
22209
22210      this[props.getterName + 'Listeners_'] = listeners;
22211      Object.keys(listeners).forEach(function (eventName) {
22212        var listener = listeners[eventName];
22213        elTracks.addEventListener(eventName, listener);
22214
22215        _this3.on('dispose', function (e) {
22216          return elTracks.removeEventListener(eventName, listener);
22217        });
22218      }); // Remove (native) tracks that are not used anymore
22219
22220      this.on('loadstart', removeOldTracks);
22221      this.on('dispose', function (e) {
22222        return _this3.off('loadstart', removeOldTracks);
22223      });
22224    }
22225    /**
22226     * Proxy all native track list events to our track lists if the browser we are playing
22227     * in supports that type of track list.
22228     *
22229     * @private
22230     */
22231    ;
22232
22233    _proto.proxyNativeTracks_ = function proxyNativeTracks_() {
22234      var _this4 = this;
22235
22236      NORMAL.names.forEach(function (name) {
22237        _this4.proxyNativeTracksForType_(name);
22238      });
22239    }
22240    /**
22241     * Create the `Html5` Tech's DOM element.
22242     *
22243     * @return {Element}
22244     *         The element that gets created.
22245     */
22246    ;
22247
22248    _proto.createEl = function createEl() {
22249      var el = this.options_.tag; // Check if this browser supports moving the element into the box.
22250      // On the iPhone video will break if you move the element,
22251      // So we have to create a brand new element.
22252      // If we ingested the player div, we do not need to move the media element.
22253
22254      if (!el || !(this.options_.playerElIngest || this.movingMediaElementInDOM)) {
22255        // If the original tag is still there, clone and remove it.
22256        if (el) {
22257          var clone = el.cloneNode(true);
22258
22259          if (el.parentNode) {
22260            el.parentNode.insertBefore(clone, el);
22261          }
22262
22263          Html5.disposeMediaElement(el);
22264          el = clone;
22265        } else {
22266          el = document.createElement('video'); // determine if native controls should be used
22267          var tagAttributes = this.options_.tag && getAttributes(this.options_.tag);
22268          var attributes = mergeOptions({}, tagAttributes);
22269
22270          if (!TOUCH_ENABLED || this.options_.nativeControlsForTouch !== true) {
22271            delete attributes.controls;
22272          }
22273
22274          setAttributes(el, assign(attributes, {
22275            id: this.options_.techId,
22276            "class": 'vjs-tech'
22277          }));
22278        }
22279
22280        el.playerId = this.options_.playerId;
22281      }
22282
22283      if (typeof this.options_.preload !== 'undefined') {
22284        setAttribute(el, 'preload', this.options_.preload);
22285      } // Update specific tag settings, in case they were overridden
22286      // `autoplay` has to be *last* so that `muted` and `playsinline` are present
vendor: 4,273 bytes, lines 22287-22408
22287      // when iOS/Safari or other browsers attempt to autoplay.
22288
22289
22290      var settingsAttrs = ['loop', 'muted', 'playsinline', 'autoplay'];
22291
22292      for (var i = 0; i < settingsAttrs.length; i++) {
22293        var attr = settingsAttrs[i];
22294        var value = this.options_[attr];
22295
22296        if (typeof value !== 'undefined') {
22297          if (value) {
22298            setAttribute(el, attr, attr);
22299          } else {
22300            removeAttribute(el, attr);
22301          }
22302
22303          el[attr] = value;
22304        }
22305      }
22306
22307      return el;
22308    }
22309    /**
22310     * This will be triggered if the loadstart event has already fired, before videojs was
22311     * ready. Two known examples of when this can happen are:
22312     * 1. If we're loading the playback object after it has started loading
22313     * 2. The media is already playing the (often with autoplay on) then
22314     *
22315     * This function will fire another loadstart so that videojs can catchup.
22316     *
22317     * @fires Tech#loadstart
22318     *
22319     * @return {undefined}
22320     *         returns nothing.
22321     */
22322    ;
22323
22324    _proto.handleLateInit_ = function handleLateInit_(el) {
22325      if (el.networkState === 0 || el.networkState === 3) {
22326        // The video element hasn't started loading the source yet
22327        // or didn't find a source
22328        return;
22329      }
22330
22331      if (el.readyState === 0) {
22332        // NetworkState is set synchronously BUT loadstart is fired at the
22333        // end of the current stack, usually before setInterval(fn, 0).
22334        // So at this point we know loadstart may have already fired or is
22335        // about to fire, and either way the player hasn't seen it yet.
22336        // We don't want to fire loadstart prematurely here and cause a
22337        // double loadstart so we'll wait and see if it happens between now
22338        // and the next loop, and fire it if not.
22339        // HOWEVER, we also want to make sure it fires before loadedmetadata
22340        // which could also happen between now and the next loop, so we'll
22341        // watch for that also.
22342        var loadstartFired = false;
22343
22344        var setLoadstartFired = function setLoadstartFired() {
22345          loadstartFired = true;
22346        };
22347
22348        this.on('loadstart', setLoadstartFired);
22349
22350        var triggerLoadstart = function triggerLoadstart() {
22351          // We did miss the original loadstart. Make sure the player
22352          // sees loadstart before loadedmetadata
22353          if (!loadstartFired) {
22354            this.trigger('loadstart');
22355          }
22356        };
22357
22358        this.on('loadedmetadata', triggerLoadstart);
22359        this.ready(function () {
22360          this.off('loadstart', setLoadstartFired);
22361          this.off('loadedmetadata', triggerLoadstart);
22362
22363          if (!loadstartFired) {
22364            // We did miss the original native loadstart. Fire it now.
22365            this.trigger('loadstart');
22366          }
22367        });
22368        return;
22369      } // From here on we know that loadstart already fired and we missed it.
22370      // The other readyState events aren't as much of a problem if we double
22371      // them, so not going to go to as much trouble as loadstart to prevent
22372      // that unless we find reason to.
22373
22374
22375      var eventsToTrigger = ['loadstart']; // loadedmetadata: newly equal to HAVE_METADATA (1) or greater
22376
22377      eventsToTrigger.push('loadedmetadata'); // loadeddata: newly increased to HAVE_CURRENT_DATA (2) or greater
22378
22379      if (el.readyState >= 2) {
22380        eventsToTrigger.push('loadeddata');
22381      } // canplay: newly increased to HAVE_FUTURE_DATA (3) or greater
22382
22383
22384      if (el.readyState >= 3) {
22385        eventsToTrigger.push('canplay');
22386      } // canplaythrough: newly equal to HAVE_ENOUGH_DATA (4)
22387
22388
22389      if (el.readyState >= 4) {
22390        eventsToTrigger.push('canplaythrough');
22391      } // We still need to give the player time to add event listeners
22392
22393
22394      this.ready(function () {
22395        eventsToTrigger.forEach(function (type) {
22396          this.trigger(type);
22397        }, this);
22398      });
22399    }
22400    /**
22401     * Set current time for the `HTML5` tech.
22402     *
22403     * @param {number} seconds
22404     *        Set the current time of the media to this.
22405     */
22406    ;
22407
22408    _proto.setCurrentTime = function setCurrentTime(seconds) {
vendor: 12,562 bytes, lines 22409-22799
22409      try {
22410        this.el_.currentTime = seconds;
22411      } catch (e) {
22412        log(e, 'Video is not ready. (Video.js)'); // this.warning(VideoJS.warnings.videoNotReady);
22413      }
22414    }
22415    /**
22416     * Get the current duration of the HTML5 media element.
22417     *
22418     * @return {number}
22419     *         The duration of the media or 0 if there is no duration.
22420     */
22421    ;
22422
22423    _proto.duration = function duration() {
22424      var _this5 = this;
22425
22426      // Android Chrome will report duration as Infinity for VOD HLS until after
22427      // playback has started, which triggers the live display erroneously.
22428      // Return NaN if playback has not started and trigger a durationupdate once
22429      // the duration can be reliably known.
22430      if (this.el_.duration === Infinity && IS_ANDROID && IS_CHROME && this.el_.currentTime === 0) {
22431        // Wait for the first `timeupdate` with currentTime > 0 - there may be
22432        // several with 0
22433        var checkProgress = function checkProgress() {
22434          if (_this5.el_.currentTime > 0) {
22435            // Trigger durationchange for genuinely live video
22436            if (_this5.el_.duration === Infinity) {
22437              _this5.trigger('durationchange');
22438            }
22439
22440            _this5.off('timeupdate', checkProgress);
22441          }
22442        };
22443
22444        this.on('timeupdate', checkProgress);
22445        return NaN;
22446      }
22447
22448      return this.el_.duration || NaN;
22449    }
22450    /**
22451     * Get the current width of the HTML5 media element.
22452     *
22453     * @return {number}
22454     *         The width of the HTML5 media element.
22455     */
22456    ;
22457
22458    _proto.width = function width() {
22459      return this.el_.offsetWidth;
22460    }
22461    /**
22462     * Get the current height of the HTML5 media element.
22463     *
22464     * @return {number}
22465     *         The height of the HTML5 media element.
22466     */
22467    ;
22468
22469    _proto.height = function height() {
22470      return this.el_.offsetHeight;
22471    }
22472    /**
22473     * Proxy iOS `webkitbeginfullscreen` and `webkitendfullscreen` into
22474     * `fullscreenchange` event.
22475     *
22476     * @private
22477     * @fires fullscreenchange
22478     * @listens webkitendfullscreen
22479     * @listens webkitbeginfullscreen
22480     * @listens webkitbeginfullscreen
22481     */
22482    ;
22483
22484    _proto.proxyWebkitFullscreen_ = function proxyWebkitFullscreen_() {
22485      var _this6 = this;
22486
22487      if (!('webkitDisplayingFullscreen' in this.el_)) {
22488        return;
22489      }
22490
22491      var endFn = function endFn() {
22492        this.trigger('fullscreenchange', {
22493          isFullscreen: false
22494        });
22495      };
22496
22497      var beginFn = function beginFn() {
22498        if ('webkitPresentationMode' in this.el_ && this.el_.webkitPresentationMode !== 'picture-in-picture') {
22499          this.one('webkitendfullscreen', endFn);
22500          this.trigger('fullscreenchange', {
22501            isFullscreen: true
22502          });
22503        }
22504      };
22505
22506      this.on('webkitbeginfullscreen', beginFn);
22507      this.on('dispose', function () {
22508        _this6.off('webkitbeginfullscreen', beginFn);
22509
22510        _this6.off('webkitendfullscreen', endFn);
22511      });
22512    }
22513    /**
22514     * Check if fullscreen is supported on the current playback device.
22515     *
22516     * @return {boolean}
22517     *         - True if fullscreen is supported.
22518     *         - False if fullscreen is not supported.
22519     */
22520    ;
22521
22522    _proto.supportsFullScreen = function supportsFullScreen() {
22523      if (typeof this.el_.webkitEnterFullScreen === 'function') {
22524        var userAgent = window$1.navigator && window$1.navigator.userAgent || ''; // Seems to be broken in Chromium/Chrome && Safari in Leopard
22525
22526        if (/Android/.test(userAgent) || !/Chrome|Mac OS X 10.5/.test(userAgent)) {
22527          return true;
22528        }
22529      }
22530
22531      return false;
22532    }
22533    /**
22534     * Request that the `HTML5` Tech enter fullscreen.
22535     */
22536    ;
22537
22538    _proto.enterFullScreen = function enterFullScreen() {
22539      var video = this.el_;
22540
22541      if (video.paused && video.networkState <= video.HAVE_METADATA) {
22542        // attempt to prime the video element for programmatic access
22543        // this isn't necessary on the desktop but shouldn't hurt
22544        this.el_.play(); // playing and pausing synchronously during the transition to fullscreen
22545        // can get iOS ~6.1 devices into a play/pause loop
22546
22547        this.setTimeout(function () {
22548          video.pause();
22549          video.webkitEnterFullScreen();
22550        }, 0);
22551      } else {
22552        video.webkitEnterFullScreen();
22553      }
22554    }
22555    /**
22556     * Request that the `HTML5` Tech exit fullscreen.
22557     */
22558    ;
22559
22560    _proto.exitFullScreen = function exitFullScreen() {
22561      this.el_.webkitExitFullScreen();
22562    }
22563    /**
22564     * Create a floating video window always on top of other windows so that users may
22565     * continue consuming media while they interact with other content sites, or
22566     * applications on their device.
22567     *
22568     * @see [Spec]{@link https://wicg.github.io/picture-in-picture}
22569     *
22570     * @return {Promise}
22571     *         A promise with a Picture-in-Picture window.
22572     */
22573    ;
22574
22575    _proto.requestPictureInPicture = function requestPictureInPicture() {
22576      return this.el_.requestPictureInPicture();
22577    }
22578    /**
22579     * A getter/setter for the `Html5` Tech's source object.
22580     * > Note: Please use {@link Html5#setSource}
22581     *
22582     * @param {Tech~SourceObject} [src]
22583     *        The source object you want to set on the `HTML5` techs element.
22584     *
22585     * @return {Tech~SourceObject|undefined}
22586     *         - The current source object when a source is not passed in.
22587     *         - undefined when setting
22588     *
22589     * @deprecated Since version 5.
22590     */
22591    ;
22592
22593    _proto.src = function src(_src) {
22594      if (_src === undefined) {
22595        return this.el_.src;
22596      } // Setting src through `src` instead of `setSrc` will be deprecated
22597
22598
22599      this.setSrc(_src);
22600    }
22601    /**
22602     * Reset the tech by removing all sources and then calling
22603     * {@link Html5.resetMediaElement}.
22604     */
22605    ;
22606
22607    _proto.reset = function reset() {
22608      Html5.resetMediaElement(this.el_);
22609    }
22610    /**
22611     * Get the current source on the `HTML5` Tech. Falls back to returning the source from
22612     * the HTML5 media element.
22613     *
22614     * @return {Tech~SourceObject}
22615     *         The current source object from the HTML5 tech. With a fallback to the
22616     *         elements source.
22617     */
22618    ;
22619
22620    _proto.currentSrc = function currentSrc() {
22621      if (this.currentSource_) {
22622        return this.currentSource_.src;
22623      }
22624
22625      return this.el_.currentSrc;
22626    }
22627    /**
22628     * Set controls attribute for the HTML5 media Element.
22629     *
22630     * @param {string} val
22631     *        Value to set the controls attribute to
22632     */
22633    ;
22634
22635    _proto.setControls = function setControls(val) {
22636      this.el_.controls = !!val;
22637    }
22638    /**
22639     * Create and returns a remote {@link TextTrack} object.
22640     *
22641     * @param {string} kind
22642     *        `TextTrack` kind (subtitles, captions, descriptions, chapters, or metadata)
22643     *
22644     * @param {string} [label]
22645     *        Label to identify the text track
22646     *
22647     * @param {string} [language]
22648     *        Two letter language abbreviation
22649     *
22650     * @return {TextTrack}
22651     *         The TextTrack that gets created.
22652     */
22653    ;
22654
22655    _proto.addTextTrack = function addTextTrack(kind, label, language) {
22656      if (!this.featuresNativeTextTracks) {
22657        return _Tech.prototype.addTextTrack.call(this, kind, label, language);
22658      }
22659
22660      return this.el_.addTextTrack(kind, label, language);
22661    }
22662    /**
22663     * Creates either native TextTrack or an emulated TextTrack depending
22664     * on the value of `featuresNativeTextTracks`
22665     *
22666     * @param {Object} options
22667     *        The object should contain the options to initialize the TextTrack with.
22668     *
22669     * @param {string} [options.kind]
22670     *        `TextTrack` kind (subtitles, captions, descriptions, chapters, or metadata).
22671     *
22672     * @param {string} [options.label]
22673     *        Label to identify the text track
22674     *
22675     * @param {string} [options.language]
22676     *        Two letter language abbreviation.
22677     *
22678     * @param {boolean} [options.default]
22679     *        Default this track to on.
22680     *
22681     * @param {string} [options.id]
22682     *        The internal id to assign this track.
22683     *
22684     * @param {string} [options.src]
22685     *        A source url for the track.
22686     *
22687     * @return {HTMLTrackElement}
22688     *         The track element that gets created.
22689     */
22690    ;
22691
22692    _proto.createRemoteTextTrack = function createRemoteTextTrack(options) {
22693      if (!this.featuresNativeTextTracks) {
22694        return _Tech.prototype.createRemoteTextTrack.call(this, options);
22695      }
22696
22697      var htmlTrackElement = document.createElement('track');
22698
22699      if (options.kind) {
22700        htmlTrackElement.kind = options.kind;
22701      }
22702
22703      if (options.label) {
22704        htmlTrackElement.label = options.label;
22705      }
22706
22707      if (options.language || options.srclang) {
22708        htmlTrackElement.srclang = options.language || options.srclang;
22709      }
22710
22711      if (options["default"]) {
22712        htmlTrackElement["default"] = options["default"];
22713      }
22714
22715      if (options.id) {
22716        htmlTrackElement.id = options.id;
22717      }
22718
22719      if (options.src) {
22720        htmlTrackElement.src = options.src;
22721      }
22722
22723      return htmlTrackElement;
22724    }
22725    /**
22726     * Creates a remote text track object and returns an html track element.
22727     *
22728     * @param {Object} options The object should contain values for
22729     * kind, language, label, and src (location of the WebVTT file)
22730     * @param {boolean} [manualCleanup=true] if set to false, the TextTrack will be
22731     * automatically removed from the video element whenever the source changes
22732     * @return {HTMLTrackElement} An Html Track Element.
22733     * This can be an emulated {@link HTMLTrackElement} or a native one.
22734     * @deprecated The default value of the "manualCleanup" parameter will default
22735     * to "false" in upcoming versions of Video.js
22736     */
22737    ;
22738
22739    _proto.addRemoteTextTrack = function addRemoteTextTrack(options, manualCleanup) {
22740      var htmlTrackElement = _Tech.prototype.addRemoteTextTrack.call(this, options, manualCleanup);
22741
22742      if (this.featuresNativeTextTracks) {
22743        this.el().appendChild(htmlTrackElement);
22744      }
22745
22746      return htmlTrackElement;
22747    }
22748    /**
22749     * Remove remote `TextTrack` from `TextTrackList` object
22750     *
22751     * @param {TextTrack} track
22752     *        `TextTrack` object to remove
22753     */
22754    ;
22755
22756    _proto.removeRemoteTextTrack = function removeRemoteTextTrack(track) {
22757      _Tech.prototype.removeRemoteTextTrack.call(this, track);
22758
22759      if (this.featuresNativeTextTracks) {
22760        var tracks = this.$$('track');
22761        var i = tracks.length;
22762
22763        while (i--) {
22764          if (track === tracks[i] || track === tracks[i].track) {
22765            this.el().removeChild(tracks[i]);
22766          }
22767        }
22768      }
22769    }
22770    /**
22771     * Gets available media playback quality metrics as specified by the W3C's Media
22772     * Playback Quality API.
22773     *
22774     * @see [Spec]{@link https://wicg.github.io/media-playback-quality}
22775     *
22776     * @return {Object}
22777     *         An object with supported media playback quality metrics
22778     */
22779    ;
22780
22781    _proto.getVideoPlaybackQuality = function getVideoPlaybackQuality() {
22782      if (typeof this.el().getVideoPlaybackQuality === 'function') {
22783        return this.el().getVideoPlaybackQuality();
22784      }
22785
22786      var videoPlaybackQuality = {};
22787
22788      if (typeof this.el().webkitDroppedFrameCount !== 'undefined' && typeof this.el().webkitDecodedFrameCount !== 'undefined') {
22789        videoPlaybackQuality.droppedVideoFrames = this.el().webkitDroppedFrameCount;
22790        videoPlaybackQuality.totalVideoFrames = this.el().webkitDecodedFrameCount;
22791      }
22792
22793      if (window$1.performance && typeof window$1.performance.now === 'function') {
22794        videoPlaybackQuality.creationTime = window$1.performance.now();
22795      } else if (window$1.performance && window$1.performance.timing && typeof window$1.performance.timing.navigationStart === 'number') {
22796        videoPlaybackQuality.creationTime = window$1.Date.now() - window$1.performance.timing.navigationStart;
22797      }
22798
22799      return videoPlaybackQuality;
vendor: 24,456 bytes, lines 22800-23513
22800    };
22801
22802    return Html5;
22803  }(Tech);
22804  /* HTML5 Support Testing ---------------------------------------------------- */
22805
22806
22807  if (isReal()) {
22808    /**
22809     * Element for testing browser HTML5 media capabilities
22810     *
22811     * @type {Element}
22812     * @constant
22813     * @private
22814     */
22815    Html5.TEST_VID = document.createElement('video');
22816    var track = document.createElement('track');
22817    track.kind = 'captions';
22818    track.srclang = 'en';
22819    track.label = 'English';
22820    Html5.TEST_VID.appendChild(track);
22821  }
22822  /**
22823   * Check if HTML5 media is supported by this browser/device.
22824   *
22825   * @return {boolean}
22826   *         - True if HTML5 media is supported.
22827   *         - False if HTML5 media is not supported.
22828   */
22829
22830
22831  Html5.isSupported = function () {
22832    // IE with no Media Player is a LIAR! (#984)
22833    try {
22834      Html5.TEST_VID.volume = 0.5;
22835    } catch (e) {
22836      return false;
22837    }
22838
22839    return !!(Html5.TEST_VID && Html5.TEST_VID.canPlayType);
22840  };
22841  /**
22842   * Check if the tech can support the given type
22843   *
22844   * @param {string} type
22845   *        The mimetype to check
22846   * @return {string} 'probably', 'maybe', or '' (empty string)
22847   */
22848
22849
22850  Html5.canPlayType = function (type) {
22851    return Html5.TEST_VID.canPlayType(type);
22852  };
22853  /**
22854   * Check if the tech can support the given source
22855   *
22856   * @param {Object} srcObj
22857   *        The source object
22858   * @param {Object} options
22859   *        The options passed to the tech
22860   * @return {string} 'probably', 'maybe', or '' (empty string)
22861   */
22862
22863
22864  Html5.canPlaySource = function (srcObj, options) {
22865    return Html5.canPlayType(srcObj.type);
22866  };
22867  /**
22868   * Check if the volume can be changed in this browser/device.
22869   * Volume cannot be changed in a lot of mobile devices.
22870   * Specifically, it can't be changed from 1 on iOS.
22871   *
22872   * @return {boolean}
22873   *         - True if volume can be controlled
22874   *         - False otherwise
22875   */
22876
22877
22878  Html5.canControlVolume = function () {
22879    // IE will error if Windows Media Player not installed #3315
22880    try {
22881      var volume = Html5.TEST_VID.volume;
22882      Html5.TEST_VID.volume = volume / 2 + 0.1;
22883      return volume !== Html5.TEST_VID.volume;
22884    } catch (e) {
22885      return false;
22886    }
22887  };
22888  /**
22889   * Check if the volume can be muted in this browser/device.
22890   * Some devices, e.g. iOS, don't allow changing volume
22891   * but permits muting/unmuting.
22892   *
22893   * @return {bolean}
22894   *      - True if volume can be muted
22895   *      - False otherwise
22896   */
22897
22898
22899  Html5.canMuteVolume = function () {
22900    try {
22901      var muted = Html5.TEST_VID.muted; // in some versions of iOS muted property doesn't always
22902      // work, so we want to set both property and attribute
22903
22904      Html5.TEST_VID.muted = !muted;
22905
22906      if (Html5.TEST_VID.muted) {
22907        setAttribute(Html5.TEST_VID, 'muted', 'muted');
22908      } else {
22909        removeAttribute(Html5.TEST_VID, 'muted', 'muted');
22910      }
22911
22912      return muted !== Html5.TEST_VID.muted;
22913    } catch (e) {
22914      return false;
22915    }
22916  };
22917  /**
22918   * Check if the playback rate can be changed in this browser/device.
22919   *
22920   * @return {boolean}
22921   *         - True if playback rate can be controlled
22922   *         - False otherwise
22923   */
22924
22925
22926  Html5.canControlPlaybackRate = function () {
22927    // Playback rate API is implemented in Android Chrome, but doesn't do anything
22928    // https://github.com/videojs/video.js/issues/3180
22929    if (IS_ANDROID && IS_CHROME && CHROME_VERSION < 58) {
22930      return false;
22931    } // IE will error if Windows Media Player not installed #3315
22932
22933
22934    try {
22935      var playbackRate = Html5.TEST_VID.playbackRate;
22936      Html5.TEST_VID.playbackRate = playbackRate / 2 + 0.1;
22937      return playbackRate !== Html5.TEST_VID.playbackRate;
22938    } catch (e) {
22939      return false;
22940    }
22941  };
22942  /**
22943   * Check if we can override a video/audio elements attributes, with
22944   * Object.defineProperty.
22945   *
22946   * @return {boolean}
22947   *         - True if builtin attributes can be overridden
22948   *         - False otherwise
22949   */
22950
22951
22952  Html5.canOverrideAttributes = function () {
22953    // if we cannot overwrite the src/innerHTML property, there is no support
22954    // iOS 7 safari for instance cannot do this.
22955    try {
22956      var noop = function noop() {};
22957
22958      Object.defineProperty(document.createElement('video'), 'src', {
22959        get: noop,
22960        set: noop
22961      });
22962      Object.defineProperty(document.createElement('audio'), 'src', {
22963        get: noop,
22964        set: noop
22965      });
22966      Object.defineProperty(document.createElement('video'), 'innerHTML', {
22967        get: noop,
22968        set: noop
22969      });
22970      Object.defineProperty(document.createElement('audio'), 'innerHTML', {
22971        get: noop,
22972        set: noop
22973      });
22974    } catch (e) {
22975      return false;
22976    }
22977
22978    return true;
22979  };
22980  /**
22981   * Check to see if native `TextTrack`s are supported by this browser/device.
22982   *
22983   * @return {boolean}
22984   *         - True if native `TextTrack`s are supported.
22985   *         - False otherwise
22986   */
22987
22988
22989  Html5.supportsNativeTextTracks = function () {
22990    return IS_ANY_SAFARI || IS_IOS && IS_CHROME;
22991  };
22992  /**
22993   * Check to see if native `VideoTrack`s are supported by this browser/device
22994   *
22995   * @return {boolean}
22996   *        - True if native `VideoTrack`s are supported.
22997   *        - False otherwise
22998   */
22999
23000
23001  Html5.supportsNativeVideoTracks = function () {
23002    return !!(Html5.TEST_VID && Html5.TEST_VID.videoTracks);
23003  };
23004  /**
23005   * Check to see if native `AudioTrack`s are supported by this browser/device
23006   *
23007   * @return {boolean}
23008   *        - True if native `AudioTrack`s are supported.
23009   *        - False otherwise
23010   */
23011
23012
23013  Html5.supportsNativeAudioTracks = function () {
23014    return !!(Html5.TEST_VID && Html5.TEST_VID.audioTracks);
23015  };
23016  /**
23017   * An array of events available on the Html5 tech.
23018   *
23019   * @private
23020   * @type {Array}
23021   */
23022
23023
23024  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'];
23025  /**
23026   * Boolean indicating whether the `Tech` supports volume control.
23027   *
23028   * @type {boolean}
23029   * @default {@link Html5.canControlVolume}
23030   */
23031
23032  Html5.prototype.featuresVolumeControl = Html5.canControlVolume();
23033  /**
23034   * Boolean indicating whether the `Tech` supports muting volume.
23035   *
23036   * @type {bolean}
23037   * @default {@link Html5.canMuteVolume}
23038   */
23039
23040  Html5.prototype.featuresMuteControl = Html5.canMuteVolume();
23041  /**
23042   * Boolean indicating whether the `Tech` supports changing the speed at which the media
23043   * plays. Examples:
23044   *   - Set player to play 2x (twice) as fast
23045   *   - Set player to play 0.5x (half) as fast
23046   *
23047   * @type {boolean}
23048   * @default {@link Html5.canControlPlaybackRate}
23049   */
23050
23051  Html5.prototype.featuresPlaybackRate = Html5.canControlPlaybackRate();
23052  /**
23053   * Boolean indicating whether the `Tech` supports the `sourceset` event.
23054   *
23055   * @type {boolean}
23056   * @default
23057   */
23058
23059  Html5.prototype.featuresSourceset = Html5.canOverrideAttributes();
23060  /**
23061   * Boolean indicating whether the `HTML5` tech currently supports the media element
23062   * moving in the DOM. iOS breaks if you move the media element, so this is set this to
23063   * false there. Everywhere else this should be true.
23064   *
23065   * @type {boolean}
23066   * @default
23067   */
23068
23069  Html5.prototype.movingMediaElementInDOM = !IS_IOS; // TODO: Previous comment: No longer appears to be used. Can probably be removed.
23070  //       Is this true?
23071
23072  /**
23073   * Boolean indicating whether the `HTML5` tech currently supports automatic media resize
23074   * when going into fullscreen.
23075   *
23076   * @type {boolean}
23077   * @default
23078   */
23079
23080  Html5.prototype.featuresFullscreenResize = true;
23081  /**
23082   * Boolean indicating whether the `HTML5` tech currently supports the progress event.
23083   * If this is false, manual `progress` events will be triggered instead.
23084   *
23085   * @type {boolean}
23086   * @default
23087   */
23088
23089  Html5.prototype.featuresProgressEvents = true;
23090  /**
23091   * Boolean indicating whether the `HTML5` tech currently supports the timeupdate event.
23092   * If this is false, manual `timeupdate` events will be triggered instead.
23093   *
23094   * @default
23095   */
23096
23097  Html5.prototype.featuresTimeupdateEvents = true;
23098  /**
23099   * Boolean indicating whether the `HTML5` tech currently supports native `TextTrack`s.
23100   *
23101   * @type {boolean}
23102   * @default {@link Html5.supportsNativeTextTracks}
23103   */
23104
23105  Html5.prototype.featuresNativeTextTracks = Html5.supportsNativeTextTracks();
23106  /**
23107   * Boolean indicating whether the `HTML5` tech currently supports native `VideoTrack`s.
23108   *
23109   * @type {boolean}
23110   * @default {@link Html5.supportsNativeVideoTracks}
23111   */
23112
23113  Html5.prototype.featuresNativeVideoTracks = Html5.supportsNativeVideoTracks();
23114  /**
23115   * Boolean indicating whether the `HTML5` tech currently supports native `AudioTrack`s.
23116   *
23117   * @type {boolean}
23118   * @default {@link Html5.supportsNativeAudioTracks}
23119   */
23120
23121  Html5.prototype.featuresNativeAudioTracks = Html5.supportsNativeAudioTracks(); // HTML5 Feature detection and Device Fixes --------------------------------- //
23122
23123  var canPlayType = Html5.TEST_VID && Html5.TEST_VID.constructor.prototype.canPlayType;
23124  var mpegurlRE = /^application\/(?:x-|vnd\.apple\.)mpegurl/i;
23125
23126  Html5.patchCanPlayType = function () {
23127    // Android 4.0 and above can play HLS to some extent but it reports being unable to do so
23128    // Firefox and Chrome report correctly
23129    if (ANDROID_VERSION >= 4.0 && !IS_FIREFOX && !IS_CHROME) {
23130      Html5.TEST_VID.constructor.prototype.canPlayType = function (type) {
23131        if (type && mpegurlRE.test(type)) {
23132          return 'maybe';
23133        }
23134
23135        return canPlayType.call(this, type);
23136      };
23137    }
23138  };
23139
23140  Html5.unpatchCanPlayType = function () {
23141    var r = Html5.TEST_VID.constructor.prototype.canPlayType;
23142    Html5.TEST_VID.constructor.prototype.canPlayType = canPlayType;
23143    return r;
23144  }; // by default, patch the media element
23145
23146
23147  Html5.patchCanPlayType();
23148
23149  Html5.disposeMediaElement = function (el) {
23150    if (!el) {
23151      return;
23152    }
23153
23154    if (el.parentNode) {
23155      el.parentNode.removeChild(el);
23156    } // remove any child track or source nodes to prevent their loading
23157
23158
23159    while (el.hasChildNodes()) {
23160      el.removeChild(el.firstChild);
23161    } // remove any src reference. not setting `src=''` because that causes a warning
23162    // in firefox
23163
23164
23165    el.removeAttribute('src'); // force the media element to update its loading state by calling load()
23166    // however IE on Windows 7N has a bug that throws an error so need a try/catch (#793)
23167
23168    if (typeof el.load === 'function') {
23169      // wrapping in an iife so it's not deoptimized (#1060#discussion_r10324473)
23170      (function () {
23171        try {
23172          el.load();
23173        } catch (e) {// not supported
23174        }
23175      })();
23176    }
23177  };
23178
23179  Html5.resetMediaElement = function (el) {
23180    if (!el) {
23181      return;
23182    }
23183
23184    var sources = el.querySelectorAll('source');
23185    var i = sources.length;
23186
23187    while (i--) {
23188      el.removeChild(sources[i]);
23189    } // remove any src reference.
23190    // not setting `src=''` because that throws an error
23191
23192
23193    el.removeAttribute('src');
23194
23195    if (typeof el.load === 'function') {
23196      // wrapping in an iife so it's not deoptimized (#1060#discussion_r10324473)
23197      (function () {
23198        try {
23199          el.load();
23200        } catch (e) {// satisfy linter
23201        }
23202      })();
23203    }
23204  };
23205  /* Native HTML5 element property wrapping ----------------------------------- */
23206  // Wrap native boolean attributes with getters that check both property and attribute
23207  // The list is as followed:
23208  // muted, defaultMuted, autoplay, controls, loop, playsinline
23209
23210
23211  [
23212  /**
23213   * Get the value of `muted` from the media element. `muted` indicates
23214   * that the volume for the media should be set to silent. This does not actually change
23215   * the `volume` attribute.
23216   *
23217   * @method Html5#muted
23218   * @return {boolean}
23219   *         - True if the value of `volume` should be ignored and the audio set to silent.
23220   *         - False if the value of `volume` should be used.
23221   *
23222   * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-media-muted}
23223   */
23224  'muted',
23225  /**
23226   * Get the value of `defaultMuted` from the media element. `defaultMuted` indicates
23227   * whether the media should start muted or not. Only changes the default state of the
23228   * media. `muted` and `defaultMuted` can have different values. {@link Html5#muted} indicates the
23229   * current state.
23230   *
23231   * @method Html5#defaultMuted
23232   * @return {boolean}
23233   *         - The value of `defaultMuted` from the media element.
23234   *         - True indicates that the media should start muted.
23235   *         - False indicates that the media should not start muted
23236   *
23237   * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-media-defaultmuted}
23238   */
23239  'defaultMuted',
23240  /**
23241   * Get the value of `autoplay` from the media element. `autoplay` indicates
23242   * that the media should start to play as soon as the page is ready.
23243   *
23244   * @method Html5#autoplay
23245   * @return {boolean}
23246   *         - The value of `autoplay` from the media element.
23247   *         - True indicates that the media should start as soon as the page loads.
23248   *         - False indicates that the media should not start as soon as the page loads.
23249   *
23250   * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#attr-media-autoplay}
23251   */
23252  'autoplay',
23253  /**
23254   * Get the value of `controls` from the media element. `controls` indicates
23255   * whether the native media controls should be shown or hidden.
23256   *
23257   * @method Html5#controls
23258   * @return {boolean}
23259   *         - The value of `controls` from the media element.
23260   *         - True indicates that native controls should be showing.
23261   *         - False indicates that native controls should be hidden.
23262   *
23263   * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#attr-media-controls}
23264   */
23265  'controls',
23266  /**
23267   * Get the value of `loop` from the media element. `loop` indicates
23268   * that the media should return to the start of the media and continue playing once
23269   * it reaches the end.
23270   *
23271   * @method Html5#loop
23272   * @return {boolean}
23273   *         - The value of `loop` from the media element.
23274   *         - True indicates that playback should seek back to start once
23275   *           the end of a media is reached.
23276   *         - False indicates that playback should not loop back to the start when the
23277   *           end of the media is reached.
23278   *
23279   * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#attr-media-loop}
23280   */
23281  'loop',
23282  /**
23283   * Get the value of `playsinline` from the media element. `playsinline` indicates
23284   * to the browser that non-fullscreen playback is preferred when fullscreen
23285   * playback is the native default, such as in iOS Safari.
23286   *
23287   * @method Html5#playsinline
23288   * @return {boolean}
23289   *         - The value of `playsinline` from the media element.
23290   *         - True indicates that the media should play inline.
23291   *         - False indicates that the media should not play inline.
23292   *
23293   * @see [Spec]{@link https://html.spec.whatwg.org/#attr-video-playsinline}
23294   */
23295  'playsinline'].forEach(function (prop) {
23296    Html5.prototype[prop] = function () {
23297      return this.el_[prop] || this.el_.hasAttribute(prop);
23298    };
23299  }); // Wrap native boolean attributes with setters that set both property and attribute
23300  // The list is as followed:
23301  // setMuted, setDefaultMuted, setAutoplay, setLoop, setPlaysinline
23302  // setControls is special-cased above
23303
23304  [
23305  /**
23306   * Set the value of `muted` on the media element. `muted` indicates that the current
23307   * audio level should be silent.
23308   *
23309   * @method Html5#setMuted
23310   * @param {boolean} muted
23311   *        - True if the audio should be set to silent
23312   *        - False otherwise
23313   *
23314   * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-media-muted}
23315   */
23316  'muted',
23317  /**
23318   * Set the value of `defaultMuted` on the media element. `defaultMuted` indicates that the current
23319   * audio level should be silent, but will only effect the muted level on intial playback..
23320   *
23321   * @method Html5.prototype.setDefaultMuted
23322   * @param {boolean} defaultMuted
23323   *        - True if the audio should be set to silent
23324   *        - False otherwise
23325   *
23326   * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-media-defaultmuted}
23327   */
23328  'defaultMuted',
23329  /**
23330   * Set the value of `autoplay` on the media element. `autoplay` indicates
23331   * that the media should start to play as soon as the page is ready.
23332   *
23333   * @method Html5#setAutoplay
23334   * @param {boolean} autoplay
23335   *         - True indicates that the media should start as soon as the page loads.
23336   *         - False indicates that the media should not start as soon as the page loads.
23337   *
23338   * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#attr-media-autoplay}
23339   */
23340  'autoplay',
23341  /**
23342   * Set the value of `loop` on the media element. `loop` indicates
23343   * that the media should return to the start of the media and continue playing once
23344   * it reaches the end.
23345   *
23346   * @method Html5#setLoop
23347   * @param {boolean} loop
23348   *         - True indicates that playback should seek back to start once
23349   *           the end of a media is reached.
23350   *         - False indicates that playback should not loop back to the start when the
23351   *           end of the media is reached.
23352   *
23353   * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#attr-media-loop}
23354   */
23355  'loop',
23356  /**
23357   * Set the value of `playsinline` from the media element. `playsinline` indicates
23358   * to the browser that non-fullscreen playback is preferred when fullscreen
23359   * playback is the native default, such as in iOS Safari.
23360   *
23361   * @method Html5#setPlaysinline
23362   * @param {boolean} playsinline
23363   *         - True indicates that the media should play inline.
23364   *         - False indicates that the media should not play inline.
23365   *
23366   * @see [Spec]{@link https://html.spec.whatwg.org/#attr-video-playsinline}
23367   */
23368  'playsinline'].forEach(function (prop) {
23369    Html5.prototype['set' + toTitleCase(prop)] = function (v) {
23370      this.el_[prop] = v;
23371
23372      if (v) {
23373        this.el_.setAttribute(prop, prop);
23374      } else {
23375        this.el_.removeAttribute(prop);
23376      }
23377    };
23378  }); // Wrap native properties with a getter
23379  // The list is as followed
23380  // paused, currentTime, buffered, volume, poster, preload, error, seeking
23381  // seekable, ended, playbackRate, defaultPlaybackRate, played, networkState
23382  // readyState, videoWidth, videoHeight
23383
23384  [
23385  /**
23386   * Get the value of `paused` from the media element. `paused` indicates whether the media element
23387   * is currently paused or not.
23388   *
23389   * @method Html5#paused
23390   * @return {boolean}
23391   *         The value of `paused` from the media element.
23392   *
23393   * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-media-paused}
23394   */
23395  'paused',
23396  /**
23397   * Get the value of `currentTime` from the media element. `currentTime` indicates
23398   * the current second that the media is at in playback.
23399   *
23400   * @method Html5#currentTime
23401   * @return {number}
23402   *         The value of `currentTime` from the media element.
23403   *
23404   * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-media-currenttime}
23405   */
23406  'currentTime',
23407  /**
23408   * Get the value of `buffered` from the media element. `buffered` is a `TimeRange`
23409   * object that represents the parts of the media that are already downloaded and
23410   * available for playback.
23411   *
23412   * @method Html5#buffered
23413   * @return {TimeRange}
23414   *         The value of `buffered` from the media element.
23415   *
23416   * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-media-buffered}
23417   */
23418  'buffered',
23419  /**
23420   * Get the value of `volume` from the media element. `volume` indicates
23421   * the current playback volume of audio for a media. `volume` will be a value from 0
23422   * (silent) to 1 (loudest and default).
23423   *
23424   * @method Html5#volume
23425   * @return {number}
23426   *         The value of `volume` from the media element. Value will be between 0-1.
23427   *
23428   * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-a-volume}
23429   */
23430  'volume',
23431  /**
23432   * Get the value of `poster` from the media element. `poster` indicates
23433   * that the url of an image file that can/will be shown when no media data is available.
23434   *
23435   * @method Html5#poster
23436   * @return {string}
23437   *         The value of `poster` from the media element. Value will be a url to an
23438   *         image.
23439   *
23440   * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#attr-video-poster}
23441   */
23442  'poster',
23443  /**
23444   * Get the value of `preload` from the media element. `preload` indicates
23445   * what should download before the media is interacted with. It can have the following
23446   * values:
23447   * - none: nothing should be downloaded
23448   * - metadata: poster and the first few frames of the media may be downloaded to get
23449   *   media dimensions and other metadata
23450   * - auto: allow the media and metadata for the media to be downloaded before
23451   *    interaction
23452   *
23453   * @method Html5#preload
23454   * @return {string}
23455   *         The value of `preload` from the media element. Will be 'none', 'metadata',
23456   *         or 'auto'.
23457   *
23458   * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#attr-media-preload}
23459   */
23460  'preload',
23461  /**
23462   * Get the value of the `error` from the media element. `error` indicates any
23463   * MediaError that may have occurred during playback. If error returns null there is no
23464   * current error.
23465   *
23466   * @method Html5#error
23467   * @return {MediaError|null}
23468   *         The value of `error` from the media element. Will be `MediaError` if there
23469   *         is a current error and null otherwise.
23470   *
23471   * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-media-error}
23472   */
23473  'error',
23474  /**
23475   * Get the value of `seeking` from the media element. `seeking` indicates whether the
23476   * media is currently seeking to a new position or not.
23477   *
23478   * @method Html5#seeking
23479   * @return {boolean}
23480   *         - The value of `seeking` from the media element.
23481   *         - True indicates that the media is currently seeking to a new position.
23482   *         - False indicates that the media is not seeking to a new position at this time.
23483   *
23484   * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-media-seeking}
23485   */
23486  'seeking',
23487  /**
23488   * Get the value of `seekable` from the media element. `seekable` returns a
23489   * `TimeRange` object indicating ranges of time that can currently be `seeked` to.
23490   *
23491   * @method Html5#seekable
23492   * @return {TimeRange}
23493   *         The value of `seekable` from the media element. A `TimeRange` object
23494   *         indicating the current ranges of time that can be seeked to.
23495   *
23496   * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-media-seekable}
23497   */
23498  'seekable',
23499  /**
23500   * Get the value of `ended` from the media element. `ended` indicates whether
23501   * the media has reached the end or not.
23502   *
23503   * @method Html5#ended
23504   * @return {boolean}
23505   *         - The value of `ended` from the media element.
23506   *         - True indicates that the media has ended.
23507   *         - False indicates that the media has not ended.
23508   *
23509   * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-media-ended}
23510   */
23511  'ended',
23512  /**
23513   * Get the value of `playbackRate` from the media element. `playbackRate` indicates
23514   * the rate at which the media is currently playing back. Examples:
23515   *   - if playbackRate is set to 2, media will play twice as fast.
23516   *   - if playbackRate is set to 0.5, media will play half as fast.
23517   *
23518   * @method Html5#playbackRate
23519   * @return {number}
23520   *         The value of `playbackRate` from the media element. A number indicating
23521   *         the current playback speed of the media, where 1 is normal speed.
23522   *
23523   * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-media-playbackrate}
23524   */
23525  'playbackRate',
23526  /**
23527   * Get the value of `defaultPlaybackRate` from the media element. `defaultPlaybackRate` indicates
23528   * the rate at which the media is currently playing back. This value will not indicate the current
23529   * `playbackRate` after playback has started, use {@link Html5#playbackRate} for that.
23530   *
23531   * Examples:
23532   *   - if defaultPlaybackRate is set to 2, media will play twice as fast.
23533   *   - if defaultPlaybackRate is set to 0.5, media will play half as fast.
23534   *
23535   * @method Html5.prototype.defaultPlaybackRate
23536   * @return {number}
23537   *         The value of `defaultPlaybackRate` from the media element. A number indicating
23538   *         the current playback speed of the media, where 1 is normal speed.
23539   *
23540   * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-media-playbackrate}
23541   */
23542  'defaultPlaybackRate',
23543  /**
23544   * Get the value of `played` from the media element. `played` returns a `TimeRange`
23545   * object representing points in the media timeline that have been played.
23546   *
23547   * @method Html5#played
23548   * @return {TimeRange}
23549   *         The value of `played` from the media element. A `TimeRange` object indicating
23550   *         the ranges of time that have been played.
23551   *
23552   * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-media-played}
23553   */
23554  'played',
23555  /**
23556   * Get the value of `networkState` from the media element. `networkState` indicates
23557   * the current network state. It returns an enumeration from the following list:
23558   * - 0: NETWORK_EMPTY
23559   * - 1: NETWORK_IDLE
23560   * - 2: NETWORK_LOADING
23561   * - 3: NETWORK_NO_SOURCE
23562   *
23563   * @method Html5#networkState
23564   * @return {number}
23565   *         The value of `networkState` from the media element. This will be a number
23566   *         from the list in the description.
23567   *
23568   * @see [Spec] {@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-media-networkstate}
23569   */
23570  'networkState',
23571  /**
23572   * Get the value of `readyState` from the media element. `readyState` indicates
23573   * the current state of the media element. It returns an enumeration from the
23574   * following list:
23575   * - 0: HAVE_NOTHING
23576   * - 1: HAVE_METADATA
23577   * - 2: HAVE_CURRENT_DATA
23578   * - 3: HAVE_FUTURE_DATA
23579   * - 4: HAVE_ENOUGH_DATA
23580   *
23581   * @method Html5#readyState
23582   * @return {number}
23583   *         The value of `readyState` from the media element. This will be a number
23584   *         from the list in the description.
23585   *
23586   * @see [Spec] {@link https://www.w3.org/TR/html5/embedded-content-0.html#ready-states}
23587   */
23588  'readyState',
23589  /**
23590   * Get the value of `videoWidth` from the video element. `videoWidth` indicates
23591   * the current width of the video in css pixels.
23592   *
23593   * @method Html5#videoWidth
23594   * @return {number}
23595   *         The value of `videoWidth` from the video element. This will be a number
23596   *         in css pixels.
23597   *
23598   * @see [Spec] {@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-video-videowidth}
23599   */
23600  'videoWidth',
23601  /**
23602   * Get the value of `videoHeight` from the video element. `videoHeight` indicates
23603   * the current height of the video in css pixels.
23604   *
23605   * @method Html5#videoHeight
23606   * @return {number}
23607   *         The value of `videoHeight` from the video element. This will be a number
23608   *         in css pixels.
23609   *
23610   * @see [Spec] {@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-video-videowidth}
23611   */
23612  'videoHeight'].forEach(function (prop) {
23613    Html5.prototype[prop] = function () {
23614      return this.el_[prop];
23615    };
23616  }); // Wrap native properties with a setter in this format:
23617  // set + toTitleCase(name)
23618  // The list is as follows:
23619  // setVolume, setSrc, setPoster, setPreload, setPlaybackRate, setDefaultPlaybackRate
23620
23621  [
23622  /**
23623   * Set the value of `volume` on the media element. `volume` indicates the current
23624   * audio level as a percentage in decimal form. This means that 1 is 100%, 0.5 is 50%, and
23625   * so on.
23626   *
23627   * @method Html5#setVolume
23628   * @param {number} percentAsDecimal
23629   *        The volume percent as a decimal. Valid range is from 0-1.
23630   *
23631   * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-a-volume}
23632   */
23633  'volume',
23634  /**
23635   * Set the value of `src` on the media element. `src` indicates the current
23636   * {@link Tech~SourceObject} for the media.
23637   *
23638   * @method Html5#setSrc
23639   * @param {Tech~SourceObject} src
23640   *        The source object to set as the current source.
23641   *
23642   * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-media-src}
23643   */
23644  'src',
23645  /**
23646   * Set the value of `poster` on the media element. `poster` is the url to
23647   * an image file that can/will be shown when no media data is available.
23648   *
23649   * @method Html5#setPoster
23650   * @param {string} poster
23651   *        The url to an image that should be used as the `poster` for the media
23652   *        element.
23653   *
23654   * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#attr-media-poster}
23655   */
23656  'poster',
23657  /**
23658   * Set the value of `preload` on the media element. `preload` indicates
23659   * what should download before the media is interacted with. It can have the following
23660   * values:
23661   * - none: nothing should be downloaded
23662   * - metadata: poster and the first few frames of the media may be downloaded to get
23663   *   media dimensions and other metadata
23664   * - auto: allow the media and metadata for the media to be downloaded before
23665   *    interaction
23666   *
23667   * @method Html5#setPreload
23668   * @param {string} preload
23669   *         The value of `preload` to set on the media element. Must be 'none', 'metadata',
23670   *         or 'auto'.
23671   *
23672   * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#attr-media-preload}
23673   */
23674  'preload',
23675  /**
23676   * Set the value of `playbackRate` on the media element. `playbackRate` indicates
23677   * the rate at which the media should play back. Examples:
23678   *   - if playbackRate is set to 2, media will play twice as fast.
23679   *   - if playbackRate is set to 0.5, media will play half as fast.
23680   *
23681   * @method Html5#setPlaybackRate
23682   * @return {number}
23683   *         The value of `playbackRate` from the media element. A number indicating
23684   *         the current playback speed of the media, where 1 is normal speed.
23685   *
23686   * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-media-playbackrate}
23687   */
23688  'playbackRate',
23689  /**
23690   * Set the value of `defaultPlaybackRate` on the media element. `defaultPlaybackRate` indicates
23691   * the rate at which the media should play back upon initial startup. Changing this value
23692   * after a video has started will do nothing. Instead you should used {@link Html5#setPlaybackRate}.
23693   *
23694   * Example Values:
23695   *   - if playbackRate is set to 2, media will play twice as fast.
23696   *   - if playbackRate is set to 0.5, media will play half as fast.
23697   *
23698   * @method Html5.prototype.setDefaultPlaybackRate
23699   * @return {number}
23700   *         The value of `defaultPlaybackRate` from the media element. A number indicating
23701   *         the current playback speed of the media, where 1 is normal speed.
23702   *
23703   * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-media-defaultplaybackrate}
23704   */
23705  'defaultPlaybackRate'].forEach(function (prop) {
23706    Html5.prototype['set' + toTitleCase(prop)] = function (v) {
23707      this.el_[prop] = v;
23708    };
23709  }); // wrap native functions with a function
23710  // The list is as follows:
23711  // pause, load, play
23712
23713  [
23714  /**
23715   * A wrapper around the media elements `pause` function. This will call the `HTML5`
23716   * media elements `pause` function.
23717   *
23718   * @method Html5#pause
23719   * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-media-pause}
23720   */
23721  'pause',
23722  /**
23723   * A wrapper around the media elements `load` function. This will call the `HTML5`s
23724   * media element `load` function.
23725   *
23726   * @method Html5#load
23727   * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-media-load}
23728   */
23729  'load',
23730  /**
23731   * A wrapper around the media elements `play` function. This will call the `HTML5`s
23732   * media element `play` function.
23733   *
23734   * @method Html5#play
23735   * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-media-play}
23736   */
23737  'play'].forEach(function (prop) {
23738    Html5.prototype[prop] = function () {
23739      return this.el_[prop]();
23740    };
23741  });
23742  Tech.withSourceHandlers(Html5);
23743  /**
23744   * Native source handler for Html5, simply passes the source to the media element.
23745   *
23746   * @property {Tech~SourceObject} source
23747   *        The source object
23748   *
23749   * @property {Html5} tech
23750   *        The instance of the HTML5 tech.
23751   */
23752
23753  Html5.nativeSourceHandler = {};
23754  /**
23755   * Check if the media element can play the given mime type.
23756   *
23757   * @param {string} type
23758   *        The mimetype to check
23759   *
23760   * @return {string}
23761   *         'probably', 'maybe', or '' (empty string)
23762   */
23763
23764  Html5.nativeSourceHandler.canPlayType = function (type) {
23765    // IE without MediaPlayer throws an error (#519)
23766    try {
23767      return Html5.TEST_VID.canPlayType(type);
23768    } catch (e) {
23769      return '';
23770    }
23771  };
23772  /**
23773   * Check if the media element can handle a source natively.
23774   *
23775   * @param {Tech~SourceObject} source
23776   *         The source object
23777   *
23778   * @param {Object} [options]
23779   *         Options to be passed to the tech.
23780   *
23781   * @return {string}
23782   *         'probably', 'maybe', or '' (empty string).
23783   */
23784
23785
23786  Html5.nativeSourceHandler.canHandleSource = function (source, options) {
23787    // If a type was provided we should rely on that
23788    if (source.type) {
23789      return Html5.nativeSourceHandler.canPlayType(source.type); // If no type, fall back to checking 'video/[EXTENSION]'
23790    } else if (source.src) {
23791      var ext = getFileExtension(source.src);
23792      return Html5.nativeSourceHandler.canPlayType("video/" + ext);
23793    }
23794
23795    return '';
23796  };
23797  /**
23798   * Pass the source to the native media element.
23799   *
23800   * @param {Tech~SourceObject} source
23801   *        The source object
23802   *
23803   * @param {Html5} tech
23804   *        The instance of the Html5 tech
23805   *
23806   * @param {Object} [options]
23807   *        The options to pass to the source
23808   */
23809
23810
23811  Html5.nativeSourceHandler.handleSource = function (source, tech, options) {
23812    tech.setSrc(source.src);
23813  };
23814  /**
23815   * A noop for the native dispose function, as cleanup is not needed.
23816   */
23817
23818
23819  Html5.nativeSourceHandler.dispose = function () {}; // Register the native source handler
23820
23821
23822  Html5.registerSourceHandler(Html5.nativeSourceHandler);
23823  Tech.registerTech('Html5', Html5);
23824
23825  // on the player when they happen
23826
23827  var TECH_EVENTS_RETRIGGER = [
23828  /**
23829   * Fired while the user agent is downloading media data.
23830   *
23831   * @event Player#progress
23832   * @type {EventTarget~Event}
23833   */
23834
23835  /**
23836   * Retrigger the `progress` event that was triggered by the {@link Tech}.
23837   *
23838   * @private
23839   * @method Player#handleTechProgress_
23840   * @fires Player#progress
23841   * @listens Tech#progress
23842   */
23843  'progress',
23844  /**
23845   * Fires when the loading of an audio/video is aborted.
23846   *
23847   * @event Player#abort
23848   * @type {EventTarget~Event}
23849   */
23850
23851  /**
23852   * Retrigger the `abort` event that was triggered by the {@link Tech}.
23853   *
23854   * @private
23855   * @method Player#handleTechAbort_
23856   * @fires Player#abort
23857   * @listens Tech#abort
23858   */
23859  'abort',
23860  /**
23861   * Fires when the browser is intentionally not getting media data.
23862   *
23863   * @event Player#suspend
23864   * @type {EventTarget~Event}
23865   */
23866
23867  /**
23868   * Retrigger the `suspend` event that was triggered by the {@link Tech}.
23869   *
23870   * @private
23871   * @method Player#handleTechSuspend_
23872   * @fires Player#suspend
23873   * @listens Tech#suspend
23874   */
23875  'suspend',
23876  /**
23877   * Fires when the current playlist is empty.
23878   *
23879   * @event Player#emptied
23880   * @type {EventTarget~Event}
23881   */
23882
23883  /**
23884   * Retrigger the `emptied` event that was triggered by the {@link Tech}.
23885   *
23886   * @private
23887   * @method Player#handleTechEmptied_
23888   * @fires Player#emptied
23889   * @listens Tech#emptied
23890   */
23891  'emptied',
23892  /**
23893   * Fires when the browser is trying to get media data, but data is not available.
23894   *
23895   * @event Player#stalled
23896   * @type {EventTarget~Event}
23897   */
23898
23899  /**
23900   * Retrigger the `stalled` event that was triggered by the {@link Tech}.
23901   *
23902   * @private
23903   * @method Player#handleTechStalled_
23904   * @fires Player#stalled
23905   * @listens Tech#stalled
23906   */
23907  'stalled',
23908  /**
23909   * Fires when the browser has loaded meta data for the audio/video.
23910   *
23911   * @event Player#loadedmetadata
23912   * @type {EventTarget~Event}
23913   */
23914
23915  /**
23916   * Retrigger the `stalled` event that was triggered by the {@link Tech}.
23917   *
23918   * @private
23919   * @method Player#handleTechLoadedmetadata_
23920   * @fires Player#loadedmetadata
23921   * @listens Tech#loadedmetadata
23922   */
23923  'loadedmetadata',
23924  /**
23925   * Fires when the browser has loaded the current frame of the audio/video.
23926   *
23927   * @event Player#loadeddata
23928   * @type {event}
23929   */
23930
23931  /**
23932   * Retrigger the `loadeddata` event that was triggered by the {@link Tech}.
23933   *
23934   * @private
23935   * @method Player#handleTechLoaddeddata_
23936   * @fires Player#loadeddata
23937   * @listens Tech#loadeddata
23938   */
23939  'loadeddata',
23940  /**
23941   * Fires when the current playback position has changed.
23942   *
23943   * @event Player#timeupdate
23944   * @type {event}
23945   */
23946
23947  /**
23948   * Retrigger the `timeupdate` event that was triggered by the {@link Tech}.
23949   *
23950   * @private
23951   * @method Player#handleTechTimeUpdate_
23952   * @fires Player#timeupdate
23953   * @listens Tech#timeupdate
23954   */
23955  'timeupdate',
23956  /**
23957   * Fires when the video's intrinsic dimensions change
23958   *
23959   * @event Player#resize
23960   * @type {event}
23961   */
23962
23963  /**
23964   * Retrigger the `resize` event that was triggered by the {@link Tech}.
23965   *
23966   * @private
23967   * @method Player#handleTechResize_
23968   * @fires Player#resize
23969   * @listens Tech#resize
23970   */
23971  'resize',
vendor: 24,112 bytes, lines 23972-24701
23972  /**
23973   * Fires when the volume has been changed
23974   *
23975   * @event Player#volumechange
23976   * @type {event}
23977   */
23978
23979  /**
23980   * Retrigger the `volumechange` event that was triggered by the {@link Tech}.
23981   *
23982   * @private
23983   * @method Player#handleTechVolumechange_
23984   * @fires Player#volumechange
23985   * @listens Tech#volumechange
23986   */
23987  'volumechange',
23988  /**
23989   * Fires when the text track has been changed
23990   *
23991   * @event Player#texttrackchange
23992   * @type {event}
23993   */
23994
23995  /**
23996   * Retrigger the `texttrackchange` event that was triggered by the {@link Tech}.
23997   *
23998   * @private
23999   * @method Player#handleTechTexttrackchange_
24000   * @fires Player#texttrackchange
24001   * @listens Tech#texttrackchange
24002   */
24003  'texttrackchange']; // events to queue when playback rate is zero
24004  // this is a hash for the sole purpose of mapping non-camel-cased event names
24005  // to camel-cased function names
24006
24007  var TECH_EVENTS_QUEUE = {
24008    canplay: 'CanPlay',
24009    canplaythrough: 'CanPlayThrough',
24010    playing: 'Playing',
24011    seeked: 'Seeked'
24012  };
24013  var BREAKPOINT_ORDER = ['tiny', 'xsmall', 'small', 'medium', 'large', 'xlarge', 'huge'];
24014  var BREAKPOINT_CLASSES = {}; // grep: vjs-layout-tiny
24015  // grep: vjs-layout-x-small
24016  // grep: vjs-layout-small
24017  // grep: vjs-layout-medium
24018  // grep: vjs-layout-large
24019  // grep: vjs-layout-x-large
24020  // grep: vjs-layout-huge
24021
24022  BREAKPOINT_ORDER.forEach(function (k) {
24023    var v = k.charAt(0) === 'x' ? "x-" + k.substring(1) : k;
24024    BREAKPOINT_CLASSES[k] = "vjs-layout-" + v;
24025  });
24026  var DEFAULT_BREAKPOINTS = {
24027    tiny: 210,
24028    xsmall: 320,
24029    small: 425,
24030    medium: 768,
24031    large: 1440,
24032    xlarge: 2560,
24033    huge: Infinity
24034  };
24035  /**
24036   * An instance of the `Player` class is created when any of the Video.js setup methods
24037   * are used to initialize a video.
24038   *
24039   * After an instance has been created it can be accessed globally in two ways:
24040   * 1. By calling `videojs('example_video_1');`
24041   * 2. By using it directly via  `videojs.players.example_video_1;`
24042   *
24043   * @extends Component
24044   */
24045
24046  var Player =
24047  /*#__PURE__*/
24048  function (_Component) {
24049    _inheritsLoose(Player, _Component);
24050
24051    /**
24052     * Create an instance of this class.
24053     *
24054     * @param {Element} tag
24055     *        The original video DOM element used for configuring options.
24056     *
24057     * @param {Object} [options]
24058     *        Object of option names and values.
24059     *
24060     * @param {Component~ReadyCallback} [ready]
24061     *        Ready callback function.
24062     */
24063    function Player(tag, options, ready) {
24064      var _this;
24065
24066      // Make sure tag ID exists
24067      tag.id = tag.id || options.id || "vjs_video_" + newGUID(); // Set Options
24068      // The options argument overrides options set in the video tag
24069      // which overrides globally set options.
24070      // This latter part coincides with the load order
24071      // (tag must exist before Player)
24072
24073      options = assign(Player.getTagSettings(tag), options); // Delay the initialization of children because we need to set up
24074      // player properties first, and can't use `this` before `super()`
24075
24076      options.initChildren = false; // Same with creating the element
24077
24078      options.createEl = false; // don't auto mixin the evented mixin
24079
24080      options.evented = false; // we don't want the player to report touch activity on itself
24081      // see enableTouchActivity in Component
24082
24083      options.reportTouchActivity = false; // If language is not set, get the closest lang attribute
24084
24085      if (!options.language) {
24086        if (typeof tag.closest === 'function') {
24087          var closest = tag.closest('[lang]');
24088
24089          if (closest && closest.getAttribute) {
24090            options.language = closest.getAttribute('lang');
24091          }
24092        } else {
24093          var element = tag;
24094
24095          while (element && element.nodeType === 1) {
24096            if (getAttributes(element).hasOwnProperty('lang')) {
24097              options.language = element.getAttribute('lang');
24098              break;
24099            }
24100
24101            element = element.parentNode;
24102          }
24103        }
24104      } // Run base component initializing with new options
24105
24106
24107      _this = _Component.call(this, null, options, ready) || this; // Create bound methods for document listeners.
24108
24109      _this.boundDocumentFullscreenChange_ = bind(_assertThisInitialized(_this), _this.documentFullscreenChange_);
24110      _this.boundFullWindowOnEscKey_ = bind(_assertThisInitialized(_this), _this.fullWindowOnEscKey); // create logger
24111
24112      _this.log = createLogger$1(_this.id_); // Hold our own reference to fullscreen api so it can be mocked in tests
24113
24114      _this.fsApi_ = FullscreenApi; // Tracks when a tech changes the poster
24115
24116      _this.isPosterFromTech_ = false; // Holds callback info that gets queued when playback rate is zero
24117      // and a seek is happening
24118
24119      _this.queuedCallbacks_ = []; // Turn off API access because we're loading a new tech that might load asynchronously
24120
24121      _this.isReady_ = false; // Init state hasStarted_
24122
24123      _this.hasStarted_ = false; // Init state userActive_
24124
24125      _this.userActive_ = false; // if the global option object was accidentally blown away by
24126      // someone, bail early with an informative error
24127
24128      if (!_this.options_ || !_this.options_.techOrder || !_this.options_.techOrder.length) {
24129        throw new Error('No techOrder specified. Did you overwrite ' + 'videojs.options instead of just changing the ' + 'properties you want to override?');
24130      } // Store the original tag used to set options
24131
24132
24133      _this.tag = tag; // Store the tag attributes used to restore html5 element
24134
24135      _this.tagAttributes = tag && getAttributes(tag); // Update current language
24136
24137      _this.language(_this.options_.language); // Update Supported Languages
24138
24139
24140      if (options.languages) {
24141        // Normalise player option languages to lowercase
24142        var languagesToLower = {};
24143        Object.getOwnPropertyNames(options.languages).forEach(function (name) {
24144          languagesToLower[name.toLowerCase()] = options.languages[name];
24145        });
24146        _this.languages_ = languagesToLower;
24147      } else {
24148        _this.languages_ = Player.prototype.options_.languages;
24149      }
24150
24151      _this.resetCache_(); // Set poster
24152
24153
24154      _this.poster_ = options.poster || ''; // Set controls
24155
24156      _this.controls_ = !!options.controls; // Original tag settings stored in options
24157      // now remove immediately so native controls don't flash.
24158      // May be turned back on by HTML5 tech if nativeControlsForTouch is true
24159
24160      tag.controls = false;
24161      tag.removeAttribute('controls');
24162      _this.changingSrc_ = false;
24163      _this.playCallbacks_ = [];
24164      _this.playTerminatedQueue_ = []; // the attribute overrides the option
24165
24166      if (tag.hasAttribute('autoplay')) {
24167        _this.autoplay(true);
24168      } else {
24169        // otherwise use the setter to validate and
24170        // set the correct value.
24171        _this.autoplay(_this.options_.autoplay);
24172      } // check plugins
24173
24174
24175      if (options.plugins) {
24176        Object.keys(options.plugins).forEach(function (name) {
24177          if (typeof _this[name] !== 'function') {
24178            throw new Error("plugin \"" + name + "\" does not exist");
24179          }
24180        });
24181      }
24182      /*
24183       * Store the internal state of scrubbing
24184       *
24185       * @private
24186       * @return {Boolean} True if the user is scrubbing
24187       */
24188
24189
24190      _this.scrubbing_ = false;
24191      _this.el_ = _this.createEl(); // Make this an evented object and use `el_` as its event bus.
24192
24193      evented(_assertThisInitialized(_this), {
24194        eventBusKey: 'el_'
24195      });
24196
24197      if (_this.fluid_) {
24198        _this.on('playerreset', _this.updateStyleEl_);
24199      } // We also want to pass the original player options to each component and plugin
24200      // as well so they don't need to reach back into the player for options later.
24201      // We also need to do another copy of this.options_ so we don't end up with
24202      // an infinite loop.
24203
24204
24205      var playerOptionsCopy = mergeOptions(_this.options_); // Load plugins
24206
24207      if (options.plugins) {
24208        Object.keys(options.plugins).forEach(function (name) {
24209          _this[name](options.plugins[name]);
24210        });
24211      }
24212
24213      _this.options_.playerOptions = playerOptionsCopy;
24214      _this.middleware_ = [];
24215
24216      _this.initChildren(); // Set isAudio based on whether or not an audio tag was used
24217
24218
24219      _this.isAudio(tag.nodeName.toLowerCase() === 'audio'); // Update controls className. Can't do this when the controls are initially
24220      // set because the element doesn't exist yet.
24221
24222
24223      if (_this.controls()) {
24224        _this.addClass('vjs-controls-enabled');
24225      } else {
24226        _this.addClass('vjs-controls-disabled');
24227      } // Set ARIA label and region role depending on player type
24228
24229
24230      _this.el_.setAttribute('role', 'region');
24231
24232      if (_this.isAudio()) {
24233        _this.el_.setAttribute('aria-label', _this.localize('Audio Player'));
24234      } else {
24235        _this.el_.setAttribute('aria-label', _this.localize('Video Player'));
24236      }
24237
24238      if (_this.isAudio()) {
24239        _this.addClass('vjs-audio');
24240      }
24241
24242      if (_this.flexNotSupported_()) {
24243        _this.addClass('vjs-no-flex');
24244      } // TODO: Make this smarter. Toggle user state between touching/mousing
24245      // using events, since devices can have both touch and mouse events.
24246      // TODO: Make this check be performed again when the window switches between monitors
24247      // (See https://github.com/videojs/video.js/issues/5683)
24248
24249
24250      if (TOUCH_ENABLED) {
24251        _this.addClass('vjs-touch-enabled');
24252      } // iOS Safari has broken hover handling
24253
24254
24255      if (!IS_IOS) {
24256        _this.addClass('vjs-workinghover');
24257      } // Make player easily findable by ID
24258
24259
24260      Player.players[_this.id_] = _assertThisInitialized(_this); // Add a major version class to aid css in plugins
24261
24262      var majorVersion = version.split('.')[0];
24263
24264      _this.addClass("vjs-v" + majorVersion); // When the player is first initialized, trigger activity so components
24265      // like the control bar show themselves if needed
24266
24267
24268      _this.userActive(true);
24269
24270      _this.reportUserActivity();
24271
24272      _this.one('play', _this.listenForUserActivity_);
24273
24274      _this.on('stageclick', _this.handleStageClick_);
24275
24276      _this.on('keydown', _this.handleKeyDown);
24277
24278      _this.breakpoints(_this.options_.breakpoints);
24279
24280      _this.responsive(_this.options_.responsive);
24281
24282      return _this;
24283    }
24284    /**
24285     * Destroys the video player and does any necessary cleanup.
24286     *
24287     * This is especially helpful if you are dynamically adding and removing videos
24288     * to/from the DOM.
24289     *
24290     * @fires Player#dispose
24291     */
24292
24293
24294    var _proto = Player.prototype;
24295
24296    _proto.dispose = function dispose() {
24297      var _this2 = this;
24298
24299      /**
24300       * Called when the player is being disposed of.
24301       *
24302       * @event Player#dispose
24303       * @type {EventTarget~Event}
24304       */
24305      this.trigger('dispose'); // prevent dispose from being called twice
24306
24307      this.off('dispose'); // Make sure all player-specific document listeners are unbound. This is
24308
24309      off(document, this.fsApi_.fullscreenchange, this.boundDocumentFullscreenChange_);
24310      off(document, 'keydown', this.boundFullWindowOnEscKey_);
24311
24312      if (this.styleEl_ && this.styleEl_.parentNode) {
24313        this.styleEl_.parentNode.removeChild(this.styleEl_);
24314        this.styleEl_ = null;
24315      } // Kill reference to this player
24316
24317
24318      Player.players[this.id_] = null;
24319
24320      if (this.tag && this.tag.player) {
24321        this.tag.player = null;
24322      }
24323
24324      if (this.el_ && this.el_.player) {
24325        this.el_.player = null;
24326      }
24327
24328      if (this.tech_) {
24329        this.tech_.dispose();
24330        this.isPosterFromTech_ = false;
24331        this.poster_ = '';
24332      }
24333
24334      if (this.playerElIngest_) {
24335        this.playerElIngest_ = null;
24336      }
24337
24338      if (this.tag) {
24339        this.tag = null;
24340      }
24341
24342      clearCacheForPlayer(this); // remove all event handlers for track lists
24343      // all tracks and track listeners are removed on
24344      // tech dispose
24345
24346      ALL.names.forEach(function (name) {
24347        var props = ALL[name];
24348
24349        var list = _this2[props.getterName](); // if it is not a native list
24350        // we have to manually remove event listeners
24351
24352
24353        if (list && list.off) {
24354          list.off();
24355        }
24356      }); // the actual .el_ is removed here
24357
24358      _Component.prototype.dispose.call(this);
24359    }
24360    /**
24361     * Create the `Player`'s DOM element.
24362     *
24363     * @return {Element}
24364     *         The DOM element that gets created.
24365     */
24366    ;
24367
24368    _proto.createEl = function createEl() {
24369      var tag = this.tag;
24370      var el;
24371      var playerElIngest = this.playerElIngest_ = tag.parentNode && tag.parentNode.hasAttribute && tag.parentNode.hasAttribute('data-vjs-player');
24372      var divEmbed = this.tag.tagName.toLowerCase() === 'video-js';
24373
24374      if (playerElIngest) {
24375        el = this.el_ = tag.parentNode;
24376      } else if (!divEmbed) {
24377        el = this.el_ = _Component.prototype.createEl.call(this, 'div');
24378      } // Copy over all the attributes from the tag, including ID and class
24379      // ID will now reference player box, not the video tag
24380
24381
24382      var attrs = getAttributes(tag);
24383
24384      if (divEmbed) {
24385        el = this.el_ = tag;
24386        tag = this.tag = document.createElement('video');
24387
24388        while (el.children.length) {
24389          tag.appendChild(el.firstChild);
24390        }
24391
24392        if (!hasClass(el, 'video-js')) {
24393          addClass(el, 'video-js');
24394        }
24395
24396        el.appendChild(tag);
24397        playerElIngest = this.playerElIngest_ = el; // move properties over from our custom `video-js` element
24398        // to our new `video` element. This will move things like
24399        // `src` or `controls` that were set via js before the player
24400        // was initialized.
24401
24402        Object.keys(el).forEach(function (k) {
24403          tag[k] = el[k];
24404        });
24405      } // set tabindex to -1 to remove the video element from the focus order
24406
24407
24408      tag.setAttribute('tabindex', '-1');
24409      attrs.tabindex = '-1'; // Workaround for #4583 (JAWS+IE doesn't announce BPB or play button), and
24410      // for the same issue with Chrome (on Windows) with JAWS.
24411      // See https://github.com/FreedomScientific/VFO-standards-support/issues/78
24412      // Note that we can't detect if JAWS is being used, but this ARIA attribute
24413      //  doesn't change behavior of IE11 or Chrome if JAWS is not being used
24414
24415      if (IE_VERSION || IS_CHROME && IS_WINDOWS) {
24416        tag.setAttribute('role', 'application');
24417        attrs.role = 'application';
24418      } // Remove width/height attrs from tag so CSS can make it 100% width/height
24419
24420
24421      tag.removeAttribute('width');
24422      tag.removeAttribute('height');
24423
24424      if ('width' in attrs) {
24425        delete attrs.width;
24426      }
24427
24428      if ('height' in attrs) {
24429        delete attrs.height;
24430      }
24431
24432      Object.getOwnPropertyNames(attrs).forEach(function (attr) {
24433        // don't copy over the class attribute to the player element when we're in a div embed
24434        // the class is already set up properly in the divEmbed case
24435        // and we want to make sure that the `video-js` class doesn't get lost
24436        if (!(divEmbed && attr === 'class')) {
24437          el.setAttribute(attr, attrs[attr]);
24438        }
24439
24440        if (divEmbed) {
24441          tag.setAttribute(attr, attrs[attr]);
24442        }
24443      }); // Update tag id/class for use as HTML5 playback tech
24444      // Might think we should do this after embedding in container so .vjs-tech class
24445      // doesn't flash 100% width/height, but class only applies with .video-js parent
24446
24447      tag.playerId = tag.id;
24448      tag.id += '_html5_api';
24449      tag.className = 'vjs-tech'; // Make player findable on elements
24450
24451      tag.player = el.player = this; // Default state of video is paused
24452
24453      this.addClass('vjs-paused'); // Add a style element in the player that we'll use to set the width/height
24454      // of the player in a way that's still overrideable by CSS, just like the
24455      // video element
24456
24457      if (window$1.VIDEOJS_NO_DYNAMIC_STYLE !== true) {
24458        this.styleEl_ = createStyleElement('vjs-styles-dimensions');
24459        var defaultsStyleEl = $('.vjs-styles-defaults');
24460        var head = $('head');
24461        head.insertBefore(this.styleEl_, defaultsStyleEl ? defaultsStyleEl.nextSibling : head.firstChild);
24462      }
24463
24464      this.fill_ = false;
24465      this.fluid_ = false; // Pass in the width/height/aspectRatio options which will update the style el
24466
24467      this.width(this.options_.width);
24468      this.height(this.options_.height);
24469      this.fill(this.options_.fill);
24470      this.fluid(this.options_.fluid);
24471      this.aspectRatio(this.options_.aspectRatio); // Hide any links within the video/audio tag,
24472      // because IE doesn't hide them completely from screen readers.
24473
24474      var links = tag.getElementsByTagName('a');
24475
24476      for (var i = 0; i < links.length; i++) {
24477        var linkEl = links.item(i);
24478        addClass(linkEl, 'vjs-hidden');
24479        linkEl.setAttribute('hidden', 'hidden');
24480      } // insertElFirst seems to cause the networkState to flicker from 3 to 2, so
24481      // keep track of the original for later so we can know if the source originally failed
24482
24483
24484      tag.initNetworkState_ = tag.networkState; // Wrap video tag in div (el/box) container
24485
24486      if (tag.parentNode && !playerElIngest) {
24487        tag.parentNode.insertBefore(el, tag);
24488      } // insert the tag as the first child of the player element
24489      // then manually add it to the children array so that this.addChild
24490      // will work properly for other components
24491      //
24492      // Breaks iPhone, fixed in HTML5 setup.
24493
24494
24495      prependTo(tag, el);
24496      this.children_.unshift(tag); // Set lang attr on player to ensure CSS :lang() in consistent with player
24497      // if it's been set to something different to the doc
24498
24499      this.el_.setAttribute('lang', this.language_);
24500      this.el_ = el;
24501      return el;
24502    }
24503    /**
24504     * A getter/setter for the `Player`'s width. Returns the player's configured value.
24505     * To get the current width use `currentWidth()`.
24506     *
24507     * @param {number} [value]
24508     *        The value to set the `Player`'s width to.
24509     *
24510     * @return {number}
24511     *         The current width of the `Player` when getting.
24512     */
24513    ;
24514
24515    _proto.width = function width(value) {
24516      return this.dimension('width', value);
24517    }
24518    /**
24519     * A getter/setter for the `Player`'s height. Returns the player's configured value.
24520     * To get the current height use `currentheight()`.
24521     *
24522     * @param {number} [value]
24523     *        The value to set the `Player`'s heigth to.
24524     *
24525     * @return {number}
24526     *         The current height of the `Player` when getting.
24527     */
24528    ;
24529
24530    _proto.height = function height(value) {
24531      return this.dimension('height', value);
24532    }
24533    /**
24534     * A getter/setter for the `Player`'s width & height.
24535     *
24536     * @param {string} dimension
24537     *        This string can be:
24538     *        - 'width'
24539     *        - 'height'
24540     *
24541     * @param {number} [value]
24542     *        Value for dimension specified in the first argument.
24543     *
24544     * @return {number}
24545     *         The dimension arguments value when getting (width/height).
24546     */
24547    ;
24548
24549    _proto.dimension = function dimension(_dimension, value) {
24550      var privDimension = _dimension + '_';
24551
24552      if (value === undefined) {
24553        return this[privDimension] || 0;
24554      }
24555
24556      if (value === '') {
24557        // If an empty string is given, reset the dimension to be automatic
24558        this[privDimension] = undefined;
24559        this.updateStyleEl_();
24560        return;
24561      }
24562
24563      var parsedVal = parseFloat(value);
24564
24565      if (isNaN(parsedVal)) {
24566        log.error("Improper value \"" + value + "\" supplied for for " + _dimension);
24567        return;
24568      }
24569
24570      this[privDimension] = parsedVal;
24571      this.updateStyleEl_();
24572    }
24573    /**
24574     * A getter/setter/toggler for the vjs-fluid `className` on the `Player`.
24575     *
24576     * Turning this on will turn off fill mode.
24577     *
24578     * @param {boolean} [bool]
24579     *        - A value of true adds the class.
24580     *        - A value of false removes the class.
24581     *        - No value will be a getter.
24582     *
24583     * @return {boolean|undefined}
24584     *         - The value of fluid when getting.
24585     *         - `undefined` when setting.
24586     */
24587    ;
24588
24589    _proto.fluid = function fluid(bool) {
24590      if (bool === undefined) {
24591        return !!this.fluid_;
24592      }
24593
24594      this.fluid_ = !!bool;
24595
24596      if (isEvented(this)) {
24597        this.off('playerreset', this.updateStyleEl_);
24598      }
24599
24600      if (bool) {
24601        this.addClass('vjs-fluid');
24602        this.fill(false);
24603        addEventedCallback(function () {
24604          this.on('playerreset', this.updateStyleEl_);
24605        });
24606      } else {
24607        this.removeClass('vjs-fluid');
24608      }
24609
24610      this.updateStyleEl_();
24611    }
24612    /**
24613     * A getter/setter/toggler for the vjs-fill `className` on the `Player`.
24614     *
24615     * Turning this on will turn off fluid mode.
24616     *
24617     * @param {boolean} [bool]
24618     *        - A value of true adds the class.
24619     *        - A value of false removes the class.
24620     *        - No value will be a getter.
24621     *
24622     * @return {boolean|undefined}
24623     *         - The value of fluid when getting.
24624     *         - `undefined` when setting.
24625     */
24626    ;
24627
24628    _proto.fill = function fill(bool) {
24629      if (bool === undefined) {
24630        return !!this.fill_;
24631      }
24632
24633      this.fill_ = !!bool;
24634
24635      if (bool) {
24636        this.addClass('vjs-fill');
24637        this.fluid(false);
24638      } else {
24639        this.removeClass('vjs-fill');
24640      }
24641    }
24642    /**
24643     * Get/Set the aspect ratio
24644     *
24645     * @param {string} [ratio]
24646     *        Aspect ratio for player
24647     *
24648     * @return {string|undefined}
24649     *         returns the current aspect ratio when getting
24650     */
24651
24652    /**
24653     * A getter/setter for the `Player`'s aspect ratio.
24654     *
24655     * @param {string} [ratio]
24656     *        The value to set the `Player's aspect ratio to.
24657     *
24658     * @return {string|undefined}
24659     *         - The current aspect ratio of the `Player` when getting.
24660     *         - undefined when setting
24661     */
24662    ;
24663
24664    _proto.aspectRatio = function aspectRatio(ratio) {
24665      if (ratio === undefined) {
24666        return this.aspectRatio_;
24667      } // Check for width:height format
24668
24669
24670      if (!/^\d+\:\d+$/.test(ratio)) {
24671        throw new Error('Improper value supplied for aspect ratio. The format should be width:height, for example 16:9.');
24672      }
24673
24674      this.aspectRatio_ = ratio; // We're assuming if you set an aspect ratio you want fluid mode,
24675      // because in fixed mode you could calculate width and height yourself.
24676
24677      this.fluid(true);
24678      this.updateStyleEl_();
24679    }
24680    /**
24681     * Update styles of the `Player` element (height, width and aspect ratio).
24682     *
24683     * @private
24684     * @listens Tech#loadedmetadata
24685     */
24686    ;
24687
24688    _proto.updateStyleEl_ = function updateStyleEl_() {
24689      if (window$1.VIDEOJS_NO_DYNAMIC_STYLE === true) {
24690        var _width = typeof this.width_ === 'number' ? this.width_ : this.options_.width;
24691
24692        var _height = typeof this.height_ === 'number' ? this.height_ : this.options_.height;
24693
24694        var techEl = this.tech_ && this.tech_.el();
24695
24696        if (techEl) {
24697          if (_width >= 0) {
24698            techEl.width = _width;
24699          }
24700
24701          if (_height >= 0) {
vendor: 9,471 bytes, lines 24702-24937
24702            techEl.height = _height;
24703          }
24704        }
24705
24706        return;
24707      }
24708
24709      var width;
24710      var height;
24711      var aspectRatio;
24712      var idClass; // The aspect ratio is either used directly or to calculate width and height.
24713
24714      if (this.aspectRatio_ !== undefined && this.aspectRatio_ !== 'auto') {
24715        // Use any aspectRatio that's been specifically set
24716        aspectRatio = this.aspectRatio_;
24717      } else if (this.videoWidth() > 0) {
24718        // Otherwise try to get the aspect ratio from the video metadata
24719        aspectRatio = this.videoWidth() + ':' + this.videoHeight();
24720      } else {
24721        // Or use a default. The video element's is 2:1, but 16:9 is more common.
24722        aspectRatio = '16:9';
24723      } // Get the ratio as a decimal we can use to calculate dimensions
24724
24725
24726      var ratioParts = aspectRatio.split(':');
24727      var ratioMultiplier = ratioParts[1] / ratioParts[0];
24728
24729      if (this.width_ !== undefined) {
24730        // Use any width that's been specifically set
24731        width = this.width_;
24732      } else if (this.height_ !== undefined) {
24733        // Or calulate the width from the aspect ratio if a height has been set
24734        width = this.height_ / ratioMultiplier;
24735      } else {
24736        // Or use the video's metadata, or use the video el's default of 300
24737        width = this.videoWidth() || 300;
24738      }
24739
24740      if (this.height_ !== undefined) {
24741        // Use any height that's been specifically set
24742        height = this.height_;
24743      } else {
24744        // Otherwise calculate the height from the ratio and the width
24745        height = width * ratioMultiplier;
24746      } // Ensure the CSS class is valid by starting with an alpha character
24747
24748
24749      if (/^[^a-zA-Z]/.test(this.id())) {
24750        idClass = 'dimensions-' + this.id();
24751      } else {
24752        idClass = this.id() + '-dimensions';
24753      } // Ensure the right class is still on the player for the style element
24754
24755
24756      this.addClass(idClass);
24757      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    ");
24758    }
24759    /**
24760     * Load/Create an instance of playback {@link Tech} including element
24761     * and API methods. Then append the `Tech` element in `Player` as a child.
24762     *
24763     * @param {string} techName
24764     *        name of the playback technology
24765     *
24766     * @param {string} source
24767     *        video source
24768     *
24769     * @private
24770     */
24771    ;
24772
24773    _proto.loadTech_ = function loadTech_(techName, source) {
24774      var _this3 = this;
24775
24776      // Pause and remove current playback technology
24777      if (this.tech_) {
24778        this.unloadTech_();
24779      }
24780
24781      var titleTechName = toTitleCase(techName);
24782      var camelTechName = techName.charAt(0).toLowerCase() + techName.slice(1); // get rid of the HTML5 video tag as soon as we are using another tech
24783
24784      if (titleTechName !== 'Html5' && this.tag) {
24785        Tech.getTech('Html5').disposeMediaElement(this.tag);
24786        this.tag.player = null;
24787        this.tag = null;
24788      }
24789
24790      this.techName_ = titleTechName; // Turn off API access because we're loading a new tech that might load asynchronously
24791
24792      this.isReady_ = false; // if autoplay is a string we pass false to the tech
24793      // because the player is going to handle autoplay on `loadstart`
24794
24795      var autoplay = typeof this.autoplay() === 'string' ? false : this.autoplay(); // Grab tech-specific options from player options and add source and parent element to use.
24796
24797      var techOptions = {
24798        source: source,
24799        autoplay: autoplay,
24800        'nativeControlsForTouch': this.options_.nativeControlsForTouch,
24801        'playerId': this.id(),
24802        'techId': this.id() + "_" + camelTechName + "_api",
24803        'playsinline': this.options_.playsinline,
24804        'preload': this.options_.preload,
24805        'loop': this.options_.loop,
24806        'muted': this.options_.muted,
24807        'poster': this.poster(),
24808        'language': this.language(),
24809        'playerElIngest': this.playerElIngest_ || false,
24810        'vtt.js': this.options_['vtt.js'],
24811        'canOverridePoster': !!this.options_.techCanOverridePoster,
24812        'enableSourceset': this.options_.enableSourceset,
24813        'Promise': this.options_.Promise
24814      };
24815      ALL.names.forEach(function (name) {
24816        var props = ALL[name];
24817        techOptions[props.getterName] = _this3[props.privateName];
24818      });
24819      assign(techOptions, this.options_[titleTechName]);
24820      assign(techOptions, this.options_[camelTechName]);
24821      assign(techOptions, this.options_[techName.toLowerCase()]);
24822
24823      if (this.tag) {
24824        techOptions.tag = this.tag;
24825      }
24826
24827      if (source && source.src === this.cache_.src && this.cache_.currentTime > 0) {
24828        techOptions.startTime = this.cache_.currentTime;
24829      } // Initialize tech instance
24830
24831
24832      var TechClass = Tech.getTech(techName);
24833
24834      if (!TechClass) {
24835        throw new Error("No Tech named '" + titleTechName + "' exists! '" + titleTechName + "' should be registered using videojs.registerTech()'");
24836      }
24837
24838      this.tech_ = new TechClass(techOptions); // player.triggerReady is always async, so don't need this to be async
24839
24840      this.tech_.ready(bind(this, this.handleTechReady_), true);
24841      textTrackConverter.jsonToTextTracks(this.textTracksJson_ || [], this.tech_); // Listen to all HTML5-defined events and trigger them on the player
24842
24843      TECH_EVENTS_RETRIGGER.forEach(function (event) {
24844        _this3.on(_this3.tech_, event, _this3["handleTech" + toTitleCase(event) + "_"]);
24845      });
24846      Object.keys(TECH_EVENTS_QUEUE).forEach(function (event) {
24847        _this3.on(_this3.tech_, event, function (eventObj) {
24848          if (_this3.tech_.playbackRate() === 0 && _this3.tech_.seeking()) {
24849            _this3.queuedCallbacks_.push({
24850              callback: _this3["handleTech" + TECH_EVENTS_QUEUE[event] + "_"].bind(_this3),
24851              event: eventObj
24852            });
24853
24854            return;
24855          }
24856
24857          _this3["handleTech" + TECH_EVENTS_QUEUE[event] + "_"](eventObj);
24858        });
24859      });
24860      this.on(this.tech_, 'loadstart', this.handleTechLoadStart_);
24861      this.on(this.tech_, 'sourceset', this.handleTechSourceset_);
24862      this.on(this.tech_, 'waiting', this.handleTechWaiting_);
24863      this.on(this.tech_, 'ended', this.handleTechEnded_);
24864      this.on(this.tech_, 'seeking', this.handleTechSeeking_);
24865      this.on(this.tech_, 'play', this.handleTechPlay_);
24866      this.on(this.tech_, 'firstplay', this.handleTechFirstPlay_);
24867      this.on(this.tech_, 'pause', this.handleTechPause_);
24868      this.on(this.tech_, 'durationchange', this.handleTechDurationChange_);
24869      this.on(this.tech_, 'fullscreenchange', this.handleTechFullscreenChange_);
24870      this.on(this.tech_, 'enterpictureinpicture', this.handleTechEnterPictureInPicture_);
24871      this.on(this.tech_, 'leavepictureinpicture', this.handleTechLeavePictureInPicture_);
24872      this.on(this.tech_, 'error', this.handleTechError_);
24873      this.on(this.tech_, 'loadedmetadata', this.updateStyleEl_);
24874      this.on(this.tech_, 'posterchange', this.handleTechPosterChange_);
24875      this.on(this.tech_, 'textdata', this.handleTechTextData_);
24876      this.on(this.tech_, 'ratechange', this.handleTechRateChange_);
24877      this.usingNativeControls(this.techGet_('controls'));
24878
24879      if (this.controls() && !this.usingNativeControls()) {
24880        this.addTechControlsListeners_();
24881      } // Add the tech element in the DOM if it was not already there
24882      // Make sure to not insert the original video element if using Html5
24883
24884
24885      if (this.tech_.el().parentNode !== this.el() && (titleTechName !== 'Html5' || !this.tag)) {
24886        prependTo(this.tech_.el(), this.el());
24887      } // Get rid of the original video tag reference after the first tech is loaded
24888
24889
24890      if (this.tag) {
24891        this.tag.player = null;
24892        this.tag = null;
24893      }
24894    }
24895    /**
24896     * Unload and dispose of the current playback {@link Tech}.
24897     *
24898     * @private
24899     */
24900    ;
24901
24902    _proto.unloadTech_ = function unloadTech_() {
24903      var _this4 = this;
24904
24905      // Save the current text tracks so that we can reuse the same text tracks with the next tech
24906      ALL.names.forEach(function (name) {
24907        var props = ALL[name];
24908        _this4[props.privateName] = _this4[props.getterName]();
24909      });
24910      this.textTracksJson_ = textTrackConverter.textTracksToJson(this.tech_);
24911      this.isReady_ = false;
24912      this.tech_.dispose();
24913      this.tech_ = false;
24914
24915      if (this.isPosterFromTech_) {
24916        this.poster_ = '';
24917        this.trigger('posterchange');
24918      }
24919
24920      this.isPosterFromTech_ = false;
24921    }
24922    /**
24923     * Return a reference to the current {@link Tech}.
24924     * It will print a warning by default about the danger of using the tech directly
24925     * but any argument that is passed in will silence the warning.
24926     *
24927     * @param {*} [safety]
24928     *        Anything passed in to silence the warning
24929     *
24930     * @return {Tech}
24931     *         The Tech
24932     */
24933    ;
24934
24935    _proto.tech = function tech(safety) {
24936      if (safety === undefined) {
24937        log.warn('Using the tech directly can be dangerous. I hope you know 
vendor: 9,281 bytes, lines 24937-25198
24937what you\'re doing.\n' + 'See https://github.com/videojs/video.js/issues/2617 for more info.\n');
24938      }
24939
24940      return this.tech_;
24941    }
24942    /**
24943     * Set up click and touch listeners for the playback element
24944     *
24945     * - On desktops: a click on the video itself will toggle playback
24946     * - On mobile devices: a click on the video toggles controls
24947     *   which is done by toggling the user state between active and
24948     *   inactive
24949     * - A tap can signal that a user has become active or has become inactive
24950     *   e.g. a quick tap on an iPhone movie should reveal the controls. Another
24951     *   quick tap should hide them again (signaling the user is in an inactive
24952     *   viewing state)
24953     * - In addition to this, we still want the user to be considered inactive after
24954     *   a few seconds of inactivity.
24955     *
24956     * > Note: the only part of iOS interaction we can't mimic with this setup
24957     * is a touch and hold on the video element counting as activity in order to
24958     * keep the controls showing, but that shouldn't be an issue. A touch and hold
24959     * on any controls will still keep the user active
24960     *
24961     * @private
24962     */
24963    ;
24964
24965    _proto.addTechControlsListeners_ = function addTechControlsListeners_() {
24966      // Make sure to remove all the previous listeners in case we are called multiple times.
24967      this.removeTechControlsListeners_(); // Some browsers (Chrome & IE) don't trigger a click on a flash swf, but do
24968      // trigger mousedown/up.
24969      // http://stackoverflow.com/questions/1444562/javascript-onclick-event-over-flash-object
24970      // Any touch events are set to block the mousedown event from happening
24971
24972      this.on(this.tech_, 'mouseup', this.handleTechClick_);
24973      this.on(this.tech_, 'dblclick', this.handleTechDoubleClick_); // If the controls were hidden we don't want that to change without a tap event
24974      // so we'll check if the controls were already showing before reporting user
24975      // activity
24976
24977      this.on(this.tech_, 'touchstart', this.handleTechTouchStart_);
24978      this.on(this.tech_, 'touchmove', this.handleTechTouchMove_);
24979      this.on(this.tech_, 'touchend', this.handleTechTouchEnd_); // The tap listener needs to come after the touchend listener because the tap
24980      // listener cancels out any reportedUserActivity when setting userActive(false)
24981
24982      this.on(this.tech_, 'tap', this.handleTechTap_);
24983    }
24984    /**
24985     * Remove the listeners used for click and tap controls. This is needed for
24986     * toggling to controls disabled, where a tap/touch should do nothing.
24987     *
24988     * @private
24989     */
24990    ;
24991
24992    _proto.removeTechControlsListeners_ = function removeTechControlsListeners_() {
24993      // We don't want to just use `this.off()` because there might be other needed
24994      // listeners added by techs that extend this.
24995      this.off(this.tech_, 'tap', this.handleTechTap_);
24996      this.off(this.tech_, 'touchstart', this.handleTechTouchStart_);
24997      this.off(this.tech_, 'touchmove', this.handleTechTouchMove_);
24998      this.off(this.tech_, 'touchend', this.handleTechTouchEnd_);
24999      this.off(this.tech_, 'mouseup', this.handleTechClick_);
25000      this.off(this.tech_, 'dblclick', this.handleTechDoubleClick_);
25001    }
25002    /**
25003     * Player waits for the tech to be ready
25004     *
25005     * @private
25006     */
25007    ;
25008
25009    _proto.handleTechReady_ = function handleTechReady_() {
25010      this.triggerReady(); // Keep the same volume as before
25011
25012      if (this.cache_.volume) {
25013        this.techCall_('setVolume', this.cache_.volume);
25014      } // Look if the tech found a higher resolution poster while loading
25015
25016
25017      this.handleTechPosterChange_(); // Update the duration if available
25018
25019      this.handleTechDurationChange_();
25020    }
25021    /**
25022     * Retrigger the `loadstart` event that was triggered by the {@link Tech}. This
25023     * function will also trigger {@link Player#firstplay} if it is the first loadstart
25024     * for a video.
25025     *
25026     * @fires Player#loadstart
25027     * @fires Player#firstplay
25028     * @listens Tech#loadstart
25029     * @private
25030     */
25031    ;
25032
25033    _proto.handleTechLoadStart_ = function handleTechLoadStart_() {
25034      // TODO: Update to use `emptied` event instead. See #1277.
25035      this.removeClass('vjs-ended');
25036      this.removeClass('vjs-seeking'); // reset the error state
25037
25038      this.error(null); // Update the duration
25039
25040      this.handleTechDurationChange_(); // If it's already playing we want to trigger a firstplay event now.
25041      // The firstplay event relies on both the play and loadstart events
25042      // which can happen in any order for a new source
25043
25044      if (!this.paused()) {
25045        /**
25046         * Fired when the user agent begins looking for media data
25047         *
25048         * @event Player#loadstart
25049         * @type {EventTarget~Event}
25050         */
25051        this.trigger('loadstart');
25052        this.trigger('firstplay');
25053      } else {
25054        // reset the hasStarted state
25055        this.hasStarted(false);
25056        this.trigger('loadstart');
25057      } // autoplay happens after loadstart for the browser,
25058      // so we mimic that behavior
25059
25060
25061      this.manualAutoplay_(this.autoplay());
25062    }
25063    /**
25064     * Handle autoplay string values, rather than the typical boolean
25065     * values that should be handled by the tech. Note that this is not
25066     * part of any specification. Valid values and what they do can be
25067     * found on the autoplay getter at Player#autoplay()
25068     */
25069    ;
25070
25071    _proto.manualAutoplay_ = function manualAutoplay_(type) {
25072      var _this5 = this;
25073
25074      if (!this.tech_ || typeof type !== 'string') {
25075        return;
25076      }
25077
25078      var muted = function muted() {
25079        var previouslyMuted = _this5.muted();
25080
25081        _this5.muted(true);
25082
25083        var restoreMuted = function restoreMuted() {
25084          _this5.muted(previouslyMuted);
25085        }; // restore muted on play terminatation
25086
25087
25088        _this5.playTerminatedQueue_.push(restoreMuted);
25089
25090        var mutedPromise = _this5.play();
25091
25092        if (!isPromise(mutedPromise)) {
25093          return;
25094        }
25095
25096        return mutedPromise["catch"](restoreMuted);
25097      };
25098
25099      var promise; // if muted defaults to true
25100      // the only thing we can do is call play
25101
25102      if (type === 'any' && this.muted() !== true) {
25103        promise = this.play();
25104
25105        if (isPromise(promise)) {
25106          promise = promise["catch"](muted);
25107        }
25108      } else if (type === 'muted' && this.muted() !== true) {
25109        promise = muted();
25110      } else {
25111        promise = this.play();
25112      }
25113
25114      if (!isPromise(promise)) {
25115        return;
25116      }
25117
25118      return promise.then(function () {
25119        _this5.trigger({
25120          type: 'autoplay-success',
25121          autoplay: type
25122        });
25123      })["catch"](function (e) {
25124        _this5.trigger({
25125          type: 'autoplay-failure',
25126          autoplay: type
25127        });
25128      });
25129    }
25130    /**
25131     * Update the internal source caches so that we return the correct source from
25132     * `src()`, `currentSource()`, and `currentSources()`.
25133     *
25134     * > Note: `currentSources` will not be updated if the source that is passed in exists
25135     *         in the current `currentSources` cache.
25136     *
25137     *
25138     * @param {Tech~SourceObject} srcObj
25139     *        A string or object source to update our caches to.
25140     */
25141    ;
25142
25143    _proto.updateSourceCaches_ = function updateSourceCaches_(srcObj) {
25144      if (srcObj === void 0) {
25145        srcObj = '';
25146      }
25147
25148      var src = srcObj;
25149      var type = '';
25150
25151      if (typeof src !== 'string') {
25152        src = srcObj.src;
25153        type = srcObj.type;
25154      } // make sure all the caches are set to default values
25155      // to prevent null checking
25156
25157
25158      this.cache_.source = this.cache_.source || {};
25159      this.cache_.sources = this.cache_.sources || []; // try to get the type of the src that was passed in
25160
25161      if (src && !type) {
25162        type = findMimetype(this, src);
25163      } // update `currentSource` cache always
25164
25165
25166      this.cache_.source = mergeOptions({}, srcObj, {
25167        src: src,
25168        type: type
25169      });
25170      var matchingSources = this.cache_.sources.filter(function (s) {
25171        return s.src && s.src === src;
25172      });
25173      var sourceElSources = [];
25174      var sourceEls = this.$$('source');
25175      var matchingSourceEls = [];
25176
25177      for (var i = 0; i < sourceEls.length; i++) {
25178        var sourceObj = getAttributes(sourceEls[i]);
25179        sourceElSources.push(sourceObj);
25180
25181        if (sourceObj.src && sourceObj.src === src) {
25182          matchingSourceEls.push(sourceObj.src);
25183        }
25184      } // if we have matching source els but not matching sources
25185      // the current source cache is not up to date
25186
25187
25188      if (matchingSourceEls.length && !matchingSources.length) {
25189        this.cache_.sources = sourceElSources; // if we don't have matching source or source els set the
25190        // sources cache to the `currentSource` cache
25191      } else if (!matchingSources.length) {
25192        this.cache_.sources = [this.cache_.source];
25193      } // update the tech `src` cache
25194
25195
25196      this.cache_.src = src;
25197    }
25198    /**
25199     * *EXPERIMENTAL* Fired when the source is set or changed on the {@link Tech}
25200     * causing the media element to reload.
25201     *
25202     * It will fire for the initial source and each subsequent source.
25203     * This event is a custom event from Video.js and is triggered by the {@link Tech}.
25204     *
25205     * The event object for this event contains a `src` property that will contain the source
25206     * that was available when the event was triggered. This is generally only necessary if Video.js
25207     * is switching techs while the source was being changed.
25208     *
25209     * It is also fired when `load` is called on the player (or media element)
25210     * because the {@link https://html.spec.whatwg.org/multipage/media.html#dom-media-load|specification for `load`}
25211     * says that the resource selection algorithm needs to be aborted and restarted.
25212     * In this case, it is very likely that the `src` property will be set to the
25213     * empty string `""` to indicate we do not know what the source will be but
25214     * that it is changing.
25215     *
25216     * *This event is currently still experimental and may change in minor releases.*
25217     * __To use this, pass `enableSourceset` option to the player.__
25218     *
25219     * @event Player#sourceset
25220     * @type {EventTarget~Event}
25221     * @prop {string} src
25222     *                The source url available when the `sourceset` was triggered.
25223     *                It will be an empty string if we cannot know what the source is
25224     *                but know that the source will change.
25225     */
25226
25227    /**
25228     * Retrigger the `sourceset` event that was triggered by the {@link Tech}.
25229     *
25230     * @fires Player#sourceset
25231     * @listens Tech#sourceset
25232     * @private
25233     */
25234    ;
25235
25236    _proto.handleTechSourceset_ = function handleTechSourceset_(event) {
25237      var _this6 = this;
25238
25239      // only update the source cache when the source
25240      // was not updated using the player api
25241      if (!this.changingSrc_) {
25242        var updateSourceCaches = function updateSourceCaches(src) {
25243          return _this6.updateSourceCaches_(src);
25244        };
25245
25246        var playerSrc = this.currentSource().src;
25247        var eventSrc = event.src; // if we have a playerSrc that is not a blob, and a tech src that is a blob
25248
25249        if (playerSrc && !/^blob:/.test(playerSrc) && /^blob:/.test(eventSrc)) {
25250          // if both the tech source and the player source were updated we assume
25251          // something like @videojs/http-streaming did the sourceset and skip updating the source cache.
25252          if (!this.lastSource_ || this.lastSource_.tech !== eventSrc && this.lastSource_.player !== playerSrc) {
25253            updateSourceCaches = function updateSourceCaches() {};
25254          }
25255        } // update the source to the intial source right away
25256        // in some cases this will be empty string
25257
25258
25259        updateSourceCaches(eventSrc); // if the `sourceset` `src` was an empty string
25260        // wait for a `loadstart` to update the cache to `currentSrc`.
25261        // If a sourceset happens before a `loadstart`, we reset the state
25262
25263        if (!event.src) {
25264          this.tech_.any(['sourceset', 'loadstart'], function (e) {
25265            // if a sourceset happens before a `loadstart` there
25266            // is nothing to do as this `handleTechSourceset_`
25267            // will be called again and this will be handled there.
25268            if (e.type === 'sourceset') {
25269              return;
25270            }
25271
25272            var techSrc = _this6.techGet('currentSrc');
25273
25274            _this6.lastSource_.tech = techSrc;
25275
25276            _this6.updateSourceCaches_(techSrc);
25277          });
25278        }
25279      }
25280
25281      this.lastSource_ = {
25282        player: this.currentSource().src,
25283        tech: event.src
25284      };
25285      this.trigger({
25286        src: event.src,
25287        type: 'sourceset'
25288      });
25289    }
25290    /**
25291     * Add/remove the vjs-has-started class
25292     *
25293     * @fires Player#firstplay
25294     *
25295     * @param {boolean} request
25296     *        - true: adds the class
25297     *        - false: remove the class
25298     *
25299     * @return {boolean}
25300     *         the boolean value of hasStarted_
25301     */
25302    ;
25303
25304    _proto.hasStarted = function hasStarted(request) {
25305      if (request === undefined) {
25306        // act as getter, if we have no request to change
25307        return this.hasStarted_;
25308      }
25309
25310      if (request === this.hasStarted_) {
25311        return;
25312      }
25313
25314      this.hasStarted_ = request;
25315
25316      if (this.hasStarted_) {
25317        this.addClass('vjs-has-started');
25318        this.trigger('firstplay');
25319      } else {
25320        this.removeClass('vjs-has-started');
25321      }
25322    }
25323    /**
25324     * Fired whenever the media begins or resumes playback
25325     *
25326     * @see [Spec]{@link https://html.spec.whatwg.org/multipage/embedded-content.html#dom-media-play}
25327     * @fires Player#play
25328     * @listens Tech#play
25329     * @private
25330     */
25331    ;
25332
25333    _proto.handleTechPlay_ = function handleTechPlay_() {
25334      this.removeClass('vjs-ended');
25335      this.removeClass('vjs-paused');
25336      this.addClass('vjs-playing'); // hide the poster when the user hits play
25337
25338      this.hasStarted(true);
25339      /**
25340       * Triggered whenever an {@link Tech#play} event happens. Indicates that
25341       * playback has started or resumed.
25342       *
25343       * @event Player#play
25344       * @type {EventTarget~Event}
25345       */
25346
25347      this.trigger('play');
25348    }
25349    /**
25350     * Retrigger the `ratechange` event that was triggered by the {@link Tech}.
25351     *
25352     * If there were any events queued while the playback rate was zero, fire
25353     * those events now.
25354     *
25355     * @private
25356     * @method Player#handleTechRateChange_
25357     * @fires Player#ratechange
25358     * @listens Tech#ratechange
25359     */
25360    ;
25361
25362    _proto.handleTechRateChange_ = function handleTechRateChange_() {
25363      if (this.tech_.playbackRate() > 0 && this.cache_.lastPlaybackRate === 0) {
25364        this.queuedCallbacks_.forEach(function (queued) {
25365          return queued.callback(queued.event);
25366        });
25367        this.queuedCallbacks_ = [];
25368      }
25369
25370      this.cache_.lastPlaybackRate = this.tech_.playbackRate();
25371      /**
25372       * Fires when the playing speed of the audio/video is changed
25373       *
25374       * @event Player#ratechange
25375       * @type {event}
25376       */
25377
25378      this.trigger('ratechange');
25379    }
25380    /**
25381     * Retrigger the `waiting` event that was triggered by the {@link Tech}.
25382     *
25383     * @fires Player#waiting
25384     * @listens Tech#waiting
25385     * @private
25386     */
25387    ;
25388
25389    _proto.handleTechWaiting_ = function handleTechWaiting_() {
25390      var _this7 = this;
25391
25392      this.addClass('vjs-waiting');
25393      /**
25394       * A readyState change on the DOM element has caused playback to stop.
25395       *
25396       * @event Player#waiting
25397       * @type {EventTarget~Event}
25398       */
25399
25400      this.trigger('waiting'); // Browsers may emit a timeupdate event after a waiting event. In order to prevent
25401      // premature removal of the waiting class, wait for the time to change.
25402
25403      var timeWhenWaiting = this.currentTime();
25404
25405      var timeUpdateListener = function timeUpdateListener() {
25406        if (timeWhenWaiting !== _this7.currentTime()) {
25407          _this7.removeClass('vjs-waiting');
25408
25409          _this7.off('timeupdate', timeUpdateListener);
25410        }
25411      };
25412
25413      this.on('timeupdate', timeUpdateListener);
25414    }
25415    /**
25416     * Retrigger the `canplay` event that was triggered by the {@link Tech}.
25417     * > Note: This is not consistent between browsers. See #1351
25418     *
25419     * @fires Player#canplay
25420     * @listens Tech#canplay
25421     * @private
25422     */
25423    ;
25424
25425    _proto.handleTechCanPlay_ = function handleTechCanPlay_() {
25426      this.removeClass('vjs-waiting');
25427      /**
25428       * The media has a readyState of HAVE_FUTURE_DATA or greater.
25429       *
25430       * @event Player#canplay
25431       * @type {EventTarget~Event}
25432       */
25433
25434      this.trigger('canplay');
25435    }
25436    /**
25437     * Retrigger the `canplaythrough` event that was triggered by the {@link Tech}.
25438     *
25439     * @fires Player#canplaythrough
25440     * @listens Tech#canplaythrough
25441     * @private
25442     */
25443    ;
25444
25445    _proto.handleTechCanPlayThrough_ = function handleTechCanPlayThrough_() {
25446      this.removeClass('vjs-waiting');
25447      /**
25448       * The media has a readyState of HAVE_ENOUGH_DATA or greater. This means that the
25449       * entire media file can be played without buffering.
25450       *
25451       * @event Player#canplaythrough
25452       * @type {EventTarget~Event}
25453       */
25454
25455      this.trigger('canplaythrough');
25456    }
25457    /**
25458     * Retrigger the `playing` event that was triggered by the {@link Tech}.
25459     *
25460     * @fires Player#playing
25461     * @listens Tech#playing
25462     * @private
25463     */
25464    ;
25465
25466    _proto.handleTechPlaying_ = function handleTechPlaying_() {
25467      this.removeClass('vjs-waiting');
25468      /**
25469       * The media is no longer blocked from playback, and has started playing.
25470       *
25471       * @event Player#playing
25472       * @type {EventTarget~Event}
25473       */
25474
25475      this.trigger('playing');
25476    }
25477    /**
25478     * Retrigger the `seeking` event that was triggered by the {@link Tech}.
25479     *
25480     * @fires Player#seeking
25481     * @listens Tech#seeking
25482     * @private
25483     */
25484    ;
25485
25486    _proto.handleTechSeeking_ = function handleTechSeeking_() {
25487      this.addClass('vjs-seeking');
25488      /**
25489       * Fired whenever the player is jumping to a new time
25490       *
25491       * @event Player#seeking
25492       * @type {EventTarget~Event}
25493       */
25494
25495      this.trigger('seeking');
25496    }
25497    /**
25498     * Retrigger the `seeked` event that was triggered by the {@link Tech}.
25499     *
25500     * @fires Player#seeked
25501     * @listens Tech#seeked
25502     * @private
25503     */
25504    ;
25505
25506    _proto.handleTechSeeked_ = function handleTechSeeked_() {
25507      this.removeClass('vjs-seeking');
25508      this.removeClass('vjs-ended');
25509      /**
25510       * Fired when the player has finished jumping to a new time
25511       *
25512       * @event Player#seeked
25513       * @type {EventTarget~Event}
25514       */
25515
25516      this.trigger('seeked');
25517    }
25518    /**
25519     * Retrigger the `firstplay` event that was triggered by the {@link Tech}.
25520     *
25521     * @fires Player#firstplay
25522     * @listens Tech#firstplay
25523     * @deprecated As of 6.0 firstplay event is deprecated.
25524     *             As of 6.0 passing the `starttime` option to the player and the firstplay event are deprecated.
25525     * @private
25526     */
25527    ;
25528
25529    _proto.handleTechFirstPlay_ = function handleTechFirstPlay_() {
25530      // If the first starttime attribute is specified
25531      // then we will start at the given offset in seconds
25532      if (this.options_.starttime) {
25533        log.warn('Passing the `starttime` option to the player will be deprecated 
vendor: 44,481 bytes, lines 25533-26948
25533in 6.0');
25534        this.currentTime(this.options_.starttime);
25535      }
25536
25537      this.addClass('vjs-has-started');
25538      /**
25539       * Fired the first time a video is played. Not part of the HLS spec, and this is
25540       * probably not the best implementation yet, so use sparingly. If you don't have a
25541       * reason to prevent playback, use `myPlayer.one('play');` instead.
25542       *
25543       * @event Player#firstplay
25544       * @deprecated As of 6.0 firstplay event is deprecated.
25545       * @type {EventTarget~Event}
25546       */
25547
25548      this.trigger('firstplay');
25549    }
25550    /**
25551     * Retrigger the `pause` event that was triggered by the {@link Tech}.
25552     *
25553     * @fires Player#pause
25554     * @listens Tech#pause
25555     * @private
25556     */
25557    ;
25558
25559    _proto.handleTechPause_ = function handleTechPause_() {
25560      this.removeClass('vjs-playing');
25561      this.addClass('vjs-paused');
25562      /**
25563       * Fired whenever the media has been paused
25564       *
25565       * @event Player#pause
25566       * @type {EventTarget~Event}
25567       */
25568
25569      this.trigger('pause');
25570    }
25571    /**
25572     * Retrigger the `ended` event that was triggered by the {@link Tech}.
25573     *
25574     * @fires Player#ended
25575     * @listens Tech#ended
25576     * @private
25577     */
25578    ;
25579
25580    _proto.handleTechEnded_ = function handleTechEnded_() {
25581      this.addClass('vjs-ended');
25582
25583      if (this.options_.loop) {
25584        this.currentTime(0);
25585        this.play();
25586      } else if (!this.paused()) {
25587        this.pause();
25588      }
25589      /**
25590       * Fired when the end of the media resource is reached (currentTime == duration)
25591       *
25592       * @event Player#ended
25593       * @type {EventTarget~Event}
25594       */
25595
25596
25597      this.trigger('ended');
25598    }
25599    /**
25600     * Fired when the duration of the media resource is first known or changed
25601     *
25602     * @listens Tech#durationchange
25603     * @private
25604     */
25605    ;
25606
25607    _proto.handleTechDurationChange_ = function handleTechDurationChange_() {
25608      this.duration(this.techGet_('duration'));
25609    }
25610    /**
25611     * Handle a click on the media element to play/pause
25612     *
25613     * @param {EventTarget~Event} event
25614     *        the event that caused this function to trigger
25615     *
25616     * @listens Tech#mouseup
25617     * @private
25618     */
25619    ;
25620
25621    _proto.handleTechClick_ = function handleTechClick_(event) {
25622      if (!isSingleLeftClick(event)) {
25623        return;
25624      } // When controls are disabled a click should not toggle playback because
25625      // the click is considered a control
25626
25627
25628      if (!this.controls_) {
25629        return;
25630      }
25631
25632      if (this.paused()) {
25633        silencePromise(this.play());
25634      } else {
25635        this.pause();
25636      }
25637    }
25638    /**
25639     * Handle a double-click on the media element to enter/exit fullscreen
25640     *
25641     * @param {EventTarget~Event} event
25642     *        the event that caused this function to trigger
25643     *
25644     * @listens Tech#dblclick
25645     * @private
25646     */
25647    ;
25648
25649    _proto.handleTechDoubleClick_ = function handleTechDoubleClick_(event) {
25650      if (!this.controls_) {
25651        return;
25652      } // we do not want to toggle fullscreen state
25653      // when double-clicking inside a control bar or a modal
25654
25655
25656      var inAllowedEls = Array.prototype.some.call(this.$$('.vjs-control-bar, .vjs-modal-dialog'), function (el) {
25657        return el.contains(event.target);
25658      });
25659
25660      if (!inAllowedEls) {
25661        /*
25662         * options.userActions.doubleClick
25663         *
25664         * If `undefined` or `true`, double-click toggles fullscreen if controls are present
25665         * Set to `false` to disable double-click handling
25666         * Set to a function to substitute an external double-click handler
25667         */
25668        if (this.options_ === undefined || this.options_.userActions === undefined || this.options_.userActions.doubleClick === undefined || this.options_.userActions.doubleClick !== false) {
25669          if (this.options_ !== undefined && this.options_.userActions !== undefined && typeof this.options_.userActions.doubleClick === 'function') {
25670            this.options_.userActions.doubleClick.call(this, event);
25671          } else if (this.isFullscreen()) {
25672            this.exitFullscreen();
25673          } else {
25674            this.requestFullscreen();
25675          }
25676        }
25677      }
25678    }
25679    /**
25680     * Handle a tap on the media element. It will toggle the user
25681     * activity state, which hides and shows the controls.
25682     *
25683     * @listens Tech#tap
25684     * @private
25685     */
25686    ;
25687
25688    _proto.handleTechTap_ = function handleTechTap_() {
25689      this.userActive(!this.userActive());
25690    }
25691    /**
25692     * Handle touch to start
25693     *
25694     * @listens Tech#touchstart
25695     * @private
25696     */
25697    ;
25698
25699    _proto.handleTechTouchStart_ = function handleTechTouchStart_() {
25700      this.userWasActive = this.userActive();
25701    }
25702    /**
25703     * Handle touch to move
25704     *
25705     * @listens Tech#touchmove
25706     * @private
25707     */
25708    ;
25709
25710    _proto.handleTechTouchMove_ = function handleTechTouchMove_() {
25711      if (this.userWasActive) {
25712        this.reportUserActivity();
25713      }
25714    }
25715    /**
25716     * Handle touch to end
25717     *
25718     * @param {EventTarget~Event} event
25719     *        the touchend event that triggered
25720     *        this function
25721     *
25722     * @listens Tech#touchend
25723     * @private
25724     */
25725    ;
25726
25727    _proto.handleTechTouchEnd_ = function handleTechTouchEnd_(event) {
25728      // Stop the mouse events from also happening
25729      event.preventDefault();
25730    }
25731    /**
25732     * native click events on the SWF aren't triggered on IE11, Win8.1RT
25733     * use stageclick events triggered from inside the SWF instead
25734     *
25735     * @private
25736     * @listens stageclick
25737     */
25738    ;
25739
25740    _proto.handleStageClick_ = function handleStageClick_() {
25741      this.reportUserActivity();
25742    }
25743    /**
25744     * @private
25745     */
25746    ;
25747
25748    _proto.toggleFullscreenClass_ = function toggleFullscreenClass_() {
25749      if (this.isFullscreen()) {
25750        this.addClass('vjs-fullscreen');
25751      } else {
25752        this.removeClass('vjs-fullscreen');
25753      }
25754    }
25755    /**
25756     * when the document fschange event triggers it calls this
25757     */
25758    ;
25759
25760    _proto.documentFullscreenChange_ = function documentFullscreenChange_(e) {
25761      var el = this.el();
25762      var isFs = document[this.fsApi_.fullscreenElement] === el;
25763
25764      if (!isFs && el.matches) {
25765        isFs = el.matches(':' + this.fsApi_.fullscreen);
25766      } else if (!isFs && el.msMatchesSelector) {
25767        isFs = el.msMatchesSelector(':' + this.fsApi_.fullscreen);
25768      }
25769
25770      this.isFullscreen(isFs); // If cancelling fullscreen, remove event listener.
25771
25772      if (this.isFullscreen() === false) {
25773        off(document, this.fsApi_.fullscreenchange, this.boundDocumentFullscreenChange_);
25774      }
25775
25776      if (this.fsApi_.prefixed) {
25777        /**
25778         * @event Player#fullscreenchange
25779         * @type {EventTarget~Event}
25780         */
25781        this.trigger('fullscreenchange');
25782      }
25783    }
25784    /**
25785     * Handle Tech Fullscreen Change
25786     *
25787     * @param {EventTarget~Event} event
25788     *        the fullscreenchange event that triggered this function
25789     *
25790     * @param {Object} data
25791     *        the data that was sent with the event
25792     *
25793     * @private
25794     * @listens Tech#fullscreenchange
25795     * @fires Player#fullscreenchange
25796     */
25797    ;
25798
25799    _proto.handleTechFullscreenChange_ = function handleTechFullscreenChange_(event, data) {
25800      if (data) {
25801        this.isFullscreen(data.isFullscreen);
25802      }
25803      /**
25804       * Fired when going in and out of fullscreen.
25805       *
25806       * @event Player#fullscreenchange
25807       * @type {EventTarget~Event}
25808       */
25809
25810
25811      this.trigger('fullscreenchange');
25812    }
25813    /**
25814     * @private
25815     */
25816    ;
25817
25818    _proto.togglePictureInPictureClass_ = function togglePictureInPictureClass_() {
25819      if (this.isInPictureInPicture()) {
25820        this.addClass('vjs-picture-in-picture');
25821      } else {
25822        this.removeClass('vjs-picture-in-picture');
25823      }
25824    }
25825    /**
25826     * Handle Tech Enter Picture-in-Picture.
25827     *
25828     * @param {EventTarget~Event} event
25829     *        the enterpictureinpicture event that triggered this function
25830     *
25831     * @private
25832     * @listens Tech#enterpictureinpicture
25833     */
25834    ;
25835
25836    _proto.handleTechEnterPictureInPicture_ = function handleTechEnterPictureInPicture_(event) {
25837      this.isInPictureInPicture(true);
25838    }
25839    /**
25840     * Handle Tech Leave Picture-in-Picture.
25841     *
25842     * @param {EventTarget~Event} event
25843     *        the leavepictureinpicture event that triggered this function
25844     *
25845     * @private
25846     * @listens Tech#leavepictureinpicture
25847     */
25848    ;
25849
25850    _proto.handleTechLeavePictureInPicture_ = function handleTechLeavePictureInPicture_(event) {
25851      this.isInPictureInPicture(false);
25852    }
25853    /**
25854     * Fires when an error occurred during the loading of an audio/video.
25855     *
25856     * @private
25857     * @listens Tech#error
25858     */
25859    ;
25860
25861    _proto.handleTechError_ = function handleTechError_() {
25862      var error = this.tech_.error();
25863      this.error(error);
25864    }
25865    /**
25866     * Retrigger the `textdata` event that was triggered by the {@link Tech}.
25867     *
25868     * @fires Player#textdata
25869     * @listens Tech#textdata
25870     * @private
25871     */
25872    ;
25873
25874    _proto.handleTechTextData_ = function handleTechTextData_() {
25875      var data = null;
25876
25877      if (arguments.length > 1) {
25878        data = arguments[1];
25879      }
25880      /**
25881       * Fires when we get a textdata event from tech
25882       *
25883       * @event Player#textdata
25884       * @type {EventTarget~Event}
25885       */
25886
25887
25888      this.trigger('textdata', data);
25889    }
25890    /**
25891     * Get object for cached values.
25892     *
25893     * @return {Object}
25894     *         get the current object cache
25895     */
25896    ;
25897
25898    _proto.getCache = function getCache() {
25899      return this.cache_;
25900    }
25901    /**
25902     * Resets the internal cache object.
25903     *
25904     * Using this function outside the player constructor or reset method may
25905     * have unintended side-effects.
25906     *
25907     * @private
25908     */
25909    ;
25910
25911    _proto.resetCache_ = function resetCache_() {
25912      this.cache_ = {
25913        // Right now, the currentTime is not _really_ cached because it is always
25914        // retrieved from the tech (see: currentTime). However, for completeness,
25915        // we set it to zero here to ensure that if we do start actually caching
25916        // it, we reset it along with everything else.
25917        currentTime: 0,
25918        inactivityTimeout: this.options_.inactivityTimeout,
25919        duration: NaN,
25920        lastVolume: 1,
25921        lastPlaybackRate: this.defaultPlaybackRate(),
25922        media: null,
25923        src: '',
25924        source: {},
25925        sources: [],
25926        volume: 1
25927      };
25928    }
25929    /**
25930     * Pass values to the playback tech
25931     *
25932     * @param {string} [method]
25933     *        the method to call
25934     *
25935     * @param {Object} arg
25936     *        the argument to pass
25937     *
25938     * @private
25939     */
25940    ;
25941
25942    _proto.techCall_ = function techCall_(method, arg) {
25943      // If it's not ready yet, call method when it is
25944      this.ready(function () {
25945        if (method in allowedSetters) {
25946          return set(this.middleware_, this.tech_, method, arg);
25947        } else if (method in allowedMediators) {
25948          return mediate(this.middleware_, this.tech_, method, arg);
25949        }
25950
25951        try {
25952          if (this.tech_) {
25953            this.tech_[method](arg);
25954          }
25955        } catch (e) {
25956          log(e);
25957          throw e;
25958        }
25959      }, true);
25960    }
25961    /**
25962     * Get calls can't wait for the tech, and sometimes don't need to.
25963     *
25964     * @param {string} method
25965     *        Tech method
25966     *
25967     * @return {Function|undefined}
25968     *         the method or undefined
25969     *
25970     * @private
25971     */
25972    ;
25973
25974    _proto.techGet_ = function techGet_(method) {
25975      if (!this.tech_ || !this.tech_.isReady_) {
25976        return;
25977      }
25978
25979      if (method in allowedGetters) {
25980        return get(this.middleware_, this.tech_, method);
25981      } else if (method in allowedMediators) {
25982        return mediate(this.middleware_, this.tech_, method);
25983      } // Flash likes to die and reload when you hide or reposition it.
25984      // In these cases the object methods go away and we get errors.
25985      // When that happens we'll catch the errors and inform tech that it's not ready any more.
25986
25987
25988      try {
25989        return this.tech_[method]();
25990      } catch (e) {
25991        // When building additional tech libs, an expected method may not be defined yet
25992        if (this.tech_[method] === undefined) {
25993          log("Video.js: " + method + " method not defined for " + this.techName_ + " playback technology.", e);
25994          throw e;
25995        } // When a method isn't available on the object it throws a TypeError
25996
25997
25998        if (e.name === 'TypeError') {
25999          log("Video.js: " + method + " unavailable on " + this.techName_ + " playback technology element.", e);
26000          this.tech_.isReady_ = false;
26001          throw e;
26002        } // If error unknown, just log and throw
26003
26004
26005        log(e);
26006        throw e;
26007      }
26008    }
26009    /**
26010     * Attempt to begin playback at the first opportunity.
26011     *
26012     * @return {Promise|undefined}
26013     *         Returns a promise if the browser supports Promises (or one
26014     *         was passed in as an option). This promise will be resolved on
26015     *         the return value of play. If this is undefined it will fulfill the
26016     *         promise chain otherwise the promise chain will be fulfilled when
26017     *         the promise from play is fulfilled.
26018     */
26019    ;
26020
26021    _proto.play = function play() {
26022      var _this8 = this;
26023
26024      var PromiseClass = this.options_.Promise || window$1.Promise;
26025
26026      if (PromiseClass) {
26027        return new PromiseClass(function (resolve) {
26028          _this8.play_(resolve);
26029        });
26030      }
26031
26032      return this.play_();
26033    }
26034    /**
26035     * The actual logic for play, takes a callback that will be resolved on the
26036     * return value of play. This allows us to resolve to the play promise if there
26037     * is one on modern browsers.
26038     *
26039     * @private
26040     * @param {Function} [callback]
26041     *        The callback that should be called when the techs play is actually called
26042     */
26043    ;
26044
26045    _proto.play_ = function play_(callback) {
26046      var _this9 = this;
26047
26048      if (callback === void 0) {
26049        callback = silencePromise;
26050      }
26051
26052      this.playCallbacks_.push(callback);
26053      var isSrcReady = Boolean(!this.changingSrc_ && (this.src() || this.currentSrc())); // treat calls to play_ somewhat like the `one` event function
26054
26055      if (this.waitToPlay_) {
26056        this.off(['ready', 'loadstart'], this.waitToPlay_);
26057        this.waitToPlay_ = null;
26058      } // if the player/tech is not ready or the src itself is not ready
26059      // queue up a call to play on `ready` or `loadstart`
26060
26061
26062      if (!this.isReady_ || !isSrcReady) {
26063        this.waitToPlay_ = function (e) {
26064          _this9.play_();
26065        };
26066
26067        this.one(['ready', 'loadstart'], this.waitToPlay_); // if we are in Safari, there is a high chance that loadstart will trigger after the gesture timeperiod
26068        // in that case, we need to prime the video element by calling load so it'll be ready in time
26069
26070        if (!isSrcReady && (IS_ANY_SAFARI || IS_IOS)) {
26071          this.load();
26072        }
26073
26074        return;
26075      } // If the player/tech is ready and we have a source, we can attempt playback.
26076
26077
26078      var val = this.techGet_('play'); // play was terminated if the returned value is null
26079
26080      if (val === null) {
26081        this.runPlayTerminatedQueue_();
26082      } else {
26083        this.runPlayCallbacks_(val);
26084      }
26085    }
26086    /**
26087     * These functions will be run when if play is terminated. If play
26088     * runPlayCallbacks_ is run these function will not be run. This allows us
26089     * to differenciate between a terminated play and an actual call to play.
26090     */
26091    ;
26092
26093    _proto.runPlayTerminatedQueue_ = function runPlayTerminatedQueue_() {
26094      var queue = this.playTerminatedQueue_.slice(0);
26095      this.playTerminatedQueue_ = [];
26096      queue.forEach(function (q) {
26097        q();
26098      });
26099    }
26100    /**
26101     * When a callback to play is delayed we have to run these
26102     * callbacks when play is actually called on the tech. This function
26103     * runs the callbacks that were delayed and accepts the return value
26104     * from the tech.
26105     *
26106     * @param {undefined|Promise} val
26107     *        The return value from the tech.
26108     */
26109    ;
26110
26111    _proto.runPlayCallbacks_ = function runPlayCallbacks_(val) {
26112      var callbacks = this.playCallbacks_.slice(0);
26113      this.playCallbacks_ = []; // clear play terminatedQueue since we finished a real play
26114
26115      this.playTerminatedQueue_ = [];
26116      callbacks.forEach(function (cb) {
26117        cb(val);
26118      });
26119    }
26120    /**
26121     * Pause the video playback
26122     *
26123     * @return {Player}
26124     *         A reference to the player object this function was called on
26125     */
26126    ;
26127
26128    _proto.pause = function pause() {
26129      this.techCall_('pause');
26130    }
26131    /**
26132     * Check if the player is paused or has yet to play
26133     *
26134     * @return {boolean}
26135     *         - false: if the media is currently playing
26136     *         - true: if media is not currently playing
26137     */
26138    ;
26139
26140    _proto.paused = function paused() {
26141      // The initial state of paused should be true (in Safari it's actually false)
26142      return this.techGet_('paused') === false ? false : true;
26143    }
26144    /**
26145     * Get a TimeRange object representing the current ranges of time that the user
26146     * has played.
26147     *
26148     * @return {TimeRange}
26149     *         A time range object that represents all the increments of time that have
26150     *         been played.
26151     */
26152    ;
26153
26154    _proto.played = function played() {
26155      return this.techGet_('played') || createTimeRanges(0, 0);
26156    }
26157    /**
26158     * Returns whether or not the user is "scrubbing". Scrubbing is
26159     * when the user has clicked the progress bar handle and is
26160     * dragging it along the progress bar.
26161     *
26162     * @param {boolean} [isScrubbing]
26163     *        whether the user is or is not scrubbing
26164     *
26165     * @return {boolean}
26166     *         The value of scrubbing when getting
26167     */
26168    ;
26169
26170    _proto.scrubbing = function scrubbing(isScrubbing) {
26171      if (typeof isScrubbing === 'undefined') {
26172        return this.scrubbing_;
26173      }
26174
26175      this.scrubbing_ = !!isScrubbing;
26176
26177      if (isScrubbing) {
26178        this.addClass('vjs-scrubbing');
26179      } else {
26180        this.removeClass('vjs-scrubbing');
26181      }
26182    }
26183    /**
26184     * Get or set the current time (in seconds)
26185     *
26186     * @param {number|string} [seconds]
26187     *        The time to seek to in seconds
26188     *
26189     * @return {number}
26190     *         - the current time in seconds when getting
26191     */
26192    ;
26193
26194    _proto.currentTime = function currentTime(seconds) {
26195      if (typeof seconds !== 'undefined') {
26196        if (seconds < 0) {
26197          seconds = 0;
26198        }
26199
26200        this.techCall_('setCurrentTime', seconds);
26201        return;
26202      } // cache last currentTime and return. default to 0 seconds
26203      //
26204      // Caching the currentTime is meant to prevent a massive amount of reads on the tech's
26205      // currentTime when scrubbing, but may not provide much performance benefit afterall.
26206      // Should be tested. Also something has to read the actual current time or the cache will
26207      // never get updated.
26208
26209
26210      this.cache_.currentTime = this.techGet_('currentTime') || 0;
26211      return this.cache_.currentTime;
26212    }
26213    /**
26214     * Normally gets the length in time of the video in seconds;
26215     * in all but the rarest use cases an argument will NOT be passed to the method
26216     *
26217     * > **NOTE**: The video must have started loading before the duration can be
26218     * known, and in the case of Flash, may not be known until the video starts
26219     * playing.
26220     *
26221     * @fires Player#durationchange
26222     *
26223     * @param {number} [seconds]
26224     *        The duration of the video to set in seconds
26225     *
26226     * @return {number}
26227     *         - The duration of the video in seconds when getting
26228     */
26229    ;
26230
26231    _proto.duration = function duration(seconds) {
26232      if (seconds === undefined) {
26233        // return NaN if the duration is not known
26234        return this.cache_.duration !== undefined ? this.cache_.duration : NaN;
26235      }
26236
26237      seconds = parseFloat(seconds); // Standardize on Infinity for signaling video is live
26238
26239      if (seconds < 0) {
26240        seconds = Infinity;
26241      }
26242
26243      if (seconds !== this.cache_.duration) {
26244        // Cache the last set value for optimized scrubbing (esp. Flash)
26245        this.cache_.duration = seconds;
26246
26247        if (seconds === Infinity) {
26248          this.addClass('vjs-live');
26249
26250          if (this.options_.liveui && this.player_.liveTracker) {
26251            this.addClass('vjs-liveui');
26252          }
26253        } else {
26254          this.removeClass('vjs-live');
26255          this.removeClass('vjs-liveui');
26256        }
26257
26258        if (!isNaN(seconds)) {
26259          // Do not fire durationchange unless the duration value is known.
26260          // @see [Spec]{@link https://www.w3.org/TR/2011/WD-html5-20110113/video.html#media-element-load-algorithm}
26261
26262          /**
26263           * @event Player#durationchange
26264           * @type {EventTarget~Event}
26265           */
26266          this.trigger('durationchange');
26267        }
26268      }
26269    }
26270    /**
26271     * Calculates how much time is left in the video. Not part
26272     * of the native video API.
26273     *
26274     * @return {number}
26275     *         The time remaining in seconds
26276     */
26277    ;
26278
26279    _proto.remainingTime = function remainingTime() {
26280      return this.duration() - this.currentTime();
26281    }
26282    /**
26283     * A remaining time function that is intented to be used when
26284     * the time is to be displayed directly to the user.
26285     *
26286     * @return {number}
26287     *         The rounded time remaining in seconds
26288     */
26289    ;
26290
26291    _proto.remainingTimeDisplay = function remainingTimeDisplay() {
26292      return Math.floor(this.duration()) - Math.floor(this.currentTime());
26293    } //
26294    // Kind of like an array of portions of the video that have been downloaded.
26295
26296    /**
26297     * Get a TimeRange object with an array of the times of the video
26298     * that have been downloaded. If you just want the percent of the
26299     * video that's been downloaded, use bufferedPercent.
26300     *
26301     * @see [Buffered Spec]{@link http://dev.w3.org/html5/spec/video.html#dom-media-buffered}
26302     *
26303     * @return {TimeRange}
26304     *         A mock TimeRange object (following HTML spec)
26305     */
26306    ;
26307
26308    _proto.buffered = function buffered() {
26309      var buffered = this.techGet_('buffered');
26310
26311      if (!buffered || !buffered.length) {
26312        buffered = createTimeRanges(0, 0);
26313      }
26314
26315      return buffered;
26316    }
26317    /**
26318     * Get the percent (as a decimal) of the video that's been downloaded.
26319     * This method is not a part of the native HTML video API.
26320     *
26321     * @return {number}
26322     *         A decimal between 0 and 1 representing the percent
26323     *         that is buffered 0 being 0% and 1 being 100%
26324     */
26325    ;
26326
26327    _proto.bufferedPercent = function bufferedPercent$1() {
26328      return bufferedPercent(this.buffered(), this.duration());
26329    }
26330    /**
26331     * Get the ending time of the last buffered time range
26332     * This is used in the progress bar to encapsulate all time ranges.
26333     *
26334     * @return {number}
26335     *         The end of the last buffered time range
26336     */
26337    ;
26338
26339    _proto.bufferedEnd = function bufferedEnd() {
26340      var buffered = this.buffered();
26341      var duration = this.duration();
26342      var end = buffered.end(buffered.length - 1);
26343
26344      if (end > duration) {
26345        end = duration;
26346      }
26347
26348      return end;
26349    }
26350    /**
26351     * Get or set the current volume of the media
26352     *
26353     * @param  {number} [percentAsDecimal]
26354     *         The new volume as a decimal percent:
26355     *         - 0 is muted/0%/off
26356     *         - 1.0 is 100%/full
26357     *         - 0.5 is half volume or 50%
26358     *
26359     * @return {number}
26360     *         The current volume as a percent when getting
26361     */
26362    ;
26363
26364    _proto.volume = function volume(percentAsDecimal) {
26365      var vol;
26366
26367      if (percentAsDecimal !== undefined) {
26368        // Force value to between 0 and 1
26369        vol = Math.max(0, Math.min(1, parseFloat(percentAsDecimal)));
26370        this.cache_.volume = vol;
26371        this.techCall_('setVolume', vol);
26372
26373        if (vol > 0) {
26374          this.lastVolume_(vol);
26375        }
26376
26377        return;
26378      } // Default to 1 when returning current volume.
26379
26380
26381      vol = parseFloat(this.techGet_('volume'));
26382      return isNaN(vol) ? 1 : vol;
26383    }
26384    /**
26385     * Get the current muted state, or turn mute on or off
26386     *
26387     * @param {boolean} [muted]
26388     *        - true to mute
26389     *        - false to unmute
26390     *
26391     * @return {boolean}
26392     *         - true if mute is on and getting
26393     *         - false if mute is off and getting
26394     */
26395    ;
26396
26397    _proto.muted = function muted(_muted) {
26398      if (_muted !== undefined) {
26399        this.techCall_('setMuted', _muted);
26400        return;
26401      }
26402
26403      return this.techGet_('muted') || false;
26404    }
26405    /**
26406     * Get the current defaultMuted state, or turn defaultMuted on or off. defaultMuted
26407     * indicates the state of muted on initial playback.
26408     *
26409     * ```js
26410     *   var myPlayer = videojs('some-player-id');
26411     *
26412     *   myPlayer.src("http://www.example.com/path/to/video.mp4");
26413     *
26414     *   // get, should be false
26415     *   console.log(myPlayer.defaultMuted());
26416     *   // set to true
26417     *   myPlayer.defaultMuted(true);
26418     *   // get should be true
26419     *   console.log(myPlayer.defaultMuted());
26420     * ```
26421     *
26422     * @param {boolean} [defaultMuted]
26423     *        - true to mute
26424     *        - false to unmute
26425     *
26426     * @return {boolean|Player}
26427     *         - true if defaultMuted is on and getting
26428     *         - false if defaultMuted is off and getting
26429     *         - A reference to the current player when setting
26430     */
26431    ;
26432
26433    _proto.defaultMuted = function defaultMuted(_defaultMuted) {
26434      if (_defaultMuted !== undefined) {
26435        return this.techCall_('setDefaultMuted', _defaultMuted);
26436      }
26437
26438      return this.techGet_('defaultMuted') || false;
26439    }
26440    /**
26441     * Get the last volume, or set it
26442     *
26443     * @param  {number} [percentAsDecimal]
26444     *         The new last volume as a decimal percent:
26445     *         - 0 is muted/0%/off
26446     *         - 1.0 is 100%/full
26447     *         - 0.5 is half volume or 50%
26448     *
26449     * @return {number}
26450     *         the current value of lastVolume as a percent when getting
26451     *
26452     * @private
26453     */
26454    ;
26455
26456    _proto.lastVolume_ = function lastVolume_(percentAsDecimal) {
26457      if (percentAsDecimal !== undefined && percentAsDecimal !== 0) {
26458        this.cache_.lastVolume = percentAsDecimal;
26459        return;
26460      }
26461
26462      return this.cache_.lastVolume;
26463    }
26464    /**
26465     * Check if current tech can support native fullscreen
26466     * (e.g. with built in controls like iOS, so not our flash swf)
26467     *
26468     * @return {boolean}
26469     *         if native fullscreen is supported
26470     */
26471    ;
26472
26473    _proto.supportsFullScreen = function supportsFullScreen() {
26474      return this.techGet_('supportsFullScreen') || false;
26475    }
26476    /**
26477     * Check if the player is in fullscreen mode or tell the player that it
26478     * is or is not in fullscreen mode.
26479     *
26480     * > NOTE: As of the latest HTML5 spec, isFullscreen is no longer an official
26481     * property and instead document.fullscreenElement is used. But isFullscreen is
26482     * still a valuable property for internal player workings.
26483     *
26484     * @param  {boolean} [isFS]
26485     *         Set the players current fullscreen state
26486     *
26487     * @return {boolean}
26488     *         - true if fullscreen is on and getting
26489     *         - false if fullscreen is off and getting
26490     */
26491    ;
26492
26493    _proto.isFullscreen = function isFullscreen(isFS) {
26494      if (isFS !== undefined) {
26495        this.isFullscreen_ = !!isFS;
26496        this.toggleFullscreenClass_();
26497        return;
26498      }
26499
26500      return !!this.isFullscreen_;
26501    }
26502    /**
26503     * Increase the size of the video to full screen
26504     * In some browsers, full screen is not supported natively, so it enters
26505     * "full window mode", where the video fills the browser window.
26506     * In browsers and devices that support native full screen, sometimes the
26507     * browser's default controls will be shown, and not the Video.js custom skin.
26508     * This includes most mobile devices (iOS, Android) and older versions of
26509     * Safari.
26510     *
26511     * @param  {Object} [fullscreenOptions]
26512     *         Override the player fullscreen options
26513     *
26514     * @fires Player#fullscreenchange
26515     */
26516    ;
26517
26518    _proto.requestFullscreen = function requestFullscreen(fullscreenOptions) {
26519      var fsOptions;
26520      this.isFullscreen(true);
26521
26522      if (this.fsApi_.requestFullscreen) {
26523        // the browser supports going fullscreen at the element level so we can
26524        // take the controls fullscreen as well as the video
26525        // Trigger fullscreenchange event after change
26526        // We have to specifically add this each time, and remove
26527        // when canceling fullscreen. Otherwise if there's multiple
26528        // players on a page, they would all be reacting to the same fullscreen
26529        // events
26530        on(document, this.fsApi_.fullscreenchange, this.boundDocumentFullscreenChange_); // only pass FullscreenOptions to requestFullscreen if it isn't prefixed
26531
26532        if (!this.fsApi_.prefixed) {
26533          fsOptions = this.options_.fullscreen && this.options_.fullscreen.options || {};
26534
26535          if (fullscreenOptions !== undefined) {
26536            fsOptions = fullscreenOptions;
26537          }
26538        }
26539
26540        silencePromise(this.el_[this.fsApi_.requestFullscreen](fsOptions));
26541      } else if (this.tech_.supportsFullScreen()) {
26542        // we can't take the video.js controls fullscreen but we can go fullscreen
26543        // with native controls
26544        this.techCall_('enterFullScreen');
26545      } else {
26546        // fullscreen isn't supported so we'll just stretch the video element to
26547        // fill the viewport
26548        this.enterFullWindow();
26549        /**
26550         * @event Player#fullscreenchange
26551         * @type {EventTarget~Event}
26552         */
26553
26554        this.trigger('fullscreenchange');
26555      }
26556    }
26557    /**
26558     * Return the video to its normal size after having been in full screen mode
26559     *
26560     * @fires Player#fullscreenchange
26561     */
26562    ;
26563
26564    _proto.exitFullscreen = function exitFullscreen() {
26565      this.isFullscreen(false); // Check for browser element fullscreen support
26566
26567      if (this.fsApi_.requestFullscreen) {
26568        silencePromise(document[this.fsApi_.exitFullscreen]());
26569      } else if (this.tech_.supportsFullScreen()) {
26570        this.techCall_('exitFullScreen');
26571      } else {
26572        this.exitFullWindow();
26573        /**
26574         * @event Player#fullscreenchange
26575         * @type {EventTarget~Event}
26576         */
26577
26578        this.trigger('fullscreenchange');
26579      }
26580    }
26581    /**
26582     * When fullscreen isn't supported we can stretch the
26583     * video container to as wide as the browser will let us.
26584     *
26585     * @fires Player#enterFullWindow
26586     */
26587    ;
26588
26589    _proto.enterFullWindow = function enterFullWindow() {
26590      this.isFullWindow = true; // Storing original doc overflow value to return to when fullscreen is off
26591
26592      this.docOrigOverflow = document.documentElement.style.overflow; // Add listener for esc key to exit fullscreen
26593
26594      on(document, 'keydown', this.boundFullWindowOnEscKey_); // Hide any scroll bars
26595
26596      document.documentElement.style.overflow = 'hidden'; // Apply fullscreen styles
26597
26598      addClass(document.body, 'vjs-full-window');
26599      /**
26600       * @event Player#enterFullWindow
26601       * @type {EventTarget~Event}
26602       */
26603
26604      this.trigger('enterFullWindow');
26605    }
26606    /**
26607     * Check for call to either exit full window or
26608     * full screen on ESC key
26609     *
26610     * @param {string} event
26611     *        Event to check for key press
26612     */
26613    ;
26614
26615    _proto.fullWindowOnEscKey = function fullWindowOnEscKey(event) {
26616      if (keycode.isEventKey(event, 'Esc')) {
26617        if (this.isFullscreen() === true) {
26618          this.exitFullscreen();
26619        } else {
26620          this.exitFullWindow();
26621        }
26622      }
26623    }
26624    /**
26625     * Exit full window
26626     *
26627     * @fires Player#exitFullWindow
26628     */
26629    ;
26630
26631    _proto.exitFullWindow = function exitFullWindow() {
26632      this.isFullWindow = false;
26633      off(document, 'keydown', this.boundFullWindowOnEscKey_); // Unhide scroll bars.
26634
26635      document.documentElement.style.overflow = this.docOrigOverflow; // Remove fullscreen styles
26636
26637      removeClass(document.body, 'vjs-full-window'); // Resize the box, controller, and poster to original sizes
26638      // this.positionAll();
26639
26640      /**
26641       * @event Player#exitFullWindow
26642       * @type {EventTarget~Event}
26643       */
26644
26645      this.trigger('exitFullWindow');
26646    }
26647    /**
26648     * Check if the player is in Picture-in-Picture mode or tell the player that it
26649     * is or is not in Picture-in-Picture mode.
26650     *
26651     * @param  {boolean} [isPiP]
26652     *         Set the players current Picture-in-Picture state
26653     *
26654     * @return {boolean}
26655     *         - true if Picture-in-Picture is on and getting
26656     *         - false if Picture-in-Picture is off and getting
26657     */
26658    ;
26659
26660    _proto.isInPictureInPicture = function isInPictureInPicture(isPiP) {
26661      if (isPiP !== undefined) {
26662        this.isInPictureInPicture_ = !!isPiP;
26663        this.togglePictureInPictureClass_();
26664        return;
26665      }
26666
26667      return !!this.isInPictureInPicture_;
26668    }
26669    /**
26670     * Create a floating video window always on top of other windows so that users may
26671     * continue consuming media while they interact with other content sites, or
26672     * applications on their device.
26673     *
26674     * @see [Spec]{@link https://wicg.github.io/picture-in-picture}
26675     *
26676     * @fires Player#enterpictureinpicture
26677     *
26678     * @return {Promise}
26679     *         A promise with a Picture-in-Picture window.
26680     */
26681    ;
26682
26683    _proto.requestPictureInPicture = function requestPictureInPicture() {
26684      if ('pictureInPictureEnabled' in document) {
26685        /**
26686         * This event fires when the player enters picture in picture mode
26687         *
26688         * @event Player#enterpictureinpicture
26689         * @type {EventTarget~Event}
26690         */
26691        return this.techGet_('requestPictureInPicture');
26692      }
26693    }
26694    /**
26695     * Exit Picture-in-Picture mode.
26696     *
26697     * @see [Spec]{@link https://wicg.github.io/picture-in-picture}
26698     *
26699     * @fires Player#leavepictureinpicture
26700     *
26701     * @return {Promise}
26702     *         A promise.
26703     */
26704    ;
26705
26706    _proto.exitPictureInPicture = function exitPictureInPicture() {
26707      if ('pictureInPictureEnabled' in document) {
26708        /**
26709         * This event fires when the player leaves picture in picture mode
26710         *
26711         * @event Player#leavepictureinpicture
26712         * @type {EventTarget~Event}
26713         */
26714        return document.exitPictureInPicture();
26715      }
26716    }
26717    /**
26718     * Called when this Player has focus and a key gets pressed down, or when
26719     * any Component of this player receives a key press that it doesn't handle.
26720     * This allows player-wide hotkeys (either as defined below, or optionally
26721     * by an external function).
26722     *
26723     * @param {EventTarget~Event} event
26724     *        The `keydown` event that caused this function to be called.
26725     *
26726     * @listens keydown
26727     */
26728    ;
26729
26730    _proto.handleKeyDown = function handleKeyDown(event) {
26731      var userActions = this.options_.userActions; // Bail out if hotkeys are not configured.
26732
26733      if (!userActions || !userActions.hotkeys) {
26734        return;
26735      } // Function that determines whether or not to exclude an element from
26736      // hotkeys handling.
26737
26738
26739      var excludeElement = function excludeElement(el) {
26740        var tagName = el.tagName.toLowerCase(); // The first and easiest test is for `contenteditable` elements.
26741
26742        if (el.isContentEditable) {
26743          return true;
26744        } // Inputs matching these types will still trigger hotkey handling as
26745        // they are not text inputs.
26746
26747
26748        var allowedInputTypes = ['button', 'checkbox', 'hidden', 'radio', 'reset', 'submit'];
26749
26750        if (tagName === 'input') {
26751          return allowedInputTypes.indexOf(el.type) === -1;
26752        } // The final test is by tag name. These tags will be excluded entirely.
26753
26754
26755        var excludedTags = ['textarea'];
26756        return excludedTags.indexOf(tagName) !== -1;
26757      }; // Bail out if the user is focused on an interactive form element.
26758
26759
26760      if (excludeElement(this.el_.ownerDocument.activeElement)) {
26761        return;
26762      }
26763
26764      if (typeof userActions.hotkeys === 'function') {
26765        userActions.hotkeys.call(this, event);
26766      } else {
26767        this.handleHotkeys(event);
26768      }
26769    }
26770    /**
26771     * Called when this Player receives a hotkey keydown event.
26772     * Supported player-wide hotkeys are:
26773     *
26774     *   f          - toggle fullscreen
26775     *   m          - toggle mute
26776     *   k or Space - toggle play/pause
26777     *
26778     * @param {EventTarget~Event} event
26779     *        The `keydown` event that caused this function to be called.
26780     */
26781    ;
26782
26783    _proto.handleHotkeys = function handleHotkeys(event) {
26784      var hotkeys = this.options_.userActions ? this.options_.userActions.hotkeys : {}; // set fullscreenKey, muteKey, playPauseKey from `hotkeys`, use defaults if not set
26785
26786      var _hotkeys$fullscreenKe = hotkeys.fullscreenKey,
26787          fullscreenKey = _hotkeys$fullscreenKe === void 0 ? function (keydownEvent) {
26788        return keycode.isEventKey(keydownEvent, 'f');
26789      } : _hotkeys$fullscreenKe,
26790          _hotkeys$muteKey = hotkeys.muteKey,
26791          muteKey = _hotkeys$muteKey === void 0 ? function (keydownEvent) {
26792        return keycode.isEventKey(keydownEvent, 'm');
26793      } : _hotkeys$muteKey,
26794          _hotkeys$playPauseKey = hotkeys.playPauseKey,
26795          playPauseKey = _hotkeys$playPauseKey === void 0 ? function (keydownEvent) {
26796        return keycode.isEventKey(keydownEvent, 'k') || keycode.isEventKey(keydownEvent, 'Space');
26797      } : _hotkeys$playPauseKey;
26798
26799      if (fullscreenKey.call(this, event)) {
26800        event.preventDefault();
26801        event.stopPropagation();
26802        var FSToggle = Component.getComponent('FullscreenToggle');
26803
26804        if (document[this.fsApi_.fullscreenEnabled] !== false) {
26805          FSToggle.prototype.handleClick.call(this, event);
26806        }
26807      } else if (muteKey.call(this, event)) {
26808        event.preventDefault();
26809        event.stopPropagation();
26810        var MuteToggle = Component.getComponent('MuteToggle');
26811        MuteToggle.prototype.handleClick.call(this, event);
26812      } else if (playPauseKey.call(this, event)) {
26813        event.preventDefault();
26814        event.stopPropagation();
26815        var PlayToggle = Component.getComponent('PlayToggle');
26816        PlayToggle.prototype.handleClick.call(this, event);
26817      }
26818    }
26819    /**
26820     * Check whether the player can play a given mimetype
26821     *
26822     * @see https://www.w3.org/TR/2011/WD-html5-20110113/video.html#dom-navigator-canplaytype
26823     *
26824     * @param {string} type
26825     *        The mimetype to check
26826     *
26827     * @return {string}
26828     *         'probably', 'maybe', or '' (empty string)
26829     */
26830    ;
26831
26832    _proto.canPlayType = function canPlayType(type) {
26833      var can; // Loop through each playback technology in the options order
26834
26835      for (var i = 0, j = this.options_.techOrder; i < j.length; i++) {
26836        var techName = j[i];
26837        var tech = Tech.getTech(techName); // Support old behavior of techs being registered as components.
26838        // Remove once that deprecated behavior is removed.
26839
26840        if (!tech) {
26841          tech = Component.getComponent(techName);
26842        } // Check if the current tech is defined before continuing
26843
26844
26845        if (!tech) {
26846          log.error("The \"" + techName + "\" tech is undefined. Skipped browser support check for that tech.");
26847          continue;
26848        } // Check if the browser supports this technology
26849
26850
26851        if (tech.isSupported()) {
26852          can = tech.canPlayType(type);
26853
26854          if (can) {
26855            return can;
26856          }
26857        }
26858      }
26859
26860      return '';
26861    }
26862    /**
26863     * Select source based on tech-order or source-order
26864     * Uses source-order selection if `options.sourceOrder` is truthy. Otherwise,
26865     * defaults to tech-order selection
26866     *
26867     * @param {Array} sources
26868     *        The sources for a media asset
26869     *
26870     * @return {Object|boolean}
26871     *         Object of source and tech order or false
26872     */
26873    ;
26874
26875    _proto.selectSource = function selectSource(sources) {
26876      var _this10 = this;
26877
26878      // Get only the techs specified in `techOrder` that exist and are supported by the
26879      // current platform
26880      var techs = this.options_.techOrder.map(function (techName) {
26881        return [techName, Tech.getTech(techName)];
26882      }).filter(function (_ref) {
26883        var techName = _ref[0],
26884            tech = _ref[1];
26885
26886        // Check if the current tech is defined before continuing
26887        if (tech) {
26888          // Check if the browser supports this technology
26889          return tech.isSupported();
26890        }
26891
26892        log.error("The \"" + techName + "\" tech is undefined. Skipped browser support check for that tech.");
26893        return false;
26894      }); // Iterate over each `innerArray` element once per `outerArray` element and execute
26895      // `tester` with both. If `tester` returns a non-falsy value, exit early and return
26896      // that value.
26897
26898      var findFirstPassingTechSourcePair = function findFirstPassingTechSourcePair(outerArray, innerArray, tester) {
26899        var found;
26900        outerArray.some(function (outerChoice) {
26901          return innerArray.some(function (innerChoice) {
26902            found = tester(outerChoice, innerChoice);
26903
26904            if (found) {
26905              return true;
26906            }
26907          });
26908        });
26909        return found;
26910      };
26911
26912      var foundSourceAndTech;
26913
26914      var flip = function flip(fn) {
26915        return function (a, b) {
26916          return fn(b, a);
26917        };
26918      };
26919
26920      var finder = function finder(_ref2, source) {
26921        var techName = _ref2[0],
26922            tech = _ref2[1];
26923
26924        if (tech.canPlaySource(source, _this10.options_[techName.toLowerCase()])) {
26925          return {
26926            source: source,
26927            tech: techName
26928          };
26929        }
26930      }; // Depending on the truthiness of `options.sourceOrder`, we swap the order of techs and sources
26931      // to select from them based on their priority.
26932
26933
26934      if (this.options_.sourceOrder) {
26935        // Source-first ordering
26936        foundSourceAndTech = findFirstPassingTechSourcePair(sources, techs, flip(finder));
26937      } else {
26938        // Tech-first ordering
26939        foundSourceAndTech = findFirstPassingTechSourcePair(techs, sources, finder);
26940      }
26941
26942      return foundSourceAndTech || false;
26943    }
26944    /**
26945     * Get or set the video source.
26946     *
26947     * @param {Tech~SourceObject|Tech~SourceObject[]|string} [source]
26948     *        A SourceObject, an array of SourceObjects, or a string referencing
vendor: 4,455 bytes, lines 26949-27084
26949     *        a URL to a media source. It is _highly recommended_ that an object
26950     *        or array of objects is used here, so that source selection
26951     *        algorithms can take the `type` into account.
26952     *
26953     *        If not provided, this method acts as a getter.
26954     *
26955     * @return {string|undefined}
26956     *         If the `source` argument is missing, returns the current source
26957     *         URL. Otherwise, returns nothing/undefined.
26958     */
26959    ;
26960
26961    _proto.src = function src(source) {
26962      var _this11 = this;
26963
26964      // getter usage
26965      if (typeof source === 'undefined') {
26966        return this.cache_.src || '';
26967      } // filter out invalid sources and turn our source into
26968      // an array of source objects
26969
26970
26971      var sources = filterSource(source); // if a source was passed in then it is invalid because
26972      // it was filtered to a zero length Array. So we have to
26973      // show an error
26974
26975      if (!sources.length) {
26976        this.setTimeout(function () {
26977          this.error({
26978            code: 4,
26979            message: this.localize(this.options_.notSupportedMessage)
26980          });
26981        }, 0);
26982        return;
26983      } // intial sources
26984
26985
26986      this.changingSrc_ = true;
26987      this.cache_.sources = sources;
26988      this.updateSourceCaches_(sources[0]); // middlewareSource is the source after it has been changed by middleware
26989
26990      setSource(this, sources[0], function (middlewareSource, mws) {
26991        _this11.middleware_ = mws; // since sourceSet is async we have to update the cache again after we select a source since
26992        // the source that is selected could be out of order from the cache update above this callback.
26993
26994        _this11.cache_.sources = sources;
26995
26996        _this11.updateSourceCaches_(middlewareSource);
26997
26998        var err = _this11.src_(middlewareSource);
26999
27000        if (err) {
27001          if (sources.length > 1) {
27002            return _this11.src(sources.slice(1));
27003          }
27004
27005          _this11.changingSrc_ = false; // We need to wrap this in a timeout to give folks a chance to add error event handlers
27006
27007          _this11.setTimeout(function () {
27008            this.error({
27009              code: 4,
27010              message: this.localize(this.options_.notSupportedMessage)
27011            });
27012          }, 0); // we could not find an appropriate tech, but let's still notify the delegate that this is it
27013          // this needs a better comment about why this is needed
27014
27015
27016          _this11.triggerReady();
27017
27018          return;
27019        }
27020
27021        setTech(mws, _this11.tech_);
27022      });
27023    }
27024    /**
27025     * Set the source object on the tech, returns a boolean that indicates whether
27026     * there is a tech that can play the source or not
27027     *
27028     * @param {Tech~SourceObject} source
27029     *        The source object to set on the Tech
27030     *
27031     * @return {boolean}
27032     *         - True if there is no Tech to playback this source
27033     *         - False otherwise
27034     *
27035     * @private
27036     */
27037    ;
27038
27039    _proto.src_ = function src_(source) {
27040      var _this12 = this;
27041
27042      var sourceTech = this.selectSource([source]);
27043
27044      if (!sourceTech) {
27045        return true;
27046      }
27047
27048      if (!titleCaseEquals(sourceTech.tech, this.techName_)) {
27049        this.changingSrc_ = true; // load this technology with the chosen source
27050
27051        this.loadTech_(sourceTech.tech, sourceTech.source);
27052        this.tech_.ready(function () {
27053          _this12.changingSrc_ = false;
27054        });
27055        return false;
27056      } // wait until the tech is ready to set the source
27057      // and set it synchronously if possible (#2326)
27058
27059
27060      this.ready(function () {
27061        // The setSource tech method was added with source handlers
27062        // so older techs won't support it
27063        // We need to check the direct prototype for the case where subclasses
27064        // of the tech do not support source handlers
27065        if (this.tech_.constructor.prototype.hasOwnProperty('setSource')) {
27066          this.techCall_('setSource', source);
27067        } else {
27068          this.techCall_('src', source.src);
27069        }
27070
27071        this.changingSrc_ = false;
27072      }, true);
27073      return false;
27074    }
27075    /**
27076     * Begin loading the src data.
27077     */
27078    ;
27079
27080    _proto.load = function load() {
27081      this.techCall_('load');
27082    }
27083    /**
27084     * Reset the player. Loads the first tech in the techOrder,
vendor: 37,047 bytes, lines 27085-28236
27085     * removes all the text tracks in the existing `tech`,
27086     * and calls `reset` on the `tech`.
27087     */
27088    ;
27089
27090    _proto.reset = function reset() {
27091      var _this13 = this;
27092
27093      var PromiseClass = this.options_.Promise || window$1.Promise;
27094
27095      if (this.paused() || !PromiseClass) {
27096        this.doReset_();
27097      } else {
27098        var playPromise = this.play();
27099        silencePromise(playPromise.then(function () {
27100          return _this13.doReset_();
27101        }));
27102      }
27103    };
27104
27105    _proto.doReset_ = function doReset_() {
27106      if (this.tech_) {
27107        this.tech_.clearTracks('text');
27108      }
27109
27110      this.resetCache_();
27111      this.poster('');
27112      this.loadTech_(this.options_.techOrder[0], null);
27113      this.techCall_('reset');
27114      this.resetControlBarUI_();
27115
27116      if (isEvented(this)) {
27117        this.trigger('playerreset');
27118      }
27119    }
27120    /**
27121     * Reset Control Bar's UI by calling sub-methods that reset
27122     * all of Control Bar's components
27123     */
27124    ;
27125
27126    _proto.resetControlBarUI_ = function resetControlBarUI_() {
27127      this.resetProgressBar_();
27128      this.resetPlaybackRate_();
27129      this.resetVolumeBar_();
27130    }
27131    /**
27132     * Reset tech's progress so progress bar is reset in the UI
27133     */
27134    ;
27135
27136    _proto.resetProgressBar_ = function resetProgressBar_() {
27137      this.currentTime(0);
27138      var _this$controlBar = this.controlBar,
27139          durationDisplay = _this$controlBar.durationDisplay,
27140          remainingTimeDisplay = _this$controlBar.remainingTimeDisplay;
27141
27142      if (durationDisplay) {
27143        durationDisplay.updateContent();
27144      }
27145
27146      if (remainingTimeDisplay) {
27147        remainingTimeDisplay.updateContent();
27148      }
27149    }
27150    /**
27151     * Reset Playback ratio
27152     */
27153    ;
27154
27155    _proto.resetPlaybackRate_ = function resetPlaybackRate_() {
27156      this.playbackRate(this.defaultPlaybackRate());
27157      this.handleTechRateChange_();
27158    }
27159    /**
27160     * Reset Volume bar
27161     */
27162    ;
27163
27164    _proto.resetVolumeBar_ = function resetVolumeBar_() {
27165      this.volume(1.0);
27166      this.trigger('volumechange');
27167    }
27168    /**
27169     * Returns all of the current source objects.
27170     *
27171     * @return {Tech~SourceObject[]}
27172     *         The current source objects
27173     */
27174    ;
27175
27176    _proto.currentSources = function currentSources() {
27177      var source = this.currentSource();
27178      var sources = []; // assume `{}` or `{ src }`
27179
27180      if (Object.keys(source).length !== 0) {
27181        sources.push(source);
27182      }
27183
27184      return this.cache_.sources || sources;
27185    }
27186    /**
27187     * Returns the current source object.
27188     *
27189     * @return {Tech~SourceObject}
27190     *         The current source object
27191     */
27192    ;
27193
27194    _proto.currentSource = function currentSource() {
27195      return this.cache_.source || {};
27196    }
27197    /**
27198     * Returns the fully qualified URL of the current source value e.g. http://mysite.com/video.mp4
27199     * Can be used in conjunction with `currentType` to assist in rebuilding the current source object.
27200     *
27201     * @return {string}
27202     *         The current source
27203     */
27204    ;
27205
27206    _proto.currentSrc = function currentSrc() {
27207      return this.currentSource() && this.currentSource().src || '';
27208    }
27209    /**
27210     * Get the current source type e.g. video/mp4
27211     * This can allow you rebuild the current source object so that you could load the same
27212     * source and tech later
27213     *
27214     * @return {string}
27215     *         The source MIME type
27216     */
27217    ;
27218
27219    _proto.currentType = function currentType() {
27220      return this.currentSource() && this.currentSource().type || '';
27221    }
27222    /**
27223     * Get or set the preload attribute
27224     *
27225     * @param {boolean} [value]
27226     *        - true means that we should preload
27227     *        - false means that we should not preload
27228     *
27229     * @return {string}
27230     *         The preload attribute value when getting
27231     */
27232    ;
27233
27234    _proto.preload = function preload(value) {
27235      if (value !== undefined) {
27236        this.techCall_('setPreload', value);
27237        this.options_.preload = value;
27238        return;
27239      }
27240
27241      return this.techGet_('preload');
27242    }
27243    /**
27244     * Get or set the autoplay option. When this is a boolean it will
27245     * modify the attribute on the tech. When this is a string the attribute on
27246     * the tech will be removed and `Player` will handle autoplay on loadstarts.
27247     *
27248     * @param {boolean|string} [value]
27249     *        - true: autoplay using the browser behavior
27250     *        - false: do not autoplay
27251     *        - 'play': call play() on every loadstart
27252     *        - 'muted': call muted() then play() on every loadstart
27253     *        - 'any': call play() on every loadstart. if that fails call muted() then play().
27254     *        - *: values other than those listed here will be set `autoplay` to true
27255     *
27256     * @return {boolean|string}
27257     *         The current value of autoplay when getting
27258     */
27259    ;
27260
27261    _proto.autoplay = function autoplay(value) {
27262      // getter usage
27263      if (value === undefined) {
27264        return this.options_.autoplay || false;
27265      }
27266
27267      var techAutoplay; // if the value is a valid string set it to that
27268
27269      if (typeof value === 'string' && /(any|play|muted)/.test(value)) {
27270        this.options_.autoplay = value;
27271        this.manualAutoplay_(value);
27272        techAutoplay = false; // any falsy value sets autoplay to false in the browser,
27273        // lets do the same
27274      } else if (!value) {
27275        this.options_.autoplay = false; // any other value (ie truthy) sets autoplay to true
27276      } else {
27277        this.options_.autoplay = true;
27278      }
27279
27280      techAutoplay = typeof techAutoplay === 'undefined' ? this.options_.autoplay : techAutoplay; // if we don't have a tech then we do not queue up
27281      // a setAutoplay call on tech ready. We do this because the
27282      // autoplay option will be passed in the constructor and we
27283      // do not need to set it twice
27284
27285      if (this.tech_) {
27286        this.techCall_('setAutoplay', techAutoplay);
27287      }
27288    }
27289    /**
27290     * Set or unset the playsinline attribute.
27291     * Playsinline tells the browser that non-fullscreen playback is preferred.
27292     *
27293     * @param {boolean} [value]
27294     *        - true means that we should try to play inline by default
27295     *        - false means that we should use the browser's default playback mode,
27296     *          which in most cases is inline. iOS Safari is a notable exception
27297     *          and plays fullscreen by default.
27298     *
27299     * @return {string|Player}
27300     *         - the current value of playsinline
27301     *         - the player when setting
27302     *
27303     * @see [Spec]{@link https://html.spec.whatwg.org/#attr-video-playsinline}
27304     */
27305    ;
27306
27307    _proto.playsinline = function playsinline(value) {
27308      if (value !== undefined) {
27309        this.techCall_('setPlaysinline', value);
27310        this.options_.playsinline = value;
27311        return this;
27312      }
27313
27314      return this.techGet_('playsinline');
27315    }
27316    /**
27317     * Get or set the loop attribute on the video element.
27318     *
27319     * @param {boolean} [value]
27320     *        - true means that we should loop the video
27321     *        - false means that we should not loop the video
27322     *
27323     * @return {boolean}
27324     *         The current value of loop when getting
27325     */
27326    ;
27327
27328    _proto.loop = function loop(value) {
27329      if (value !== undefined) {
27330        this.techCall_('setLoop', value);
27331        this.options_.loop = value;
27332        return;
27333      }
27334
27335      return this.techGet_('loop');
27336    }
27337    /**
27338     * Get or set the poster image source url
27339     *
27340     * @fires Player#posterchange
27341     *
27342     * @param {string} [src]
27343     *        Poster image source URL
27344     *
27345     * @return {string}
27346     *         The current value of poster when getting
27347     */
27348    ;
27349
27350    _proto.poster = function poster(src) {
27351      if (src === undefined) {
27352        return this.poster_;
27353      } // The correct way to remove a poster is to set as an empty string
27354      // other falsey values will throw errors
27355
27356
27357      if (!src) {
27358        src = '';
27359      }
27360
27361      if (src === this.poster_) {
27362        return;
27363      } // update the internal poster variable
27364
27365
27366      this.poster_ = src; // update the tech's poster
27367
27368      this.techCall_('setPoster', src);
27369      this.isPosterFromTech_ = false; // alert components that the poster has been set
27370
27371      /**
27372       * This event fires when the poster image is changed on the player.
27373       *
27374       * @event Player#posterchange
27375       * @type {EventTarget~Event}
27376       */
27377
27378      this.trigger('posterchange');
27379    }
27380    /**
27381     * Some techs (e.g. YouTube) can provide a poster source in an
27382     * asynchronous way. We want the poster component to use this
27383     * poster source so that it covers up the tech's controls.
27384     * (YouTube's play button). However we only want to use this
27385     * source if the player user hasn't set a poster through
27386     * the normal APIs.
27387     *
27388     * @fires Player#posterchange
27389     * @listens Tech#posterchange
27390     * @private
27391     */
27392    ;
27393
27394    _proto.handleTechPosterChange_ = function handleTechPosterChange_() {
27395      if ((!this.poster_ || this.options_.techCanOverridePoster) && this.tech_ && this.tech_.poster) {
27396        var newPoster = this.tech_.poster() || '';
27397
27398        if (newPoster !== this.poster_) {
27399          this.poster_ = newPoster;
27400          this.isPosterFromTech_ = true; // Let components know the poster has changed
27401
27402          this.trigger('posterchange');
27403        }
27404      }
27405    }
27406    /**
27407     * Get or set whether or not the controls are showing.
27408     *
27409     * @fires Player#controlsenabled
27410     *
27411     * @param {boolean} [bool]
27412     *        - true to turn controls on
27413     *        - false to turn controls off
27414     *
27415     * @return {boolean}
27416     *         The current value of controls when getting
27417     */
27418    ;
27419
27420    _proto.controls = function controls(bool) {
27421      if (bool === undefined) {
27422        return !!this.controls_;
27423      }
27424
27425      bool = !!bool; // Don't trigger a change event unless it actually changed
27426
27427      if (this.controls_ === bool) {
27428        return;
27429      }
27430
27431      this.controls_ = bool;
27432
27433      if (this.usingNativeControls()) {
27434        this.techCall_('setControls', bool);
27435      }
27436
27437      if (this.controls_) {
27438        this.removeClass('vjs-controls-disabled');
27439        this.addClass('vjs-controls-enabled');
27440        /**
27441         * @event Player#controlsenabled
27442         * @type {EventTarget~Event}
27443         */
27444
27445        this.trigger('controlsenabled');
27446
27447        if (!this.usingNativeControls()) {
27448          this.addTechControlsListeners_();
27449        }
27450      } else {
27451        this.removeClass('vjs-controls-enabled');
27452        this.addClass('vjs-controls-disabled');
27453        /**
27454         * @event Player#controlsdisabled
27455         * @type {EventTarget~Event}
27456         */
27457
27458        this.trigger('controlsdisabled');
27459
27460        if (!this.usingNativeControls()) {
27461          this.removeTechControlsListeners_();
27462        }
27463      }
27464    }
27465    /**
27466     * Toggle native controls on/off. Native controls are the controls built into
27467     * devices (e.g. default iPhone controls), Flash, or other techs
27468     * (e.g. Vimeo Controls)
27469     * **This should only be set by the current tech, because only the tech knows
27470     * if it can support native controls**
27471     *
27472     * @fires Player#usingnativecontrols
27473     * @fires Player#usingcustomcontrols
27474     *
27475     * @param {boolean} [bool]
27476     *        - true to turn native controls on
27477     *        - false to turn native controls off
27478     *
27479     * @return {boolean}
27480     *         The current value of native controls when getting
27481     */
27482    ;
27483
27484    _proto.usingNativeControls = function usingNativeControls(bool) {
27485      if (bool === undefined) {
27486        return !!this.usingNativeControls_;
27487      }
27488
27489      bool = !!bool; // Don't trigger a change event unless it actually changed
27490
27491      if (this.usingNativeControls_ === bool) {
27492        return;
27493      }
27494
27495      this.usingNativeControls_ = bool;
27496
27497      if (this.usingNativeControls_) {
27498        this.addClass('vjs-using-native-controls');
27499        /**
27500         * player is using the native device controls
27501         *
27502         * @event Player#usingnativecontrols
27503         * @type {EventTarget~Event}
27504         */
27505
27506        this.trigger('usingnativecontrols');
27507      } else {
27508        this.removeClass('vjs-using-native-controls');
27509        /**
27510         * player is using the custom HTML controls
27511         *
27512         * @event Player#usingcustomcontrols
27513         * @type {EventTarget~Event}
27514         */
27515
27516        this.trigger('usingcustomcontrols');
27517      }
27518    }
27519    /**
27520     * Set or get the current MediaError
27521     *
27522     * @fires Player#error
27523     *
27524     * @param  {MediaError|string|number} [err]
27525     *         A MediaError or a string/number to be turned
27526     *         into a MediaError
27527     *
27528     * @return {MediaError|null}
27529     *         The current MediaError when getting (or null)
27530     */
27531    ;
27532
27533    _proto.error = function error(err) {
27534      if (err === undefined) {
27535        return this.error_ || null;
27536      } // Suppress the first error message for no compatible source until
27537      // user interaction
27538
27539
27540      if (this.options_.suppressNotSupportedError && err && err.message && err.message === this.localize(this.options_.notSupportedMessage)) {
27541        var triggerSuppressedError = function triggerSuppressedError() {
27542          this.error(err);
27543        };
27544
27545        this.options_.suppressNotSupportedError = false;
27546        this.any(['click', 'touchstart'], triggerSuppressedError);
27547        this.one('loadstart', function () {
27548          this.off(['click', 'touchstart'], triggerSuppressedError);
27549        });
27550        return;
27551      } // restoring to default
27552
27553
27554      if (err === null) {
27555        this.error_ = err;
27556        this.removeClass('vjs-error');
27557
27558        if (this.errorDisplay) {
27559          this.errorDisplay.close();
27560        }
27561
27562        return;
27563      }
27564
27565      this.error_ = new MediaError(err); // add the vjs-error classname to the player
27566
27567      this.addClass('vjs-error'); // log the name of the error type and any message
27568      // IE11 logs "[object object]" and required you to expand message to see error object
27569
27570      log.error("(CODE:" + this.error_.code + " " + MediaError.errorTypes[this.error_.code] + ")", this.error_.message, this.error_);
27571      /**
27572       * @event Player#error
27573       * @type {EventTarget~Event}
27574       */
27575
27576      this.trigger('error');
27577      return;
27578    }
27579    /**
27580     * Report user activity
27581     *
27582     * @param {Object} event
27583     *        Event object
27584     */
27585    ;
27586
27587    _proto.reportUserActivity = function reportUserActivity(event) {
27588      this.userActivity_ = true;
27589    }
27590    /**
27591     * Get/set if user is active
27592     *
27593     * @fires Player#useractive
27594     * @fires Player#userinactive
27595     *
27596     * @param {boolean} [bool]
27597     *        - true if the user is active
27598     *        - false if the user is inactive
27599     *
27600     * @return {boolean}
27601     *         The current value of userActive when getting
27602     */
27603    ;
27604
27605    _proto.userActive = function userActive(bool) {
27606      if (bool === undefined) {
27607        return this.userActive_;
27608      }
27609
27610      bool = !!bool;
27611
27612      if (bool === this.userActive_) {
27613        return;
27614      }
27615
27616      this.userActive_ = bool;
27617
27618      if (this.userActive_) {
27619        this.userActivity_ = true;
27620        this.removeClass('vjs-user-inactive');
27621        this.addClass('vjs-user-active');
27622        /**
27623         * @event Player#useractive
27624         * @type {EventTarget~Event}
27625         */
27626
27627        this.trigger('useractive');
27628        return;
27629      } // Chrome/Safari/IE have bugs where when you change the cursor it can
27630      // trigger a mousemove event. This causes an issue when you're hiding
27631      // the cursor when the user is inactive, and a mousemove signals user
27632      // activity. Making it impossible to go into inactive mode. Specifically
27633      // this happens in fullscreen when we really need to hide the cursor.
27634      //
27635      // When this gets resolved in ALL browsers it can be removed
27636      // https://code.google.com/p/chromium/issues/detail?id=103041
27637
27638
27639      if (this.tech_) {
27640        this.tech_.one('mousemove', function (e) {
27641          e.stopPropagation();
27642          e.preventDefault();
27643        });
27644      }
27645
27646      this.userActivity_ = false;
27647      this.removeClass('vjs-user-active');
27648      this.addClass('vjs-user-inactive');
27649      /**
27650       * @event Player#userinactive
27651       * @type {EventTarget~Event}
27652       */
27653
27654      this.trigger('userinactive');
27655    }
27656    /**
27657     * Listen for user activity based on timeout value
27658     *
27659     * @private
27660     */
27661    ;
27662
27663    _proto.listenForUserActivity_ = function listenForUserActivity_() {
27664      var mouseInProgress;
27665      var lastMoveX;
27666      var lastMoveY;
27667      var handleActivity = bind(this, this.reportUserActivity);
27668
27669      var handleMouseMove = function handleMouseMove(e) {
27670        // #1068 - Prevent mousemove spamming
27671        // Chrome Bug: https://code.google.com/p/chromium/issues/detail?id=366970
27672        if (e.screenX !== lastMoveX || e.screenY !== lastMoveY) {
27673          lastMoveX = e.screenX;
27674          lastMoveY = e.screenY;
27675          handleActivity();
27676        }
27677      };
27678
27679      var handleMouseDown = function handleMouseDown() {
27680        handleActivity(); // For as long as the they are touching the device or have their mouse down,
27681        // we consider them active even if they're not moving their finger or mouse.
27682        // So we want to continue to update that they are active
27683
27684        this.clearInterval(mouseInProgress); // Setting userActivity=true now and setting the interval to the same time
27685        // as the activityCheck interval (250) should ensure we never miss the
27686        // next activityCheck
27687
27688        mouseInProgress = this.setInterval(handleActivity, 250);
27689      };
27690
27691      var handleMouseUp = function handleMouseUp(event) {
27692        handleActivity(); // Stop the interval that maintains activity if the mouse/touch is down
27693
27694        this.clearInterval(mouseInProgress);
27695      }; // Any mouse movement will be considered user activity
27696
27697
27698      this.on('mousedown', handleMouseDown);
27699      this.on('mousemove', handleMouseMove);
27700      this.on('mouseup', handleMouseUp);
27701      var controlBar = this.getChild('controlBar'); // Fixes bug on Android & iOS where when tapping progressBar (when control bar is displayed)
27702      // controlBar would no longer be hidden by default timeout.
27703
27704      if (controlBar && !IS_IOS && !IS_ANDROID) {
27705        controlBar.on('mouseenter', function (event) {
27706          this.player().cache_.inactivityTimeout = this.player().options_.inactivityTimeout;
27707          this.player().options_.inactivityTimeout = 0;
27708        });
27709        controlBar.on('mouseleave', function (event) {
27710          this.player().options_.inactivityTimeout = this.player().cache_.inactivityTimeout;
27711        });
27712      } // Listen for keyboard navigation
27713      // Shouldn't need to use inProgress interval because of key repeat
27714
27715
27716      this.on('keydown', handleActivity);
27717      this.on('keyup', handleActivity); // Run an interval every 250 milliseconds instead of stuffing everything into
27718      // the mousemove/touchmove function itself, to prevent performance degradation.
27719      // `this.reportUserActivity` simply sets this.userActivity_ to true, which
27720      // then gets picked up by this loop
27721      // http://ejohn.org/blog/learning-from-twitter/
27722
27723      var inactivityTimeout;
27724      this.setInterval(function () {
27725        // Check to see if mouse/touch activity has happened
27726        if (!this.userActivity_) {
27727          return;
27728        } // Reset the activity tracker
27729
27730
27731        this.userActivity_ = false; // If the user state was inactive, set the state to active
27732
27733        this.userActive(true); // Clear any existing inactivity timeout to start the timer over
27734
27735        this.clearTimeout(inactivityTimeout);
27736        var timeout = this.options_.inactivityTimeout;
27737
27738        if (timeout <= 0) {
27739          return;
27740        } // In <timeout> milliseconds, if no more activity has occurred the
27741        // user will be considered inactive
27742
27743
27744        inactivityTimeout = this.setTimeout(function () {
27745          // Protect against the case where the inactivityTimeout can trigger just
27746          // before the next user activity is picked up by the activity check loop
27747          // causing a flicker
27748          if (!this.userActivity_) {
27749            this.userActive(false);
27750          }
27751        }, timeout);
27752      }, 250);
27753    }
27754    /**
27755     * Gets or sets the current playback rate. A playback rate of
27756     * 1.0 represents normal speed and 0.5 would indicate half-speed
27757     * playback, for instance.
27758     *
27759     * @see https://html.spec.whatwg.org/multipage/embedded-content.html#dom-media-playbackrate
27760     *
27761     * @param {number} [rate]
27762     *       New playback rate to set.
27763     *
27764     * @return {number}
27765     *         The current playback rate when getting or 1.0
27766     */
27767    ;
27768
27769    _proto.playbackRate = function playbackRate(rate) {
27770      if (rate !== undefined) {
27771        // NOTE: this.cache_.lastPlaybackRate is set from the tech handler
27772        // that is registered above
27773        this.techCall_('setPlaybackRate', rate);
27774        return;
27775      }
27776
27777      if (this.tech_ && this.tech_.featuresPlaybackRate) {
27778        return this.cache_.lastPlaybackRate || this.techGet_('playbackRate');
27779      }
27780
27781      return 1.0;
27782    }
27783    /**
27784     * Gets or sets the current default playback rate. A default playback rate of
27785     * 1.0 represents normal speed and 0.5 would indicate half-speed playback, for instance.
27786     * defaultPlaybackRate will only represent what the initial playbackRate of a video was, not
27787     * not the current playbackRate.
27788     *
27789     * @see https://html.spec.whatwg.org/multipage/embedded-content.html#dom-media-defaultplaybackrate
27790     *
27791     * @param {number} [rate]
27792     *       New default playback rate to set.
27793     *
27794     * @return {number|Player}
27795     *         - The default playback rate when getting or 1.0
27796     *         - the player when setting
27797     */
27798    ;
27799
27800    _proto.defaultPlaybackRate = function defaultPlaybackRate(rate) {
27801      if (rate !== undefined) {
27802        return this.techCall_('setDefaultPlaybackRate', rate);
27803      }
27804
27805      if (this.tech_ && this.tech_.featuresPlaybackRate) {
27806        return this.techGet_('defaultPlaybackRate');
27807      }
27808
27809      return 1.0;
27810    }
27811    /**
27812     * Gets or sets the audio flag
27813     *
27814     * @param {boolean} bool
27815     *        - true signals that this is an audio player
27816     *        - false signals that this is not an audio player
27817     *
27818     * @return {boolean}
27819     *         The current value of isAudio when getting
27820     */
27821    ;
27822
27823    _proto.isAudio = function isAudio(bool) {
27824      if (bool !== undefined) {
27825        this.isAudio_ = !!bool;
27826        return;
27827      }
27828
27829      return !!this.isAudio_;
27830    }
27831    /**
27832     * A helper method for adding a {@link TextTrack} to our
27833     * {@link TextTrackList}.
27834     *
27835     * In addition to the W3C settings we allow adding additional info through options.
27836     *
27837     * @see http://www.w3.org/html/wg/drafts/html/master/embedded-content-0.html#dom-media-addtexttrack
27838     *
27839     * @param {string} [kind]
27840     *        the kind of TextTrack you are adding
27841     *
27842     * @param {string} [label]
27843     *        the label to give the TextTrack label
27844     *
27845     * @param {string} [language]
27846     *        the language to set on the TextTrack
27847     *
27848     * @return {TextTrack|undefined}
27849     *         the TextTrack that was added or undefined
27850     *         if there is no tech
27851     */
27852    ;
27853
27854    _proto.addTextTrack = function addTextTrack(kind, label, language) {
27855      if (this.tech_) {
27856        return this.tech_.addTextTrack(kind, label, language);
27857      }
27858    }
27859    /**
27860     * Create a remote {@link TextTrack} and an {@link HTMLTrackElement}. It will
27861     * automatically removed from the video element whenever the source changes, unless
27862     * manualCleanup is set to false.
27863     *
27864     * @param {Object} options
27865     *        Options to pass to {@link HTMLTrackElement} during creation. See
27866     *        {@link HTMLTrackElement} for object properties that you should use.
27867     *
27868     * @param {boolean} [manualCleanup=true] if set to false, the TextTrack will be
27869     *
27870     * @return {HtmlTrackElement}
27871     *         the HTMLTrackElement that was created and added
27872     *         to the HtmlTrackElementList and the remote
27873     *         TextTrackList
27874     *
27875     * @deprecated The default value of the "manualCleanup" parameter will default
27876     *             to "false" in upcoming versions of Video.js
27877     */
27878    ;
27879
27880    _proto.addRemoteTextTrack = function addRemoteTextTrack(options, manualCleanup) {
27881      if (this.tech_) {
27882        return this.tech_.addRemoteTextTrack(options, manualCleanup);
27883      }
27884    }
27885    /**
27886     * Remove a remote {@link TextTrack} from the respective
27887     * {@link TextTrackList} and {@link HtmlTrackElementList}.
27888     *
27889     * @param {Object} track
27890     *        Remote {@link TextTrack} to remove
27891     *
27892     * @return {undefined}
27893     *         does not return anything
27894     */
27895    ;
27896
27897    _proto.removeRemoteTextTrack = function removeRemoteTextTrack(obj) {
27898      if (obj === void 0) {
27899        obj = {};
27900      }
27901
27902      var _obj = obj,
27903          track = _obj.track;
27904
27905      if (!track) {
27906        track = obj;
27907      } // destructure the input into an object with a track argument, defaulting to arguments[0]
27908      // default the whole argument to an empty object if nothing was passed in
27909
27910
27911      if (this.tech_) {
27912        return this.tech_.removeRemoteTextTrack(track);
27913      }
27914    }
27915    /**
27916     * Gets available media playback quality metrics as specified by the W3C's Media
27917     * Playback Quality API.
27918     *
27919     * @see [Spec]{@link https://wicg.github.io/media-playback-quality}
27920     *
27921     * @return {Object|undefined}
27922     *         An object with supported media playback quality metrics or undefined if there
27923     *         is no tech or the tech does not support it.
27924     */
27925    ;
27926
27927    _proto.getVideoPlaybackQuality = function getVideoPlaybackQuality() {
27928      return this.techGet_('getVideoPlaybackQuality');
27929    }
27930    /**
27931     * Get video width
27932     *
27933     * @return {number}
27934     *         current video width
27935     */
27936    ;
27937
27938    _proto.videoWidth = function videoWidth() {
27939      return this.tech_ && this.tech_.videoWidth && this.tech_.videoWidth() || 0;
27940    }
27941    /**
27942     * Get video height
27943     *
27944     * @return {number}
27945     *         current video height
27946     */
27947    ;
27948
27949    _proto.videoHeight = function videoHeight() {
27950      return this.tech_ && this.tech_.videoHeight && this.tech_.videoHeight() || 0;
27951    }
27952    /**
27953     * The player's language code
27954     * NOTE: The language should be set in the player options if you want the
27955     * the controls to be built with a specific language. Changing the language
27956     * later will not update controls text.
27957     *
27958     * @param {string} [code]
27959     *        the language code to set the player to
27960     *
27961     * @return {string}
27962     *         The current language code when getting
27963     */
27964    ;
27965
27966    _proto.language = function language(code) {
27967      if (code === undefined) {
27968        return this.language_;
27969      }
27970
27971      this.language_ = String(code).toLowerCase();
27972    }
27973    /**
27974     * Get the player's language dictionary
27975     * Merge every time, because a newly added plugin might call videojs.addLanguage() at any time
27976     * Languages specified directly in the player options have precedence
27977     *
27978     * @return {Array}
27979     *         An array of of supported languages
27980     */
27981    ;
27982
27983    _proto.languages = function languages() {
27984      return mergeOptions(Player.prototype.options_.languages, this.languages_);
27985    }
27986    /**
27987     * returns a JavaScript object reperesenting the current track
27988     * information. **DOES not return it as JSON**
27989     *
27990     * @return {Object}
27991     *         Object representing the current of track info
27992     */
27993    ;
27994
27995    _proto.toJSON = function toJSON() {
27996      var options = mergeOptions(this.options_);
27997      var tracks = options.tracks;
27998      options.tracks = [];
27999
28000      for (var i = 0; i < tracks.length; i++) {
28001        var track = tracks[i]; // deep merge tracks and null out player so no circular references
28002
28003        track = mergeOptions(track);
28004        track.player = undefined;
28005        options.tracks[i] = track;
28006      }
28007
28008      return options;
28009    }
28010    /**
28011     * Creates a simple modal dialog (an instance of the {@link ModalDialog}
28012     * component) that immediately overlays the player with arbitrary
28013     * content and removes itself when closed.
28014     *
28015     * @param {string|Function|Element|Array|null} content
28016     *        Same as {@link ModalDialog#content}'s param of the same name.
28017     *        The most straight-forward usage is to provide a string or DOM
28018     *        element.
28019     *
28020     * @param {Object} [options]
28021     *        Extra options which will be passed on to the {@link ModalDialog}.
28022     *
28023     * @return {ModalDialog}
28024     *         the {@link ModalDialog} that was created
28025     */
28026    ;
28027
28028    _proto.createModal = function createModal(content, options) {
28029      var _this14 = this;
28030
28031      options = options || {};
28032      options.content = content || '';
28033      var modal = new ModalDialog(this, options);
28034      this.addChild(modal);
28035      modal.on('dispose', function () {
28036        _this14.removeChild(modal);
28037      });
28038      modal.open();
28039      return modal;
28040    }
28041    /**
28042     * Change breakpoint classes when the player resizes.
28043     *
28044     * @private
28045     */
28046    ;
28047
28048    _proto.updateCurrentBreakpoint_ = function updateCurrentBreakpoint_() {
28049      if (!this.responsive()) {
28050        return;
28051      }
28052
28053      var currentBreakpoint = this.currentBreakpoint();
28054      var currentWidth = this.currentWidth();
28055
28056      for (var i = 0; i < BREAKPOINT_ORDER.length; i++) {
28057        var candidateBreakpoint = BREAKPOINT_ORDER[i];
28058        var maxWidth = this.breakpoints_[candidateBreakpoint];
28059
28060        if (currentWidth <= maxWidth) {
28061          // The current breakpoint did not change, nothing to do.
28062          if (currentBreakpoint === candidateBreakpoint) {
28063            return;
28064          } // Only remove a class if there is a current breakpoint.
28065
28066
28067          if (currentBreakpoint) {
28068            this.removeClass(BREAKPOINT_CLASSES[currentBreakpoint]);
28069          }
28070
28071          this.addClass(BREAKPOINT_CLASSES[candidateBreakpoint]);
28072          this.breakpoint_ = candidateBreakpoint;
28073          break;
28074        }
28075      }
28076    }
28077    /**
28078     * Removes the current breakpoint.
28079     *
28080     * @private
28081     */
28082    ;
28083
28084    _proto.removeCurrentBreakpoint_ = function removeCurrentBreakpoint_() {
28085      var className = this.currentBreakpointClass();
28086      this.breakpoint_ = '';
28087
28088      if (className) {
28089        this.removeClass(className);
28090      }
28091    }
28092    /**
28093     * Get or set breakpoints on the player.
28094     *
28095     * Calling this method with an object or `true` will remove any previous
28096     * custom breakpoints and start from the defaults again.
28097     *
28098     * @param  {Object|boolean} [breakpoints]
28099     *         If an object is given, it can be used to provide custom
28100     *         breakpoints. If `true` is given, will set default breakpoints.
28101     *         If this argument is not given, will simply return the current
28102     *         breakpoints.
28103     *
28104     * @param  {number} [breakpoints.tiny]
28105     *         The maximum width for the "vjs-layout-tiny" class.
28106     *
28107     * @param  {number} [breakpoints.xsmall]
28108     *         The maximum width for the "vjs-layout-x-small" class.
28109     *
28110     * @param  {number} [breakpoints.small]
28111     *         The maximum width for the "vjs-layout-small" class.
28112     *
28113     * @param  {number} [breakpoints.medium]
28114     *         The maximum width for the "vjs-layout-medium" class.
28115     *
28116     * @param  {number} [breakpoints.large]
28117     *         The maximum width for the "vjs-layout-large" class.
28118     *
28119     * @param  {number} [breakpoints.xlarge]
28120     *         The maximum width for the "vjs-layout-x-large" class.
28121     *
28122     * @param  {number} [breakpoints.huge]
28123     *         The maximum width for the "vjs-layout-huge" class.
28124     *
28125     * @return {Object}
28126     *         An object mapping breakpoint names to maximum width values.
28127     */
28128    ;
28129
28130    _proto.breakpoints = function breakpoints(_breakpoints) {
28131      // Used as a getter.
28132      if (_breakpoints === undefined) {
28133        return assign(this.breakpoints_);
28134      }
28135
28136      this.breakpoint_ = '';
28137      this.breakpoints_ = assign({}, DEFAULT_BREAKPOINTS, _breakpoints); // When breakpoint definitions change, we need to update the currently
28138      // selected breakpoint.
28139
28140      this.updateCurrentBreakpoint_(); // Clone the breakpoints before returning.
28141
28142      return assign(this.breakpoints_);
28143    }
28144    /**
28145     * Get or set a flag indicating whether or not this player should adjust
28146     * its UI based on its dimensions.
28147     *
28148     * @param  {boolean} value
28149     *         Should be `true` if the player should adjust its UI based on its
28150     *         dimensions; otherwise, should be `false`.
28151     *
28152     * @return {boolean}
28153     *         Will be `true` if this player should adjust its UI based on its
28154     *         dimensions; otherwise, will be `false`.
28155     */
28156    ;
28157
28158    _proto.responsive = function responsive(value) {
28159      // Used as a getter.
28160      if (value === undefined) {
28161        return this.responsive_;
28162      }
28163
28164      value = Boolean(value);
28165      var current = this.responsive_; // Nothing changed.
28166
28167      if (value === current) {
28168        return;
28169      } // The value actually changed, set it.
28170
28171
28172      this.responsive_ = value; // Start listening for breakpoints and set the initial breakpoint if the
28173      // player is now responsive.
28174
28175      if (value) {
28176        this.on('playerresize', this.updateCurrentBreakpoint_);
28177        this.updateCurrentBreakpoint_(); // Stop listening for breakpoints if the player is no longer responsive.
28178      } else {
28179        this.off('playerresize', this.updateCurrentBreakpoint_);
28180        this.removeCurrentBreakpoint_();
28181      }
28182
28183      return value;
28184    }
28185    /**
28186     * Get current breakpoint name, if any.
28187     *
28188     * @return {string}
28189     *         If there is currently a breakpoint set, returns a the key from the
28190     *         breakpoints object matching it. Otherwise, returns an empty string.
28191     */
28192    ;
28193
28194    _proto.currentBreakpoint = function currentBreakpoint() {
28195      return this.breakpoint_;
28196    }
28197    /**
28198     * Get the current breakpoint class name.
28199     *
28200     * @return {string}
28201     *         The matching class name (e.g. `"vjs-layout-tiny"` or
28202     *         `"vjs-layout-large"`) for the current breakpoint. Empty string if
28203     *         there is no current breakpoint.
28204     */
28205    ;
28206
28207    _proto.currentBreakpointClass = function currentBreakpointClass() {
28208      return BREAKPOINT_CLASSES[this.breakpoint_] || '';
28209    }
28210    /**
28211     * An object that describes a single piece of media.
28212     *
28213     * Properties that are not part of this type description will be retained; so,
28214     * this can be viewed as a generic metadata storage mechanism as well.
28215     *
28216     * @see      {@link https://wicg.github.io/mediasession/#the-mediametadata-interface}
28217     * @typedef  {Object} Player~MediaObject
28218     *
28219     * @property {string} [album]
28220     *           Unused, except if this object is passed to the `MediaSession`
28221     *           API.
28222     *
28223     * @property {string} [artist]
28224     *           Unused, except if this object is passed to the `MediaSession`
28225     *           API.
28226     *
28227     * @property {Object[]} [artwork]
28228     *           Unused, except if this object is passed to the `MediaSession`
28229     *           API. If not specified, will be populated via the `poster`, if
28230     *           available.
28231     *
28232     * @property {string} [poster]
28233     *           URL to an image that will display before playback.
28234     *
28235     * @property {Tech~SourceObject|Tech~SourceObject[]|string} [src]
28236     *           A single source object, an array of source objects, or a string
28237     *           referencing a URL to a media source. It is _highly recommended_
28238     *           that an object or array of objects is used here, so that source
28239     *           selection algorithms can take the `type` into account.
28240     *
28241     * @property {string} [title]
28242     *           Unused, except if this object is passed to the `MediaSession`
28243     *           API.
28244     *
28245     * @property {Object[]} [textTracks]
28246     *           An array of objects to be used to create text tracks, following
28247     *           the {@link https://www.w3.org/TR/html50/embedded-content-0.html#the-track-element|native track element format}.
28248     *           For ease of removal, these will be created as "remote" text
28249     *           tracks and set to automatically clean up on source changes.
28250     *
28251     *           These objects may have properties like `src`, `kind`, `label`,
28252     *           and `language`, see {@link Tech#createRemoteTextTrack}.
28253     */
28254
28255    /**
28256     * Populate the player using a {@link Player~MediaObject|MediaObject}.
28257     *
28258     * @param  {Player~MediaObject} media
28259     *         A media object.
28260     *
28261     * @param  {Function} ready
28262     *         A callback to be called when the player is ready.
28263     */
28264    ;
28265
28266    _proto.loadMedia = function loadMedia(media, ready) {
28267      var _this15 = this;
28268
28269      if (!media || typeof media !== 'object') {
28270        return;
28271      }
28272
28273      this.reset(); // Clone the media object so it cannot be mutated from outside.
28274
28275      this.cache_.media = mergeOptions(media);
28276      var _this$cache_$media = this.cache_.media,
28277          artwork = _this$cache_$media.artwork,
28278          poster = _this$cache_$media.poster,
28279          src = _this$cache_$media.src,
28280          textTracks = _this$cache_$media.textTracks; // If `artwork` is not given, create it using `poster`.
28281
28282      if (!artwork && poster) {
28283        this.cache_.media.artwork = [{
28284          src: poster,
28285          type: getMimetype(poster)
28286        }];
28287      }
28288
28289      if (src) {
28290        this.src(src);
28291      }
28292
28293      if (poster) {
28294        this.poster(poster);
28295      }
28296
28297      if (Array.isArray(textTracks)) {
28298        textTracks.forEach(function (tt) {
28299          return _this15.addRemoteTextTrack(tt, false);
28300        });
28301      }
28302
28303      this.ready(ready);
28304    }
28305    /**
28306     * Get a clone of the current {@link Player~MediaObject} for this player.
28307     *
28308     * If the `loadMedia` method has not been used, will attempt to return a
28309     * {@link Player~MediaObject} based on the current state of the player.
28310     *
28311     * @return {Player~MediaObject}
28312     */
28313    ;
28314
28315    _proto.getMedia = function getMedia() {
28316      if (!this.cache_.media) {
28317        var poster = this.poster();
28318        var src = this.currentSources();
28319        var textTracks = Array.prototype.map.call(this.remoteTextTracks(), function (tt) {
28320          return {
28321            kind: tt.kind,
28322            label: tt.label,
28323            language: tt.language,
28324            src: tt.src
28325          };
28326        });
28327        var media = {
28328          src: src,
28329          textTracks: textTracks
28330        };
28331
28332        if (poster) {
28333          media.poster = poster;
28334          media.artwork = [{
28335            src: media.poster,
28336            type: getMimetype(media.poster)
28337          }];
28338        }
28339
28340        return media;
28341      }
28342
28343      return mergeOptions(this.cache_.media);
28344    }
28345    /**
28346     * Gets tag settings
28347     *
28348     * @param {Element} tag
28349     *        The player tag
28350     *
28351     * @return {Object}
28352     *         An object containing all of the settings
28353     *         for a player tag
28354     */
28355    ;
28356
28357    Player.getTagSettings = function getTagSettings(tag) {
28358      var baseOptions = {
28359        sources: [],
28360        tracks: []
28361      };
28362      var tagOptions = getAttributes(tag);
28363      var dataSetup = tagOptions['data-setup'];
28364
28365      if (hasClass(tag, 'vjs-fill')) {
28366        tagOptions.fill = true;
28367      }
28368
28369      if (hasClass(tag, 'vjs-fluid')) {
28370        tagOptions.fluid = true;
28371      } // Check if data-setup attr exists.
28372
28373      if (dataSetup !== null) {
28374        // Parse options JSON
28375        // If empty string, make it a parsable json object.
28376        var _safeParseTuple = tuple(dataSetup || '{}'),
28377            err = _safeParseTuple[0],
28378            data = _safeParseTuple[1];
vendor: 7,667 bytes, lines 28379-28608
28379
28380        if (err) {
28381          log.error(err);
28382        }
28383
28384        assign(tagOptions, data);
28385      }
28386
28387      assign(baseOptions, tagOptions); // Get tag children settings
28388
28389      if (tag.hasChildNodes()) {
28390        var children = tag.childNodes;
28391
28392        for (var i = 0, j = children.length; i < j; i++) {
28393          var child = children[i]; // Change case needed: http://ejohn.org/blog/nodename-case-sensitivity/
28394
28395          var childName = child.nodeName.toLowerCase();
28396
28397          if (childName === 'source') {
28398            baseOptions.sources.push(getAttributes(child));
28399          } else if (childName === 'track') {
28400            baseOptions.tracks.push(getAttributes(child));
28401          }
28402        }
28403      }
28404
28405      return baseOptions;
28406    }
28407    /**
28408     * Determine whether or not flexbox is supported
28409     *
28410     * @return {boolean}
28411     *         - true if flexbox is supported
28412     *         - false if flexbox is not supported
28413     */
28414    ;
28415
28416    _proto.flexNotSupported_ = function flexNotSupported_() {
28417      var elem = document.createElement('i'); // Note: We don't actually use flexBasis (or flexOrder), but it's one of the more
28418      // common flex features that we can rely on when checking for flex support.
28419
28420      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
28421      'msFlexOrder' in elem.style);
28422    };
28423
28424    return Player;
28425  }(Component);
28426  /**
28427   * Get the {@link VideoTrackList}
28428   * @link https://html.spec.whatwg.org/multipage/embedded-content.html#videotracklist
28429   *
28430   * @return {VideoTrackList}
28431   *         the current video track list
28432   *
28433   * @method Player.prototype.videoTracks
28434   */
28435
28436  /**
28437   * Get the {@link AudioTrackList}
28438   * @link https://html.spec.whatwg.org/multipage/embedded-content.html#audiotracklist
28439   *
28440   * @return {AudioTrackList}
28441   *         the current audio track list
28442   *
28443   * @method Player.prototype.audioTracks
28444   */
28445
28446  /**
28447   * Get the {@link TextTrackList}
28448   *
28449   * @link http://www.w3.org/html/wg/drafts/html/master/embedded-content-0.html#dom-media-texttracks
28450   *
28451   * @return {TextTrackList}
28452   *         the current text track list
28453   *
28454   * @method Player.prototype.textTracks
28455   */
28456
28457  /**
28458   * Get the remote {@link TextTrackList}
28459   *
28460   * @return {TextTrackList}
28461   *         The current remote text track list
28462   *
28463   * @method Player.prototype.remoteTextTracks
28464   */
28465
28466  /**
28467   * Get the remote {@link HtmlTrackElementList} tracks.
28468   *
28469   * @return {HtmlTrackElementList}
28470   *         The current remote text track element list
28471   *
28472   * @method Player.prototype.remoteTextTrackEls
28473   */
28474
28475
28476  ALL.names.forEach(function (name) {
28477    var props = ALL[name];
28478
28479    Player.prototype[props.getterName] = function () {
28480      if (this.tech_) {
28481        return this.tech_[props.getterName]();
28482      } // if we have not yet loadTech_, we create {video,audio,text}Tracks_
28483      // these will be passed to the tech during loading
28484
28485
28486      this[props.privateName] = this[props.privateName] || new props.ListClass();
28487      return this[props.privateName];
28488    };
28489  });
28490  /**
28491   * Global enumeration of players.
28492   *
28493   * The keys are the player IDs and the values are either the {@link Player}
28494   * instance or `null` for disposed players.
28495   *
28496   * @type {Object}
28497   */
28498
28499  Player.players = {};
28500  var navigator = window$1.navigator;
28501  /*
28502   * Player instance options, surfaced using options
28503   * options = Player.prototype.options_
28504   * Make changes in options, not here.
28505   *
28506   * @type {Object}
28507   * @private
28508   */
28509
28510  Player.prototype.options_ = {
28511    // Default order of fallback technology
28512    techOrder: Tech.defaultTechOrder_,
28513    html5: {},
28514    flash: {},
28515    // default inactivity timeout
28516    inactivityTimeout: 2000,
28517    // default playback rates
28518    playbackRates: [],
28519    // Add playback rate selection by adding rates
28520    // 'playbackRates': [0.5, 1, 1.5, 2],
28521    liveui: false,
28522    // Included control sets
28523    children: ['mediaLoader', 'posterImage', 'textTrackDisplay', 'loadingSpinner', 'bigPlayButton', 'liveTracker', 'controlBar', 'errorDisplay', 'textTrackSettings', 'resizeManager'],
28524    language: navigator && (navigator.languages && navigator.languages[0] || navigator.userLanguage || navigator.language) || 'en',
28525    // locales and their language translations
28526    languages: {},
28527    // Default message to show when a video cannot be played.
28528    notSupportedMessage: 'No compatible source was found for this media.',
28529    fullscreen: {
28530      options: {
28531        navigationUI: 'hide'
28532      }
28533    },
28534    breakpoints: {},
28535    responsive: false
28536  };
28537  [
28538  /**
28539   * Returns whether or not the player is in the "ended" state.
28540   *
28541   * @return {Boolean} True if the player is in the ended state, false if not.
28542   * @method Player#ended
28543   */
28544  'ended',
28545  /**
28546   * Returns whether or not the player is in the "seeking" state.
28547   *
28548   * @return {Boolean} True if the player is in the seeking state, false if not.
28549   * @method Player#seeking
28550   */
28551  'seeking',
28552  /**
28553   * Returns the TimeRanges of the media that are currently available
28554   * for seeking to.
28555   *
28556   * @return {TimeRanges} the seekable intervals of the media timeline
28557   * @method Player#seekable
28558   */
28559  'seekable',
28560  /**
28561   * Returns the current state of network activity for the element, from
28562   * the codes in the list below.
28563   * - NETWORK_EMPTY (numeric value 0)
28564   *   The element has not yet been initialised. All attributes are in
28565   *   their initial states.
28566   * - NETWORK_IDLE (numeric value 1)
28567   *   The element's resource selection algorithm is active and has
28568   *   selected a resource, but it is not actually using the network at
28569   *   this time.
28570   * - NETWORK_LOADING (numeric value 2)
28571   *   The user agent is actively trying to download data.
28572   * - NETWORK_NO_SOURCE (numeric value 3)
28573   *   The element's resource selection algorithm is active, but it has
28574   *   not yet found a resource to use.
28575   *
28576   * @see https://html.spec.whatwg.org/multipage/embedded-content.html#network-states
28577   * @return {number} the current network activity state
28578   * @method Player#networkState
28579   */
28580  'networkState',
28581  /**
28582   * Returns a value that expresses the current state of the element
28583   * with respect to rendering the current playback position, from the
28584   * codes in the list below.
28585   * - HAVE_NOTHING (numeric value 0)
28586   *   No information regarding the media resource is available.
28587   * - HAVE_METADATA (numeric value 1)
28588   *   Enough of the resource has been obtained that the duration of the
28589   *   resource is available.
28590   * - HAVE_CURRENT_DATA (numeric value 2)
28591   *   Data for the immediate current playback position is available.
28592   * - HAVE_FUTURE_DATA (numeric value 3)
28593   *   Data for the immediate current playback position is available, as
28594   *   well as enough data for the user agent to advance the current
28595   *   playback position in the direction of playback.
28596   * - HAVE_ENOUGH_DATA (numeric value 4)
28597   *   The user agent estimates that enough data is available for
28598   *   playback to proceed uninterrupted.
28599   *
28600   * @see https://html.spec.whatwg.org/multipage/embedded-content.html#dom-media-readystate
28601   * @return {number} the current playback rendering state
28602   * @method Player#readyState
28603   */
28604  'readyState'].forEach(function (fn) {
28605    Player.prototype[fn] = function () {
28606      return this.techGet_(fn);
28607    };
28608  });
vendor: 5,424 bytes, lines 28609-28806
28609  TECH_EVENTS_RETRIGGER.forEach(function (event) {
28610    Player.prototype["handleTech" + toTitleCase(event) + "_"] = function () {
28611      return this.trigger(event);
28612    };
28613  });
28614  /**
28615   * Fired when the player has initial duration and dimension information
28616   *
28617   * @event Player#loadedmetadata
28618   * @type {EventTarget~Event}
28619   */
28620
28621  /**
28622   * Fired when the player has downloaded data at the current playback position
28623   *
28624   * @event Player#loadeddata
28625   * @type {EventTarget~Event}
28626   */
28627
28628  /**
28629   * Fired when the current playback position has changed *
28630   * During playback this is fired every 15-250 milliseconds, depending on the
28631   * playback technology in use.
28632   *
28633   * @event Player#timeupdate
28634   * @type {EventTarget~Event}
28635   */
28636
28637  /**
28638   * Fired when the volume changes
28639   *
28640   * @event Player#volumechange
28641   * @type {EventTarget~Event}
28642   */
28643
28644  /**
28645   * Reports whether or not a player has a plugin available.
28646   *
28647   * This does not report whether or not the plugin has ever been initialized
28648   * on this player. For that, [usingPlugin]{@link Player#usingPlugin}.
28649   *
28650   * @method Player#hasPlugin
28651   * @param  {string}  name
28652   *         The name of a plugin.
28653   *
28654   * @return {boolean}
28655   *         Whether or not this player has the requested plugin available.
28656   */
28657
28658  /**
28659   * Reports whether or not a player is using a plugin by name.
28660   *
28661   * For basic plugins, this only reports whether the plugin has _ever_ been
28662   * initialized on this player.
28663   *
28664   * @method Player#usingPlugin
28665   * @param  {string} name
28666   *         The name of a plugin.
28667   *
28668   * @return {boolean}
28669   *         Whether or not this player is using the requested plugin.
28670   */
28671
28672  Component.registerComponent('Player', Player);
28673
28674  /**
28675   * The base plugin name.
28676   *
28677   * @private
28678   * @constant
28679   * @type {string}
28680   */
28681
28682  var BASE_PLUGIN_NAME = 'plugin';
28683  /**
28684   * The key on which a player's active plugins cache is stored.
28685   *
28686   * @private
28687   * @constant
28688   * @type     {string}
28689   */
28690
28691  var PLUGIN_CACHE_KEY = 'activePlugins_';
28692  /**
28693   * Stores registered plugins in a private space.
28694   *
28695   * @private
28696   * @type    {Object}
28697   */
28698
28699  var pluginStorage = {};
28700  /**
28701   * Reports whether or not a plugin has been registered.
28702   *
28703   * @private
28704   * @param   {string} name
28705   *          The name of a plugin.
28706   *
28707   * @return {boolean}
28708   *          Whether or not the plugin has been registered.
28709   */
28710
28711  var pluginExists = function pluginExists(name) {
28712    return pluginStorage.hasOwnProperty(name);
28713  };
28714  /**
28715   * Get a single registered plugin by name.
28716   *
28717   * @private
28718   * @param   {string} name
28719   *          The name of a plugin.
28720   *
28721   * @return {Function|undefined}
28722   *          The plugin (or undefined).
28723   */
28724
28725
28726  var getPlugin = function getPlugin(name) {
28727    return pluginExists(name) ? pluginStorage[name] : undefined;
28728  };
28729  /**
28730   * Marks a plugin as "active" on a player.
28731   *
28732   * Also, ensures that the player has an object for tracking active plugins.
28733   *
28734   * @private
28735   * @param   {Player} player
28736   *          A Video.js player instance.
28737   *
28738   * @param   {string} name
28739   *          The name of a plugin.
28740   */
28741
28742
28743  var markPluginAsActive = function markPluginAsActive(player, name) {
28744    player[PLUGIN_CACHE_KEY] = player[PLUGIN_CACHE_KEY] || {};
28745    player[PLUGIN_CACHE_KEY][name] = true;
28746  };
28747  /**
28748   * Triggers a pair of plugin setup events.
28749   *
28750   * @private
28751   * @param  {Player} player
28752   *         A Video.js player instance.
28753   *
28754   * @param  {Plugin~PluginEventHash} hash
28755   *         A plugin event hash.
28756   *
28757   * @param  {boolean} [before]
28758   *         If true, prefixes the event name with "before". In other words,
28759   *         use this to trigger "beforepluginsetup" instead of "pluginsetup".
28760   */
28761
28762
28763  var triggerSetupEvent = function triggerSetupEvent(player, hash, before) {
28764    var eventName = (before ? 'before' : '') + 'pluginsetup';
28765    player.trigger(eventName, hash);
28766    player.trigger(eventName + ':' + hash.name, hash);
28767  };
28768  /**
28769   * Takes a basic plugin function and returns a wrapper function which marks
28770   * on the player that the plugin has been activated.
28771   *
28772   * @private
28773   * @param   {string} name
28774   *          The name of the plugin.
28775   *
28776   * @param   {Function} plugin
28777   *          The basic plugin.
28778   *
28779   * @return {Function}
28780   *          A wrapper function for the given plugin.
28781   */
28782
28783
28784  var createBasicPlugin = function createBasicPlugin(name, plugin) {
28785    var basicPluginWrapper = function basicPluginWrapper() {
28786      // We trigger the "beforepluginsetup" and "pluginsetup" events on the player
28787      // regardless, but we want the hash to be consistent with the hash provided
28788      // for advanced plugins.
28789      //
28790      // The only potentially counter-intuitive thing here is the `instance` in
28791      // the "pluginsetup" event is the value returned by the `plugin` function.
28792      triggerSetupEvent(this, {
28793        name: name,
28794        plugin: plugin,
28795        instance: null
28796      }, true);
28797      var instance = plugin.apply(this, arguments);
28798      markPluginAsActive(this, name);
28799      triggerSetupEvent(this, {
28800        name: name,
28801        plugin: plugin,
28802        instance: instance
28803      });
28804      return instance;
28805    };
28806
vendor: 9,188 bytes, lines 28807-29088
28807    Object.keys(plugin).forEach(function (prop) {
28808      basicPluginWrapper[prop] = plugin[prop];
28809    });
28810    return basicPluginWrapper;
28811  };
28812  /**
28813   * Takes a plugin sub-class and returns a factory function for generating
28814   * instances of it.
28815   *
28816   * This factory function will replace itself with an instance of the requested
28817   * sub-class of Plugin.
28818   *
28819   * @private
28820   * @param   {string} name
28821   *          The name of the plugin.
28822   *
28823   * @param   {Plugin} PluginSubClass
28824   *          The advanced plugin.
28825   *
28826   * @return {Function}
28827   */
28828
28829
28830  var createPluginFactory = function createPluginFactory(name, PluginSubClass) {
28831    // Add a `name` property to the plugin prototype so that each plugin can
28832    // refer to itself by name.
28833    PluginSubClass.prototype.name = name;
28834    return function () {
28835      triggerSetupEvent(this, {
28836        name: name,
28837        plugin: PluginSubClass,
28838        instance: null
28839      }, true);
28840
28841      for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) {
28842        args[_key] = arguments[_key];
28843      }
28844
28845      var instance = _construct(PluginSubClass, [this].concat(args)); // The plugin is replaced by a function that returns the current instance.
28846
28847
28848      this[name] = function () {
28849        return instance;
28850      };
28851
28852      triggerSetupEvent(this, instance.getEventHash());
28853      return instance;
28854    };
28855  };
28856  /**
28857   * Parent class for all advanced plugins.
28858   *
28859   * @mixes   module:evented~EventedMixin
28860   * @mixes   module:stateful~StatefulMixin
28861   * @fires   Player#beforepluginsetup
28862   * @fires   Player#beforepluginsetup:$name
28863   * @fires   Player#pluginsetup
28864   * @fires   Player#pluginsetup:$name
28865   * @listens Player#dispose
28866   * @throws  {Error}
28867   *          If attempting to instantiate the base {@link Plugin} class
28868   *          directly instead of via a sub-class.
28869   */
28870
28871
28872  var Plugin =
28873  /*#__PURE__*/
28874  function () {
28875    /**
28876     * Creates an instance of this class.
28877     *
28878     * Sub-classes should call `super` to ensure plugins are properly initialized.
28879     *
28880     * @param {Player} player
28881     *        A Video.js player instance.
28882     */
28883    function Plugin(player) {
28884      if (this.constructor === Plugin) {
28885        throw new Error('Plugin must be sub-classed; not directly instantiated.');
28886      }
28887
28888      this.player = player; // Make this object evented, but remove the added `trigger` method so we
28889      // use the prototype version instead.
28890
28891      evented(this);
28892      delete this.trigger;
28893      stateful(this, this.constructor.defaultState);
28894      markPluginAsActive(player, this.name); // Auto-bind the dispose method so we can use it as a listener and unbind
28895      // it later easily.
28896
28897      this.dispose = bind(this, this.dispose); // If the player is disposed, dispose the plugin.
28898
28899      player.on('dispose', this.dispose);
28900    }
28901    /**
28902     * Get the version of the plugin that was set on <pluginName>.VERSION
28903     */
28904
28905
28906    var _proto = Plugin.prototype;
28907
28908    _proto.version = function version() {
28909      return this.constructor.VERSION;
28910    }
28911    /**
28912     * Each event triggered by plugins includes a hash of additional data with
28913     * conventional properties.
28914     *
28915     * This returns that object or mutates an existing hash.
28916     *
28917     * @param   {Object} [hash={}]
28918     *          An object to be used as event an event hash.
28919     *
28920     * @return {Plugin~PluginEventHash}
28921     *          An event hash object with provided properties mixed-in.
28922     */
28923    ;
28924
28925    _proto.getEventHash = function getEventHash(hash) {
28926      if (hash === void 0) {
28927        hash = {};
28928      }
28929
28930      hash.name = this.name;
28931      hash.plugin = this.constructor;
28932      hash.instance = this;
28933      return hash;
28934    }
28935    /**
28936     * Triggers an event on the plugin object and overrides
28937     * {@link module:evented~EventedMixin.trigger|EventedMixin.trigger}.
28938     *
28939     * @param   {string|Object} event
28940     *          An event type or an object with a type property.
28941     *
28942     * @param   {Object} [hash={}]
28943     *          Additional data hash to merge with a
28944     *          {@link Plugin~PluginEventHash|PluginEventHash}.
28945     *
28946     * @return {boolean}
28947     *          Whether or not default was prevented.
28948     */
28949    ;
28950
28951    _proto.trigger = function trigger$1(event, hash) {
28952      if (hash === void 0) {
28953        hash = {};
28954      }
28955
28956      return trigger(this.eventBusEl_, event, this.getEventHash(hash));
28957    }
28958    /**
28959     * Handles "statechanged" events on the plugin. No-op by default, override by
28960     * subclassing.
28961     *
28962     * @abstract
28963     * @param    {Event} e
28964     *           An event object provided by a "statechanged" event.
28965     *
28966     * @param    {Object} e.changes
28967     *           An object describing changes that occurred with the "statechanged"
28968     *           event.
28969     */
28970    ;
28971
28972    _proto.handleStateChanged = function handleStateChanged(e) {}
28973    /**
28974     * Disposes a plugin.
28975     *
28976     * Subclasses can override this if they want, but for the sake of safety,
28977     * it's probably best to subscribe the "dispose" event.
28978     *
28979     * @fires Plugin#dispose
28980     */
28981    ;
28982
28983    _proto.dispose = function dispose() {
28984      var name = this.name,
28985          player = this.player;
28986      /**
28987       * Signals that a advanced plugin is about to be disposed.
28988       *
28989       * @event Plugin#dispose
28990       * @type  {EventTarget~Event}
28991       */
28992
28993      this.trigger('dispose');
28994      this.off();
28995      player.off('dispose', this.dispose); // Eliminate any possible sources of leaking memory by clearing up
28996      // references between the player and the plugin instance and nulling out
28997      // the plugin's state and replacing methods with a function that throws.
28998
28999      player[PLUGIN_CACHE_KEY][name] = false;
29000      this.player = this.state = null; // Finally, replace the plugin name on the player with a new factory
29001      // function, so that the plugin is ready to be set up again.
29002
29003      player[name] = createPluginFactory(name, pluginStorage[name]);
29004    }
29005    /**
29006     * Determines if a plugin is a basic plugin (i.e. not a sub-class of `Plugin`).
29007     *
29008     * @param   {string|Function} plugin
29009     *          If a string, matches the name of a plugin. If a function, will be
29010     *          tested directly.
29011     *
29012     * @return {boolean}
29013     *          Whether or not a plugin is a basic plugin.
29014     */
29015    ;
29016
29017    Plugin.isBasic = function isBasic(plugin) {
29018      var p = typeof plugin === 'string' ? getPlugin(plugin) : plugin;
29019      return typeof p === 'function' && !Plugin.prototype.isPrototypeOf(p.prototype);
29020    }
29021    /**
29022     * Register a Video.js plugin.
29023     *
29024     * @param   {string} name
29025     *          The name of the plugin to be registered. Must be a string and
29026     *          must not match an existing plugin or a method on the `Player`
29027     *          prototype.
29028     *
29029     * @param   {Function} plugin
29030     *          A sub-class of `Plugin` or a function for basic plugins.
29031     *
29032     * @return {Function}
29033     *          For advanced plugins, a factory function for that plugin. For
29034     *          basic plugins, a wrapper function that initializes the plugin.
29035     */
29036    ;
29037
29038    Plugin.registerPlugin = function registerPlugin(name, plugin) {
29039      if (typeof name !== 'string') {
29040        throw new Error("Illegal plugin name, \"" + name + "\", must be a string, was " + typeof name + ".");
29041      }
29042
29043      if (pluginExists(name)) {
29044        log.warn("A plugin named \"" + name + "\" already exists. You may want to avoid re-registering plugins!");
29045      } else if (Player.prototype.hasOwnProperty(name)) {
29046        throw new Error("Illegal plugin name, \"" + name + "\", cannot share a name with an existing player method!");
29047      }
29048
29049      if (typeof plugin !== 'function') {
29050        throw new Error("Illegal plugin for \"" + name + "\", must be a function, was " + typeof plugin + ".");
29051      }
29052
29053      pluginStorage[name] = plugin; // Add a player prototype method for all sub-classed plugins (but not for
29054      // the base Plugin class).
29055
29056      if (name !== BASE_PLUGIN_NAME) {
29057        if (Plugin.isBasic(plugin)) {
29058          Player.prototype[name] = createBasicPlugin(name, plugin);
29059        } else {
29060          Player.prototype[name] = createPluginFactory(name, plugin);
29061        }
29062      }
29063
29064      return plugin;
29065    }
29066    /**
29067     * De-register a Video.js plugin.
29068     *
29069     * @param  {string} name
29070     *         The name of the plugin to be de-registered. Must be a string that
29071     *         matches an existing plugin.
29072     *
29073     * @throws {Error}
29074     *         If an attempt is made to de-register the base plugin.
29075     */
29076    ;
29077
29078    Plugin.deregisterPlugin = function deregisterPlugin(name) {
29079      if (name === BASE_PLUGIN_NAME) {
29080        throw new Error('Cannot de-register base plugin.');
29081      }
29082
29083      if (pluginExists(name)) {
29084        delete pluginStorage[name];
29085        delete Player.prototype[name];
29086      }
29087    }
29088    /**
29089     * Gets an object containing multiple Video.js plugins.
29090     *
29091     * @param   {Array} [names]
29092     *          If provided, should be an array of plugin names. Defaults to _all_
29093     *          plugin names.
29094     *
29095     * @return {Object|undefined}
29096     *          An object containing plugin(s) associated with their name(s) or
29097     *          `undefined` if no matching plugins exist).
29098     */
29099    ;
29100
29101    Plugin.getPlugins = function getPlugins(names) {
29102      if (names === void 0) {
29103        names = Object.keys(pluginStorage);
29104      }
29105
29106      var result;
29107      names.forEach(function (name) {
29108        var plugin = getPlugin(name);
29109
29110        if (plugin) {
29111          result = result || {};
29112          result[name] = plugin;
29113        }
29114      });
29115      return result;
29116    }
29117    /**
29118     * Gets a plugin's version, if available
29119     *
29120     * @param   {string} name
29121     *          The name of a plugin.
29122     *
29123     * @return {string}
29124     *          The plugin's version or an empty string.
29125     */
29126    ;
29127
29128    Plugin.getPluginVersion = function getPluginVersion(name) {
29129      var plugin = getPlugin(name);
29130      return plugin && plugin.VERSION || '';
29131    };
29132
29133    return Plugin;
29134  }();
29135  /**
29136   * Gets a plugin by name if it exists.
29137   *
29138   * @static
29139   * @method   getPlugin
29140   * @memberOf Plugin
29141   * @param    {string} name
29142   *           The name of a plugin.
29143   *
29144   * @returns  {Function|undefined}
29145   *           The plugin (or `undefined`).
29146   */
29147
29148
29149  Plugin.getPlugin = getPlugin;
29150  /**
29151   * The name of the base plugin class as it is registered.
29152   *
29153   * @type {string}
29154   */
29155
29156  Plugin.BASE_PLUGIN_NAME = BASE_PLUGIN_NAME;
29157  Plugin.registerPlugin(BASE_PLUGIN_NAME, Plugin);
29158  /**
29159   * Documented in player.js
29160   *
29161   * @ignore
29162   */
29163
29164  Player.prototype.usingPlugin = function (name) {
29165    return !!this[PLUGIN_CACHE_KEY] && this[PLUGIN_CACHE_KEY][name] === true;
29166  };
29167  /**
29168   * Documented in player.js
29169   *
29170   * @ignore
29171   */
29172
29173
29174  Player.prototype.hasPlugin = function (name) {
29175    return !!pluginExists(name);
29176  };
29177  /**
29178   * Signals that a plugin is about to be set up on a player.
29179   *
29180   * @event    Player#beforepluginsetup
29181   * @type     {Plugin~PluginEventHash}
29182   */
29183
29184  /**
29185   * Signals that a plugin is about to be set up on a player - by name. The name
29186   * is the name of the plugin.
29187   *
29188   * @event    Player#beforepluginsetup:$name
29189   * @type     {Plugin~PluginEventHash}
29190   */
29191
29192  /**
29193   * Signals that a plugin has just been set up on a player.
29194   *
29195   * @event    Player#pluginsetup
29196   * @type     {Plugin~PluginEventHash}
29197   */
29198
29199  /**
29200   * Signals that a plugin has just been set up on a player - by name. The name
29201   * is the name of the plugin.
29202   *
29203   * @event    Player#pluginsetup:$name
29204   * @type     {Plugin~PluginEventHash}
29205   */
29206
29207  /**
29208   * @typedef  {Object} Plugin~PluginEventHash
29209   *
29210   * @property {string} instance
29211   *           For basic plugins, the return value of the plugin function. For
29212   *           advanced plugins, the plugin instance on which the event is fired.
29213   *
29214   * @property {string} name
29215   *           The name of the plugin.
29216   *
29217   * @property {string} plugin
29218   *           For basic plugins, the plugin function. For advanced plugins, the
29219   *           plugin class/constructor.
29220   */
29221
29222  /**
29223   * @file extend.js
29224   * @module extend
29225   */
29226
29227  /**
29228   * A combination of node inherits and babel's inherits (after transpile).
29229   * Both work the same but node adds `super_` to the subClass
29230   * and Bable adds the superClass as __proto__. Both seem useful.
29231   *
29232   * @param {Object} subClass
29233   *        The class to inherit to
29234   *
29235   * @param {Object} superClass
29236   *        The class to inherit from
29237   *
29238   * @private
29239   */
29240  var _inherits = function _inherits(subClass, superClass) {
29241    if (typeof superClass !== 'function' && superClass !== null) {
29242      throw new TypeError('Super expression must either be null or a function, not ' + typeof superClass);
29243    }
29244
29245    subClass.prototype = Object.create(superClass && superClass.prototype, {
29246      constructor: {
29247        value: subClass,
29248        enumerable: false,
29249        writable: true,
29250        configurable: true
29251      }
29252    });
vendor: 4,752 bytes, lines 29253-29398
29253
29254    if (superClass) {
29255      // node
29256      subClass.super_ = superClass;
29257    }
29258  };
29259  /**
29260   * Used to subclass an existing class by emulating ES subclassing using the
29261   * `extends` keyword.
29262   *
29263   * @function
29264   * @example
29265   * var MyComponent = videojs.extend(videojs.getComponent('Component'), {
29266   *   myCustomMethod: function() {
29267   *     // Do things in my method.
29268   *   }
29269   * });
29270   *
29271   * @param    {Function} superClass
29272   *           The class to inherit from
29273   *
29274   * @param    {Object}   [subClassMethods={}]
29275   *           Methods of the new class
29276   *
29277   * @return   {Function}
29278   *           The new class with subClassMethods that inherited superClass.
29279   */
29280
29281
29282  var extend$1 = function extend(superClass, subClassMethods) {
29283    if (subClassMethods === void 0) {
29284      subClassMethods = {};
29285    }
29286
29287    var subClass = function subClass() {
29288      superClass.apply(this, arguments);
29289    };
29290
29291    var methods = {};
29292
29293    if (typeof subClassMethods === 'object') {
29294      if (subClassMethods.constructor !== Object.prototype.constructor) {
29295        subClass = subClassMethods.constructor;
29296      }
29297
29298      methods = subClassMethods;
29299    } else if (typeof subClassMethods === 'function') {
29300      subClass = subClassMethods;
29301    }
29302
29303    _inherits(subClass, superClass); // Extend subObj's prototype with functions and other properties from props
29304
29305
29306    for (var name in methods) {
29307      if (methods.hasOwnProperty(name)) {
29308        subClass.prototype[name] = methods[name];
29309      }
29310    }
29311
29312    return subClass;
29313  };
29314
29315  /**
29316   * @file video.js
29317   * @module videojs
29318   */
29319  /**
29320   * Normalize an `id` value by trimming off a leading `#`
29321   *
29322   * @private
29323   * @param   {string} id
29324   *          A string, maybe with a leading `#`.
29325   *
29326   * @return {string}
29327   *          The string, without any leading `#`.
29328   */
29329
29330  var normalizeId = function normalizeId(id) {
29331    return id.indexOf('#') === 0 ? id.slice(1) : id;
29332  };
29333  /**
29334   * The `videojs()` function doubles as the main function for users to create a
29335   * {@link Player} instance as well as the main library namespace.
29336   *
29337   * It can also be used as a getter for a pre-existing {@link Player} instance.
29338   * However, we _strongly_ recommend using `videojs.getPlayer()` for this
29339   * purpose because it avoids any potential for unintended initialization.
29340   *
29341   * Due to [limitations](https://github.com/jsdoc3/jsdoc/issues/955#issuecomment-313829149)
29342   * of our JSDoc template, we cannot properly document this as both a function
29343   * and a namespace, so its function signature is documented here.
29344   *
29345   * #### Arguments
29346   * ##### id
29347   * string|Element, **required**
29348   *
29349   * Video element or video element ID.
29350   *
29351   * ##### options
29352   * Object, optional
29353   *
29354   * Options object for providing settings.
29355   * See: [Options Guide](https://docs.videojs.com/tutorial-options.html).
29356   *
29357   * ##### ready
29358   * {@link Component~ReadyCallback}, optional
29359   *
29360   * A function to be called when the {@link Player} and {@link Tech} are ready.
29361   *
29362   * #### Return Value
29363   *
29364   * The `videojs()` function returns a {@link Player} instance.
29365   *
29366   * @namespace
29367   *
29368   * @borrows AudioTrack as AudioTrack
29369   * @borrows Component.getComponent as getComponent
29370   * @borrows module:computed-style~computedStyle as computedStyle
29371   * @borrows module:events.on as on
29372   * @borrows module:events.one as one
29373   * @borrows module:events.off as off
29374   * @borrows module:events.trigger as trigger
29375   * @borrows EventTarget as EventTarget
29376   * @borrows module:extend~extend as extend
29377   * @borrows module:fn.bind as bind
29378   * @borrows module:format-time.formatTime as formatTime
29379   * @borrows module:format-time.resetFormatTime as resetFormatTime
29380   * @borrows module:format-time.setFormatTime as setFormatTime
29381   * @borrows module:merge-options.mergeOptions as mergeOptions
29382   * @borrows module:middleware.use as use
29383   * @borrows Player.players as players
29384   * @borrows Plugin.registerPlugin as registerPlugin
29385   * @borrows Plugin.deregisterPlugin as deregisterPlugin
29386   * @borrows Plugin.getPlugins as getPlugins
29387   * @borrows Plugin.getPlugin as getPlugin
29388   * @borrows Plugin.getPluginVersion as getPluginVersion
29389   * @borrows Tech.getTech as getTech
29390   * @borrows Tech.registerTech as registerTech
29391   * @borrows TextTrack as TextTrack
29392   * @borrows module:time-ranges.createTimeRanges as createTimeRange
29393   * @borrows module:time-ranges.createTimeRanges as createTimeRanges
29394   * @borrows module:url.isCrossOrigin as isCrossOrigin
29395   * @borrows module:url.parseUrl as parseUrl
29396   * @borrows VideoTrack as VideoTrack
29397   *
29398   * @param  {string|Element}
29398 id
29399   *         Video element or video element ID.
29400   *
29401   * @param  {Object} [options]
29402   *         Options object for providing settings.
29403   *         See: [Options Guide](https://docs.videojs.com/tutorial-options.html).
29404   *
29405   * @param  {Component~ReadyCallback} [ready]
29406   *         A function to be called when the {@link Player} and {@link Tech} are
29407   *         ready.
29408   *
29409   * @return {Player}
29410   *         The `videojs()` function returns a {@link Player|Player} instance.
29411   */
29412
29413
29414  function videojs$1(id, options, ready) {
29415    var player = videojs$1.getPlayer(id);
29416
29417    if (player) {
29418      if (options) {
29419        log.warn("Player \"" + id + "\" is already initialised. Options will not be applied.");
29420      }
29421
29422      if (ready) {
29423        player.ready(ready);
29424      }
29425
29426      return player;
29427    }
29428
29429    var el = typeof id === 'string' ? $('#' + normalizeId(id)) : id;
29430
29431    if (!isEl(el)) {
29432      throw new TypeError('The element or ID supplied is not valid. (videojs)');
29433    } // document.body.contains(el) will only check if el is contained within that one document.
29434    // This causes problems for elements in iframes.
29435    // Instead, use the element's ownerDocument instead of the global document.
29436    // This will make sure that the element is indeed in the dom of that document.
29437    // Additionally, check that the document in question has a default view.
29438    // If the document is no longer attached to the dom, the defaultView of the document will be null.
29439
29440
29441    if (!el.ownerDocument.defaultView || !el.ownerDocument.body.contains(el)) {
29442      log.warn('The element supplied is not included in the DOM');
29443    }
29444
29445    options = options || {};
29446    videojs$1.hooks('beforesetup').forEach(function (hookFunction) {
29447      var opts = hookFunction(el, mergeOptions(options));
29448
29449      if (!isObject(opts) || Array.isArray(opts)) {
29450        log.error('please return an object in beforesetup hooks');
29451        return;
29452      }
29453
29454      options = mergeOptions(options, opts);
29455    }); // We get the current "Player" component here in case an integration has
29456    // replaced it with a custom player.
29457
29458    var PlayerComponent = Component.getComponent('Player');
29459    player = new PlayerComponent(el, options, ready);
29460    videojs$1.hooks('setup').forEach(function (hookFunction) {
29461      return hookFunction(player);
29462    });
29463    return player;
29464  }
29465  /**
29466   * An Object that contains lifecycle hooks as keys which point to an array
29467   * of functions that are run when a lifecycle is triggered
29468   *
29469   * @private
29470   */
29471
29472
29473  videojs$1.hooks_ = {};
29474  /**
29475   * Get a list of hooks for a specific lifecycle
29476   *
29477   * @param  {string} type
29478   *         the lifecyle to get hooks from
29479   *
29480   * @param  {Function|Function[]} [fn]
29481   *         Optionally add a hook (or hooks) to the lifecycle that your are getting.
29482   *
29483   * @return {Array}
29484   *         an array of hooks, or an empty array if there are none.
29485   */
29486
29487  videojs$1.hooks = function (type, fn) {
29488    videojs$1.hooks_[type] = videojs$1.hooks_[type] || [];
29489
29490    if (fn) {
29491      videojs$1.hooks_[type] = videojs$1.hooks_[type].concat(fn);
29492    }
29493
29494    return videojs$1.hooks_[type];
29495  };
29496  /**
29497   * Add a function hook to a specific videojs lifecycle.
29498   *
29499   * @param {string} type
29500   *        the lifecycle to hook the function to.
29501   *
29502   * @param {Function|Function[]}
29503   *        The function or array of functions to attach.
29504   */
29505
29506
29507  videojs$1.hook = function (type, fn) {
29508    videojs$1.hooks(type, fn);
29509  };
29510  /**
29511   * Add a function hook that will only run once to a specific videojs lifecycle.
29512   *
29513   * @param {string} type
29514   *        the lifecycle to hook the function to.
29515   *
29516   * @param {Function|Function[]}
29517   *        The function or array of functions to attach.
29518   */
29519
29520
29521  videojs$1.hookOnce = function (type, fn) {
29522    videojs$1.hooks(type, [].concat(fn).map(function (original) {
29523      var wrapper = function wrapper() {
29524        videojs$1.removeHook(type, wrapper);
29525        return original.apply(void 0, arguments);
29526      };
29527
29528      return wrapper;
29529    }));
29530  };
29531  /**
29532   * Remove a hook from a specific videojs lifecycle.
29533   *
29534   * @param  {string} type
29535   *         the lifecycle that the function hooked to
29536   *
29537   * @param  {Function} fn
29538   *         The hooked function to remove
29539   *
29540   * @return {boolean}
29541   *         The function that was removed or undef
29542   */
29543
29544
vendor: 13,799 bytes, lines 29545-29947
29545  videojs$1.removeHook = function (type, fn) {
29546    var index = videojs$1.hooks(type).indexOf(fn);
29547
29548    if (index <= -1) {
29549      return false;
29550    }
29551
29552    videojs$1.hooks_[type] = videojs$1.hooks_[type].slice();
29553    videojs$1.hooks_[type].splice(index, 1);
29554    return true;
29555  }; // Add default styles
29556
29557
29558  if (window$1.VIDEOJS_NO_DYNAMIC_STYLE !== true && isReal()) {
29559    var style = $('.vjs-styles-defaults');
29560
29561    if (!style) {
29562      style = createStyleElement('vjs-styles-defaults');
29563      var head = $('head');
29564
29565      if (head) {
29566        head.insertBefore(style, head.firstChild);
29567      }
29568
29569      setTextContent(style, "\n      .video-js {\n        width: 100%;\n        height: 100%;\n      }\n\n      .vjs-fluid {\n        padding-top: 56.25%\n      }\n    ");
29570    }
29571  } // Run Auto-load players
29572  // You have to wait at least once in case this script is loaded after your
29573  // video in the DOM (weird behavior only with minified version)
29574
29575
29576  autoSetupTimeout(1, videojs$1);
29577  /**
29578   * Current Video.js version. Follows [semantic versioning](https://semver.org/).
29579   *
29580   * @type {string}
29581   */
29582
29583  videojs$1.VERSION = version;
29584  /**
29585   * The global options object. These are the settings that take effect
29586   * if no overrides are specified when the player is created.
29587   *
29588   * @type {Object}
29589   */
29590
29591  videojs$1.options = Player.prototype.options_;
29592  /**
29593   * Get an object with the currently created players, keyed by player ID
29594   *
29595   * @return {Object}
29596   *         The created players
29597   */
29598
29599  videojs$1.getPlayers = function () {
29600    return Player.players;
29601  };
29602  /**
29603   * Get a single player based on an ID or DOM element.
29604   *
29605   * This is useful if you want to check if an element or ID has an associated
29606   * Video.js player, but not create one if it doesn't.
29607   *
29608   * @param   {string|Element} id
29609   *          An HTML element - `<video>`, `<audio>`, or `<video-js>` -
29610   *          or a string matching the `id` of such an element.
29611   *
29612   * @return {Player|undefined}
29613   *          A player instance or `undefined` if there is no player instance
29614   *          matching the argument.
29615   */
29616
29617
29618  videojs$1.getPlayer = function (id) {
29619    var players = Player.players;
29620    var tag;
29621
29622    if (typeof id === 'string') {
29623      var nId = normalizeId(id);
29624      var player = players[nId];
29625
29626      if (player) {
29627        return player;
29628      }
29629
29630      tag = $('#' + nId);
29631    } else {
29632      tag = id;
29633    }
29634
29635    if (isEl(tag)) {
29636      var _tag = tag,
29637          _player = _tag.player,
29638          playerId = _tag.playerId; // Element may have a `player` property referring to an already created
29639      // player instance. If so, return that.
29640
29641      if (_player || players[playerId]) {
29642        return _player || players[playerId];
29643      }
29644    }
29645  };
29646  /**
29647   * Returns an array of all current players.
29648   *
29649   * @return {Array}
29650   *         An array of all players. The array will be in the order that
29651   *         `Object.keys` provides, which could potentially vary between
29652   *         JavaScript engines.
29653   *
29654   */
29655
29656
29657  videojs$1.getAllPlayers = function () {
29658    return (// Disposed players leave a key with a `null` value, so we need to make sure
29659      // we filter those out.
29660      Object.keys(Player.players).map(function (k) {
29661        return Player.players[k];
29662      }).filter(Boolean)
29663    );
29664  };
29665
29666  videojs$1.players = Player.players;
29667  videojs$1.getComponent = Component.getComponent;
29668  /**
29669   * Register a component so it can referred to by name. Used when adding to other
29670   * components, either through addChild `component.addChild('myComponent')` or through
29671   * default children options  `{ children: ['myComponent'] }`.
29672   *
29673   * > NOTE: You could also just initialize the component before adding.
29674   * `component.addChild(new MyComponent());`
29675   *
29676   * @param {string} name
29677   *        The class name of the component
29678   *
29679   * @param {Component} comp
29680   *        The component class
29681   *
29682   * @return {Component}
29683   *         The newly registered component
29684   */
29685
29686  videojs$1.registerComponent = function (name, comp) {
29687    if (Tech.isTech(comp)) {
29688      log.warn("The " + name + " tech was registered as a component. It should instead be registered using videojs.registerTech(name, tech)");
29689    }
29690
29691    Component.registerComponent.call(Component, name, comp);
29692  };
29693
29694  videojs$1.getTech = Tech.getTech;
29695  videojs$1.registerTech = Tech.registerTech;
29696  videojs$1.use = use;
29697  /**
29698   * An object that can be returned by a middleware to signify
29699   * that the middleware is being terminated.
29700   *
29701   * @type {object}
29702   * @property {object} middleware.TERMINATOR
29703   */
29704
29705  Object.defineProperty(videojs$1, 'middleware', {
29706    value: {},
29707    writeable: false,
29708    enumerable: true
29709  });
29710  Object.defineProperty(videojs$1.middleware, 'TERMINATOR', {
29711    value: TERMINATOR,
29712    writeable: false,
29713    enumerable: true
29714  });
29715  /**
29716   * A reference to the {@link module:browser|browser utility module} as an object.
29717   *
29718   * @type {Object}
29719   * @see  {@link module:browser|browser}
29720   */
29721
29722  videojs$1.browser = browser;
29723  /**
29724   * Use {@link module:browser.TOUCH_ENABLED|browser.TOUCH_ENABLED} instead; only
29725   * included for backward-compatibility with 4.x.
29726   *
29727   * @deprecated Since version 5.0, use {@link module:browser.TOUCH_ENABLED|browser.TOUCH_ENABLED instead.
29728   * @type {boolean}
29729   */
29730
29731  videojs$1.TOUCH_ENABLED = TOUCH_ENABLED;
29732  videojs$1.extend = extend$1;
29733  videojs$1.mergeOptions = mergeOptions;
29734  videojs$1.bind = bind;
29735  videojs$1.registerPlugin = Plugin.registerPlugin;
29736  videojs$1.deregisterPlugin = Plugin.deregisterPlugin;
29737  /**
29738   * Deprecated method to register a plugin with Video.js
29739   *
29740   * @deprecated videojs.plugin() is deprecated; use videojs.registerPlugin() instead
29741   *
29742   * @param {string} name
29743   *        The plugin name
29744   *
29745   * @param {Plugin|Function} plugin
29746   *         The plugin sub-class or function
29747   */
29748
29749  videojs$1.plugin = function (name, plugin) {
29750    log.warn('videojs.plugin() is deprecated; use videojs.registerPlugin() instead');
29751    return Plugin.registerPlugin(name, plugin);
29752  };
29753
29754  videojs$1.getPlugins = Plugin.getPlugins;
29755  videojs$1.getPlugin = Plugin.getPlugin;
29756  videojs$1.getPluginVersion = Plugin.getPluginVersion;
29757  /**
29758   * Adding languages so that they're available to all players.
29759   * Example: `videojs.addLanguage('es', { 'Hello': 'Hola' });`
29760   *
29761   * @param {string} code
29762   *        The language code or dictionary property
29763   *
29764   * @param {Object} data
29765   *        The data values to be translated
29766   *
29767   * @return {Object}
29768   *         The resulting language dictionary object
29769   */
29770
29771  videojs$1.addLanguage = function (code, data) {
29772    var _mergeOptions;
29773
29774    code = ('' + code).toLowerCase();
29775    videojs$1.options.languages = mergeOptions(videojs$1.options.languages, (_mergeOptions = {}, _mergeOptions[code] = data, _mergeOptions));
29776    return videojs$1.options.languages[code];
29777  };
29778  /**
29779   * A reference to the {@link module:log|log utility module} as an object.
29780   *
29781   * @type {Function}
29782   * @see  {@link module:log|log}
29783   */
29784
29785
29786  videojs$1.log = log;
29787  videojs$1.createLogger = createLogger$1;
29788  videojs$1.createTimeRange = videojs$1.createTimeRanges = createTimeRanges;
29789  videojs$1.formatTime = formatTime;
29790  videojs$1.setFormatTime = setFormatTime;
29791  videojs$1.resetFormatTime = resetFormatTime;
29792  videojs$1.parseUrl = parseUrl;
29793  videojs$1.isCrossOrigin = isCrossOrigin;
29794  videojs$1.EventTarget = EventTarget;
29795  videojs$1.on = on;
29796  videojs$1.one = one;
29797  videojs$1.off = off;
29798  videojs$1.trigger = trigger;
29799  /**
29800   * A cross-browser XMLHttpRequest wrapper.
29801   *
29802   * @function
29803   * @param    {Object} options
29804   *           Settings for the request.
29805   *
29806   * @return   {XMLHttpRequest|XDomainRequest}
29807   *           The request object.
29808   *
29809   * @see      https://github.com/Raynos/xhr
29810   */
29811
29812  videojs$1.xhr = xhr;
29813  videojs$1.TextTrack = TextTrack;
29814  videojs$1.AudioTrack = AudioTrack;
29815  videojs$1.VideoTrack = VideoTrack;
29816  ['isEl', 'isTextNode', 'createEl', 'hasClass', 'addClass', 'removeClass', 'toggleClass', 'setAttributes', 'getAttributes', 'emptyEl', 'appendContent', 'insertContent'].forEach(function (k) {
29817    videojs$1[k] = function () {
29818      log.warn("videojs." + k + "() is deprecated; use videojs.dom." + k + "() instead");
29819      return Dom[k].apply(null, arguments);
29820    };
29821  });
29822  videojs$1.computedStyle = computedStyle;
29823  /**
29824   * A reference to the {@link module:dom|DOM utility module} as an object.
29825   *
29826   * @type {Object}
29827   * @see  {@link module:dom|dom}
29828   */
29829
29830  videojs$1.dom = Dom;
29831  /**
29832   * A reference to the {@link module:url|URL utility module} as an object.
29833   *
29834   * @type {Object}
29835   * @see  {@link module:url|url}
29836   */
29837
29838  videojs$1.url = Url;
29839
29840  var urlToolkit = createCommonjsModule(function (module, exports) {
29841    // see https://tools.ietf.org/html/rfc1808
29842
29843    /* jshint ignore:start */
29844    (function (root) {
29845      /* jshint ignore:end */
29846      var URL_REGEX = /^((?:[a-zA-Z0-9+\-.]+:)?)(\/\/[^\/?#]*)?((?:[^\/\?#]*\/)*.*?)??(;.*?)?(\?.*?)?(#.*?)?$/;
29847      var FIRST_SEGMENT_REGEX = /^([^\/?#]*)(.*)$/;
29848      var SLASH_DOT_REGEX = /(?:\/|^)\.(?=\/)/g;
29849      var SLASH_DOT_DOT_REGEX = /(?:\/|^)\.\.\/(?!\.\.\/).*?(?=\/)/g;
29850      var URLToolkit = {
29851        // jshint ignore:line
29852        // If opts.alwaysNormalize is true then the path will always be normalized even when it starts with / or //
29853        // E.g
29854        // With opts.alwaysNormalize = false (default, spec compliant)
29855        // http://a.com/b/cd + /e/f/../g => http://a.com/e/f/../g
29856        // With opts.alwaysNormalize = true (not spec compliant)
29857        // http://a.com/b/cd + /e/f/../g => http://a.com/e/g
29858        buildAbsoluteURL: function buildAbsoluteURL(baseURL, relativeURL, opts) {
29859          opts = opts || {}; // remove any remaining space and CRLF
29860
29861          baseURL = baseURL.trim();
29862          relativeURL = relativeURL.trim();
29863
29864          if (!relativeURL) {
29865            // 2a) If the embedded URL is entirely empty, it inherits the
29866            // entire base URL (i.e., is set equal to the base URL)
29867            // and we are done.
29868            if (!opts.alwaysNormalize) {
29869              return baseURL;
29870            }
29871
29872            var basePartsForNormalise = URLToolkit.parseURL(baseURL);
29873
29874            if (!basePartsForNormalise) {
29875              throw new Error('Error trying to parse base URL.');
29876            }
29877
29878            basePartsForNormalise.path = URLToolkit.normalizePath(basePartsForNormalise.path);
29879            return URLToolkit.buildURLFromParts(basePartsForNormalise);
29880          }
29881
29882          var relativeParts = URLToolkit.parseURL(relativeURL);
29883
29884          if (!relativeParts) {
29885            throw new Error('Error trying to parse relative URL.');
29886          }
29887
29888          if (relativeParts.scheme) {
29889            // 2b) If the embedded URL starts with a scheme name, it is
29890            // interpreted as an absolute URL and we are done.
29891            if (!opts.alwaysNormalize) {
29892              return relativeURL;
29893            }
29894
29895            relativeParts.path = URLToolkit.normalizePath(relativeParts.path);
29896            return URLToolkit.buildURLFromParts(relativeParts);
29897          }
29898
29899          var baseParts = URLToolkit.parseURL(baseURL);
29900
29901          if (!baseParts) {
29902            throw new Error('Error trying to parse base URL.');
29903          }
29904
29905          if (!baseParts.netLoc && baseParts.path && baseParts.path[0] !== '/') {
29906            // If netLoc missing and path doesn't start with '/', assume everthing before the first '/' is the netLoc
29907            // This causes 'example.com/a' to be handled as '//example.com/a' instead of '/example.com/a'
29908            var pathParts = FIRST_SEGMENT_REGEX.exec(baseParts.path);
29909            baseParts.netLoc = pathParts[1];
29910            baseParts.path = pathParts[2];
29911          }
29912
29913          if (baseParts.netLoc && !baseParts.path) {
29914            baseParts.path = '/';
29915          }
29916
29917          var builtParts = {
29918            // 2c) Otherwise, the embedded URL inherits the scheme of
29919            // the base URL.
29920            scheme: baseParts.scheme,
29921            netLoc: relativeParts.netLoc,
29922            path: null,
29923            params: relativeParts.params,
29924            query: relativeParts.query,
29925            fragment: relativeParts.fragment
29926          };
29927
29928          if (!relativeParts.netLoc) {
29929            // 3) If the embedded URL's <net_loc> is non-empty, we skip to
29930            // Step 7.  Otherwise, the embedded URL inherits the <net_loc>
29931            // (if any) of the base URL.
29932            builtParts.netLoc = baseParts.netLoc; // 4) If the embedded URL path is preceded by a slash "/", the
29933            // path is not relative and we skip to Step 7.
29934
29935            if (relativeParts.path[0] !== '/') {
29936              if (!relativeParts.path) {
29937                // 5) If the embedded URL path is empty (and not preceded by a
29938                // slash), then the embedded URL inherits the base URL path
29939                builtParts.path = baseParts.path; // 5a) if the embedded URL's <params> is non-empty, we skip to
29940                // step 7; otherwise, it inherits the <params> of the base
29941                // URL (if any) and
29942
29943                if (!relativeParts.params) {
29944                  builtParts.params = baseParts.params; // 5b) if the embedded URL's <query> is non-empty, we skip to
29945                  // step 7; otherwise, it inherits the <query> of the base
29946                  // URL (if any) and we skip to step 7.
29947
vendor: 45,450 bytes, lines 29948-31442
29948                  if (!relativeParts.query) {
29949                    builtParts.query = baseParts.query;
29950                  }
29951                }
29952              } else {
29953                // 6) The last segment of the base URL's path (anything
29954                // following the rightmost slash "/", or the entire path if no
29955                // slash is present) is removed and the embedded URL's path is
29956                // appended in its place.
29957                var baseURLPath = baseParts.path;
29958                var newPath = baseURLPath.substring(0, baseURLPath.lastIndexOf('/') + 1) + relativeParts.path;
29959                builtParts.path = URLToolkit.normalizePath(newPath);
29960              }
29961            }
29962          }
29963
29964          if (builtParts.path === null) {
29965            builtParts.path = opts.alwaysNormalize ? URLToolkit.normalizePath(relativeParts.path) : relativeParts.path;
29966          }
29967
29968          return URLToolkit.buildURLFromParts(builtParts);
29969        },
29970        parseURL: function parseURL(url) {
29971          var parts = URL_REGEX.exec(url);
29972
29973          if (!parts) {
29974            return null;
29975          }
29976
29977          return {
29978            scheme: parts[1] || '',
29979            netLoc: parts[2] || '',
29980            path: parts[3] || '',
29981            params: parts[4] || '',
29982            query: parts[5] || '',
29983            fragment: parts[6] || ''
29984          };
29985        },
29986        normalizePath: function normalizePath(path) {
29987          // The following operations are
29988          // then applied, in order, to the new path:
29989          // 6a) All occurrences of "./", where "." is a complete path
29990          // segment, are removed.
29991          // 6b) If the path ends with "." as a complete path segment,
29992          // that "." is removed.
29993          path = path.split('').reverse().join('').replace(SLASH_DOT_REGEX, ''); // 6c) All occurrences of "<segment>/../", where <segment> is a
29994          // complete path segment not equal to "..", are removed.
29995          // Removal of these path segments is performed iteratively,
29996          // removing the leftmost matching pattern on each iteration,
29997          // until no matching pattern remains.
29998          // 6d) If the path ends with "<segment>/..", where <segment> is a
29999          // complete path segment not equal to "..", that
30000          // "<segment>/.." is removed.
30001
30002          while (path.length !== (path = path.replace(SLASH_DOT_DOT_REGEX, '')).length) {} // jshint ignore:line
30003
30004
30005          return path.split('').reverse().join('');
30006        },
30007        buildURLFromParts: function buildURLFromParts(parts) {
30008          return parts.scheme + parts.netLoc + parts.path + parts.params + parts.query + parts.fragment;
30009        }
30010      };
30011      /* jshint ignore:start */
30012
30013      module.exports = URLToolkit;
30014    })();
30015    /* jshint ignore:end */
30016
30017  });
30018
30019  /*! @name m3u8-parser @version 4.4.0 @license Apache-2.0 */
30020
30021  function _extends() {
30022    _extends = Object.assign || function (target) {
30023      for (var i = 1; i < arguments.length; i++) {
30024        var source = arguments[i];
30025
30026        for (var key in source) {
30027          if (Object.prototype.hasOwnProperty.call(source, key)) {
30028            target[key] = source[key];
30029          }
30030        }
30031      }
30032
30033      return target;
30034    };
30035
30036    return _extends.apply(this, arguments);
30037  }
30038
30039  function _inheritsLoose$1(subClass, superClass) {
30040    subClass.prototype = Object.create(superClass.prototype);
30041    subClass.prototype.constructor = subClass;
30042    subClass.__proto__ = superClass;
30043  }
30044
30045  function _assertThisInitialized$1(self) {
30046    if (self === void 0) {
30047      throw new ReferenceError("this hasn't been initialised - super() hasn't been called");
30048    }
30049
30050    return self;
30051  }
30052  /**
30053   * @file stream.js
30054   */
30055
30056  /**
30057   * A lightweight readable stream implementation that handles event dispatching.
30058   *
30059   * @class Stream
30060   */
30061
30062
30063  var Stream =
30064  /*#__PURE__*/
30065  function () {
30066    function Stream() {
30067      this.listeners = {};
30068    }
30069    /**
30070     * Add a listener for a specified event type.
30071     *
30072     * @param {string} type the event name
30073     * @param {Function} listener the callback to be invoked when an event of
30074     * the specified type occurs
30075     */
30076
30077
30078    var _proto = Stream.prototype;
30079
30080    _proto.on = function on(type, listener) {
30081      if (!this.listeners[type]) {
30082        this.listeners[type] = [];
30083      }
30084
30085      this.listeners[type].push(listener);
30086    }
30087    /**
30088     * Remove a listener for a specified event type.
30089     *
30090     * @param {string} type the event name
30091     * @param {Function} listener  a function previously registered for this
30092     * type of event through `on`
30093     * @return {boolean} if we could turn it off or not
30094     */
30095    ;
30096
30097    _proto.off = function off(type, listener) {
30098      if (!this.listeners[type]) {
30099        return false;
30100      }
30101
30102      var index = this.listeners[type].indexOf(listener);
30103      this.listeners[type].splice(index, 1);
30104      return index > -1;
30105    }
30106    /**
30107     * Trigger an event of the specified type on this stream. Any additional
30108     * arguments to this function are passed as parameters to event listeners.
30109     *
30110     * @param {string} type the event name
30111     */
30112    ;
30113
30114    _proto.trigger = function trigger(type) {
30115      var callbacks = this.listeners[type];
30116      var i;
30117      var length;
30118      var args;
30119
30120      if (!callbacks) {
30121        return;
30122      } // Slicing the arguments on every invocation of this method
30123      // can add a significant amount of overhead. Avoid the
30124      // intermediate object creation for the common case of a
30125      // single callback argument
30126
30127
30128      if (arguments.length === 2) {
30129        length = callbacks.length;
30130
30131        for (i = 0; i < length; ++i) {
30132          callbacks[i].call(this, arguments[1]);
30133        }
30134      } else {
30135        args = Array.prototype.slice.call(arguments, 1);
30136        length = callbacks.length;
30137
30138        for (i = 0; i < length; ++i) {
30139          callbacks[i].apply(this, args);
30140        }
30141      }
30142    }
30143    /**
30144     * Destroys the stream and cleans up.
30145     */
30146    ;
30147
30148    _proto.dispose = function dispose() {
30149      this.listeners = {};
30150    }
30151    /**
30152     * Forwards all `data` events on this stream to the destination stream. The
30153     * destination stream should provide a method `push` to receive the data
30154     * events as they arrive.
30155     *
30156     * @param {Stream} destination the stream that will receive all `data` events
30157     * @see http://nodejs.org/api/stream.html#stream_readable_pipe_destination_options
30158     */
30159    ;
30160
30161    _proto.pipe = function pipe(destination) {
30162      this.on('data', function (data) {
30163        destination.push(data);
30164      });
30165    };
30166
30167    return Stream;
30168  }();
30169  /**
30170   * A stream that buffers string input and generates a `data` event for each
30171   * line.
30172   *
30173   * @class LineStream
30174   * @extends Stream
30175   */
30176
30177
30178  var LineStream =
30179  /*#__PURE__*/
30180  function (_Stream) {
30181    _inheritsLoose$1(LineStream, _Stream);
30182
30183    function LineStream() {
30184      var _this;
30185
30186      _this = _Stream.call(this) || this;
30187      _this.buffer = '';
30188      return _this;
30189    }
30190    /**
30191     * Add new data to be parsed.
30192     *
30193     * @param {string} data the text to process
30194     */
30195
30196
30197    var _proto = LineStream.prototype;
30198
30199    _proto.push = function push(data) {
30200      var nextNewline;
30201      this.buffer += data;
30202      nextNewline = this.buffer.indexOf('\n');
30203
30204      for (; nextNewline > -1; nextNewline = this.buffer.indexOf('\n')) {
30205        this.trigger('data', this.buffer.substring(0, nextNewline));
30206        this.buffer = this.buffer.substring(nextNewline + 1);
30207      }
30208    };
30209
30210    return LineStream;
30211  }(Stream);
30212  /**
30213   * "forgiving" attribute list psuedo-grammar:
30214   * attributes -> keyvalue (',' keyvalue)*
30215   * keyvalue   -> key '=' value
30216   * key        -> [^=]*
30217   * value      -> '"' [^"]* '"' | [^,]*
30218   */
30219
30220
30221  var attributeSeparator = function attributeSeparator() {
30222    var key = '[^=]*';
30223    var value = '"[^"]*"|[^,]*';
30224    var keyvalue = '(?:' + key + ')=(?:' + value + ')';
30225    return new RegExp('(?:^|,)(' + keyvalue + ')');
30226  };
30227  /**
30228   * Parse attributes from a line given the separator
30229   *
30230   * @param {string} attributes the attribute line to parse
30231   */
30232
30233
30234  var parseAttributes = function parseAttributes(attributes) {
30235    // split the string using attributes as the separator
30236    var attrs = attributes.split(attributeSeparator());
30237    var result = {};
30238    var i = attrs.length;
30239    var attr;
30240
30241    while (i--) {
30242      // filter out unmatched portions of the string
30243      if (attrs[i] === '') {
30244        continue;
30245      } // split the key and value
30246
30247
30248      attr = /([^=]*)=(.*)/.exec(attrs[i]).slice(1); // trim whitespace and remove optional quotes around the value
30249
30250      attr[0] = attr[0].replace(/^\s+|\s+$/g, '');
30251      attr[1] = attr[1].replace(/^\s+|\s+$/g, '');
30252      attr[1] = attr[1].replace(/^['"](.*)['"]$/g, '$1');
30253      result[attr[0]] = attr[1];
30254    }
30255
30256    return result;
30257  };
30258  /**
30259   * A line-level M3U8 parser event stream. It expects to receive input one
30260   * line at a time and performs a context-free parse of its contents. A stream
30261   * interpretation of a manifest can be useful if the manifest is expected to
30262   * be too large to fit comfortably into memory or the entirety of the input
30263   * is not immediately available. Otherwise, it's probably much easier to work
30264   * with a regular `Parser` object.
30265   *
30266   * Produces `data` events with an object that captures the parser's
30267   * interpretation of the input. That object has a property `tag` that is one
30268   * of `uri`, `comment`, or `tag`. URIs only have a single additional
30269   * property, `line`, which captures the entirety of the input without
30270   * interpretation. Comments similarly have a single additional property
30271   * `text` which is the input without the leading `#`.
30272   *
30273   * Tags always have a property `tagType` which is the lower-cased version of
30274   * the M3U8 directive without the `#EXT` or `#EXT-X-` prefix. For instance,
30275   * `#EXT-X-MEDIA-SEQUENCE` becomes `media-sequence` when parsed. Unrecognized
30276   * tags are given the tag type `unknown` and a single additional property
30277   * `data` with the remainder of the input.
30278   *
30279   * @class ParseStream
30280   * @extends Stream
30281   */
30282
30283
30284  var ParseStream =
30285  /*#__PURE__*/
30286  function (_Stream) {
30287    _inheritsLoose$1(ParseStream, _Stream);
30288
30289    function ParseStream() {
30290      var _this;
30291
30292      _this = _Stream.call(this) || this;
30293      _this.customParsers = [];
30294      _this.tagMappers = [];
30295      return _this;
30296    }
30297    /**
30298     * Parses an additional line of input.
30299     *
30300     * @param {string} line a single line of an M3U8 file to parse
30301     */
30302
30303
30304    var _proto = ParseStream.prototype;
30305
30306    _proto.push = function push(line) {
30307      var _this2 = this;
30308
30309      var match;
30310      var event; // strip whitespace
30311
30312      line = line.trim();
30313
30314      if (line.length === 0) {
30315        // ignore empty lines
30316        return;
30317      } // URIs
30318
30319
30320      if (line[0] !== '#') {
30321        this.trigger('data', {
30322          type: 'uri',
30323          uri: line
30324        });
30325        return;
30326      } // map tags
30327
30328
30329      var newLines = this.tagMappers.reduce(function (acc, mapper) {
30330        var mappedLine = mapper(line); // skip if unchanged
30331
30332        if (mappedLine === line) {
30333          return acc;
30334        }
30335
30336        return acc.concat([mappedLine]);
30337      }, [line]);
30338      newLines.forEach(function (newLine) {
30339        for (var i = 0; i < _this2.customParsers.length; i++) {
30340          if (_this2.customParsers[i].call(_this2, newLine)) {
30341            return;
30342          }
30343        } // Comments
30344
30345
30346        if (newLine.indexOf('#EXT') !== 0) {
30347          _this2.trigger('data', {
30348            type: 'comment',
30349            text: newLine.slice(1)
30350          });
30351
30352          return;
30353        } // strip off any carriage returns here so the regex matching
30354        // doesn't have to account for them.
30355
30356
30357        newLine = newLine.replace('\r', ''); // Tags
30358
30359        match = /^#EXTM3U/.exec(newLine);
30360
30361        if (match) {
30362          _this2.trigger('data', {
30363            type: 'tag',
30364            tagType: 'm3u'
30365          });
30366
30367          return;
30368        }
30369
30370        match = /^#EXTINF:?([0-9\.]*)?,?(.*)?$/.exec(newLine);
30371
30372        if (match) {
30373          event = {
30374            type: 'tag',
30375            tagType: 'inf'
30376          };
30377
30378          if (match[1]) {
30379            event.duration = parseFloat(match[1]);
30380          }
30381
30382          if (match[2]) {
30383            event.title = match[2];
30384          }
30385
30386          _this2.trigger('data', event);
30387
30388          return;
30389        }
30390
30391        match = /^#EXT-X-TARGETDURATION:?([0-9.]*)?/.exec(newLine);
30392
30393        if (match) {
30394          event = {
30395            type: 'tag',
30396            tagType: 'targetduration'
30397          };
30398
30399          if (match[1]) {
30400            event.duration = parseInt(match[1], 10);
30401          }
30402
30403          _this2.trigger('data', event);
30404
30405          return;
30406        }
30407
30408        match = /^#ZEN-TOTAL-DURATION:?([0-9.]*)?/.exec(newLine);
30409
30410        if (match) {
30411          event = {
30412            type: 'tag',
30413            tagType: 'totalduration'
30414          };
30415
30416          if (match[1]) {
30417            event.duration = parseInt(match[1], 10);
30418          }
30419
30420          _this2.trigger('data', event);
30421
30422          return;
30423        }
30424
30425        match = /^#EXT-X-VERSION:?([0-9.]*)?/.exec(newLine);
30426
30427        if (match) {
30428          event = {
30429            type: 'tag',
30430            tagType: 'version'
30431          };
30432
30433          if (match[1]) {
30434            event.version = parseInt(match[1], 10);
30435          }
30436
30437          _this2.trigger('data', event);
30438
30439          return;
30440        }
30441
30442        match = /^#EXT-X-MEDIA-SEQUENCE:?(\-?[0-9.]*)?/.exec(newLine);
30443
30444        if (match) {
30445          event = {
30446            type: 'tag',
30447            tagType: 'media-sequence'
30448          };
30449
30450          if (match[1]) {
30451            event.number = parseInt(match[1], 10);
30452          }
30453
30454          _this2.trigger('data', event);
30455
30456          return;
30457        }
30458
30459        match = /^#EXT-X-DISCONTINUITY-SEQUENCE:?(\-?[0-9.]*)?/.exec(newLine);
30460
30461        if (match) {
30462          event = {
30463            type: 'tag',
30464            tagType: 'discontinuity-sequence'
30465          };
30466
30467          if (match[1]) {
30468            event.number = parseInt(match[1], 10);
30469          }
30470
30471          _this2.trigger('data', event);
30472
30473          return;
30474        }
30475
30476        match = /^#EXT-X-PLAYLIST-TYPE:?(.*)?$/.exec(newLine);
30477
30478        if (match) {
30479          event = {
30480            type: 'tag',
30481            tagType: 'playlist-type'
30482          };
30483
30484          if (match[1]) {
30485            event.playlistType = match[1];
30486          }
30487
30488          _this2.trigger('data', event);
30489
30490          return;
30491        }
30492
30493        match = /^#EXT-X-BYTERANGE:?([0-9.]*)?@?([0-9.]*)?/.exec(newLine);
30494
30495        if (match) {
30496          event = {
30497            type: 'tag',
30498            tagType: 'byterange'
30499          };
30500
30501          if (match[1]) {
30502            event.length = parseInt(match[1], 10);
30503          }
30504
30505          if (match[2]) {
30506            event.offset = parseInt(match[2], 10);
30507          }
30508
30509          _this2.trigger('data', event);
30510
30511          return;
30512        }
30513
30514        match = /^#EXT-X-ALLOW-CACHE:?(YES|NO)?/.exec(newLine);
30515
30516        if (match) {
30517          event = {
30518            type: 'tag',
30519            tagType: 'allow-cache'
30520          };
30521
30522          if (match[1]) {
30523            event.allowed = !/NO/.test(match[1]);
30524          }
30525
30526          _this2.trigger('data', event);
30527
30528          return;
30529        }
30530
30531        match = /^#EXT-X-MAP:?(.*)$/.exec(newLine);
30532
30533        if (match) {
30534          event = {
30535            type: 'tag',
30536            tagType: 'map'
30537          };
30538
30539          if (match[1]) {
30540            var attributes = parseAttributes(match[1]);
30541
30542            if (attributes.URI) {
30543              event.uri = attributes.URI;
30544            }
30545
30546            if (attributes.BYTERANGE) {
30547              var _attributes$BYTERANGE = attributes.BYTERANGE.split('@'),
30548                  length = _attributes$BYTERANGE[0],
30549                  offset = _attributes$BYTERANGE[1];
30550
30551              event.byterange = {};
30552
30553              if (length) {
30554                event.byterange.length = parseInt(length, 10);
30555              }
30556
30557              if (offset) {
30558                event.byterange.offset = parseInt(offset, 10);
30559              }
30560            }
30561          }
30562
30563          _this2.trigger('data', event);
30564
30565          return;
30566        }
30567
30568        match = /^#EXT-X-STREAM-INF:?(.*)$/.exec(newLine);
30569
30570        if (match) {
30571          event = {
30572            type: 'tag',
30573            tagType: 'stream-inf'
30574          };
30575
30576          if (match[1]) {
30577            event.attributes = parseAttributes(match[1]);
30578
30579            if (event.attributes.RESOLUTION) {
30580              var split = event.attributes.RESOLUTION.split('x');
30581              var resolution = {};
30582
30583              if (split[0]) {
30584                resolution.width = parseInt(split[0], 10);
30585              }
30586
30587              if (split[1]) {
30588                resolution.height = parseInt(split[1], 10);
30589              }
30590
30591              event.attributes.RESOLUTION = resolution;
30592            }
30593
30594            if (event.attributes.BANDWIDTH) {
30595              event.attributes.BANDWIDTH = parseInt(event.attributes.BANDWIDTH, 10);
30596            }
30597
30598            if (event.attributes['PROGRAM-ID']) {
30599              event.attributes['PROGRAM-ID'] = parseInt(event.attributes['PROGRAM-ID'], 10);
30600            }
30601          }
30602
30603          _this2.trigger('data', event);
30604
30605          return;
30606        }
30607
30608        match = /^#EXT-X-MEDIA:?(.*)$/.exec(newLine);
30609
30610        if (match) {
30611          event = {
30612            type: 'tag',
30613            tagType: 'media'
30614          };
30615
30616          if (match[1]) {
30617            event.attributes = parseAttributes(match[1]);
30618          }
30619
30620          _this2.trigger('data', event);
30621
30622          return;
30623        }
30624
30625        match = /^#EXT-X-ENDLIST/.exec(newLine);
30626
30627        if (match) {
30628          _this2.trigger('data', {
30629            type: 'tag',
30630            tagType: 'endlist'
30631          });
30632
30633          return;
30634        }
30635
30636        match = /^#EXT-X-DISCONTINUITY/.exec(newLine);
30637
30638        if (match) {
30639          _this2.trigger('data', {
30640            type: 'tag',
30641            tagType: 'discontinuity'
30642          });
30643
30644          return;
30645        }
30646
30647        match = /^#EXT-X-PROGRAM-DATE-TIME:?(.*)$/.exec(newLine);
30648
30649        if (match) {
30650          event = {
30651            type: 'tag',
30652            tagType: 'program-date-time'
30653          };
30654
30655          if (match[1]) {
30656            event.dateTimeString = match[1];
30657            event.dateTimeObject = new Date(match[1]);
30658          }
30659
30660          _this2.trigger('data', event);
30661
30662          return;
30663        }
30664
30665        match = /^#EXT-X-KEY:?(.*)$/.exec(newLine);
30666
30667        if (match) {
30668          event = {
30669            type: 'tag',
30670            tagType: 'key'
30671          };
30672
30673          if (match[1]) {
30674            event.attributes = parseAttributes(match[1]); // parse the IV string into a Uint32Array
30675
30676            if (event.attributes.IV) {
30677              if (event.attributes.IV.substring(0, 2).toLowerCase() === '0x') {
30678                event.attributes.IV = event.attributes.IV.substring(2);
30679              }
30680
30681              event.attributes.IV = event.attributes.IV.match(/.{8}/g);
30682              event.attributes.IV[0] = parseInt(event.attributes.IV[0], 16);
30683              event.attributes.IV[1] = parseInt(event.attributes.IV[1], 16);
30684              event.attributes.IV[2] = parseInt(event.attributes.IV[2], 16);
30685              event.attributes.IV[3] = parseInt(event.attributes.IV[3], 16);
30686              event.attributes.IV = new Uint32Array(event.attributes.IV);
30687            }
30688          }
30689
30690          _this2.trigger('data', event);
30691
30692          return;
30693        }
30694
30695        match = /^#EXT-X-START:?(.*)$/.exec(newLine);
30696
30697        if (match) {
30698          event = {
30699            type: 'tag',
30700            tagType: 'start'
30701          };
30702
30703          if (match[1]) {
30704            event.attributes = parseAttributes(match[1]);
30705            event.attributes['TIME-OFFSET'] = parseFloat(event.attributes['TIME-OFFSET']);
30706            event.attributes.PRECISE = /YES/.test(event.attributes.PRECISE);
30707          }
30708
30709          _this2.trigger('data', event);
30710
30711          return;
30712        }
30713
30714        match = /^#EXT-X-CUE-OUT-CONT:?(.*)?$/.exec(newLine);
30715
30716        if (match) {
30717          event = {
30718            type: 'tag',
30719            tagType: 'cue-out-cont'
30720          };
30721
30722          if (match[1]) {
30723            event.data = match[1];
30724          } else {
30725            event.data = '';
30726          }
30727
30728          _this2.trigger('data', event);
30729
30730          return;
30731        }
30732
30733        match = /^#EXT-X-CUE-OUT:?(.*)?$/.exec(newLine);
30734
30735        if (match) {
30736          event = {
30737            type: 'tag',
30738            tagType: 'cue-out'
30739          };
30740
30741          if (match[1]) {
30742            event.data = match[1];
30743          } else {
30744            event.data = '';
30745          }
30746
30747          _this2.trigger('data', event);
30748
30749          return;
30750        }
30751
30752        match = /^#EXT-X-CUE-IN:?(.*)?$/.exec(newLine);
30753
30754        if (match) {
30755          event = {
30756            type: 'tag',
30757            tagType: 'cue-in'
30758          };
30759
30760          if (match[1]) {
30761            event.data = match[1];
30762          } else {
30763            event.data = '';
30764          }
30765
30766          _this2.trigger('data', event);
30767
30768          return;
30769        } // unknown tag type
30770
30771
30772        _this2.trigger('data', {
30773          type: 'tag',
30774          data: newLine.slice(4)
30775        });
30776      });
30777    }
30778    /**
30779     * Add a parser for custom headers
30780     *
30781     * @param {Object}   options              a map of options for the added parser
30782     * @param {RegExp}   options.expression   a regular expression to match the custom header
30783     * @param {string}   options.customType   the custom type to register to the output
30784     * @param {Function} [options.dataParser] function to parse the line into an object
30785     * @param {boolean}  [options.segment]    should tag data be attached to the segment object
30786     */
30787    ;
30788
30789    _proto.addParser = function addParser(_ref) {
30790      var _this3 = this;
30791
30792      var expression = _ref.expression,
30793          customType = _ref.customType,
30794          dataParser = _ref.dataParser,
30795          segment = _ref.segment;
30796
30797      if (typeof dataParser !== 'function') {
30798        dataParser = function dataParser(line) {
30799          return line;
30800        };
30801      }
30802
30803      this.customParsers.push(function (line) {
30804        var match = expression.exec(line);
30805
30806        if (match) {
30807          _this3.trigger('data', {
30808            type: 'custom',
30809            data: dataParser(line),
30810            customType: customType,
30811            segment: segment
30812          });
30813
30814          return true;
30815        }
30816      });
30817    }
30818    /**
30819     * Add a custom header mapper
30820     *
30821     * @param {Object}   options
30822     * @param {RegExp}   options.expression   a regular expression to match the custom header
30823     * @param {Function} options.map          function to translate tag into a different tag
30824     */
30825    ;
30826
30827    _proto.addTagMapper = function addTagMapper(_ref2) {
30828      var expression = _ref2.expression,
30829          map = _ref2.map;
30830
30831      var mapFn = function mapFn(line) {
30832        if (expression.test(line)) {
30833          return map(line);
30834        }
30835
30836        return line;
30837      };
30838
30839      this.tagMappers.push(mapFn);
30840    };
30841
30842    return ParseStream;
30843  }(Stream);
30844
30845  function decodeB64ToUint8Array(b64Text) {
30846    var decodedString = window$1.atob(b64Text || '');
30847    var array = new Uint8Array(decodedString.length);
30848
30849    for (var i = 0; i < decodedString.length; i++) {
30850      array[i] = decodedString.charCodeAt(i);
30851    }
30852
30853    return array;
30854  }
30855  /**
30856   * A parser for M3U8 files. The current interpretation of the input is
30857   * exposed as a property `manifest` on parser objects. It's just two lines to
30858   * create and parse a manifest once you have the contents available as a string:
30859   *
30860   * ```js
30861   * var parser = new m3u8.Parser();
30862   * parser.push(xhr.responseText);
30863   * ```
30864   *
30865   * New input can later be applied to update the manifest object by calling
30866   * `push` again.
30867   *
30868   * The parser attempts to create a usable manifest object even if the
30869   * underlying input is somewhat nonsensical. It emits `info` and `warning`
30870   * events during the parse if it encounters input that seems invalid or
30871   * requires some property of the manifest object to be defaulted.
30872   *
30873   * @class Parser
30874   * @extends Stream
30875   */
30876
30877
30878  var Parser =
30879  /*#__PURE__*/
30880  function (_Stream) {
30881    _inheritsLoose$1(Parser, _Stream);
30882
30883    function Parser() {
30884      var _this;
30885
30886      _this = _Stream.call(this) || this;
30887      _this.lineStream = new LineStream();
30888      _this.parseStream = new ParseStream();
30889
30890      _this.lineStream.pipe(_this.parseStream);
30891      /* eslint-disable consistent-this */
30892
30893
30894      var self = _assertThisInitialized$1(_this);
30895      /* eslint-enable consistent-this */
30896
30897
30898      var uris = [];
30899      var currentUri = {}; // if specified, the active EXT-X-MAP definition
30900
30901      var currentMap; // if specified, the active decryption key
30902
30903      var _key;
30904
30905      var noop = function noop() {};
30906
30907      var defaultMediaGroups = {
30908        'AUDIO': {},
30909        'VIDEO': {},
30910        'CLOSED-CAPTIONS': {},
30911        'SUBTITLES': {}
30912      }; // This is the Widevine UUID from DASH IF IOP. The same exact string is
30913      // used in MPDs with Widevine encrypted streams.
30914
30915      var widevineUuid = 'urn:uuid:edef8ba9-79d6-4ace-a3c8-27dcd51d21ed'; // group segments into numbered timelines delineated by discontinuities
30916
30917      var currentTimeline = 0; // the manifest is empty until the parse stream begins delivering data
30918
30919      _this.manifest = {
30920        allowCache: true,
30921        discontinuityStarts: [],
30922        segments: []
30923      }; // update the manifest with the m3u8 entry from the parse stream
30924
30925      _this.parseStream.on('data', function (entry) {
30926        var mediaGroup;
30927        var rendition;
30928        ({
30929          tag: function tag() {
30930            // switch based on the tag type
30931            (({
30932              'allow-cache': function allowCache() {
30933                this.manifest.allowCache = entry.allowed;
30934
30935                if (!('allowed' in entry)) {
30936                  this.trigger('info', {
30937                    message: 'defaulting allowCache to YES'
30938                  });
30939                  this.manifest.allowCache = true;
30940                }
30941              },
30942              byterange: function byterange() {
30943                var byterange = {};
30944
30945                if ('length' in entry) {
30946                  currentUri.byterange = byterange;
30947                  byterange.length = entry.length;
30948
30949                  if (!('offset' in entry)) {
30950                    this.trigger('info', {
30951                      message: 'defaulting offset to zero'
30952                    });
30953                    entry.offset = 0;
30954                  }
30955                }
30956
30957                if ('offset' in entry) {
30958                  currentUri.byterange = byterange;
30959                  byterange.offset = entry.offset;
30960                }
30961              },
30962              endlist: function endlist() {
30963                this.manifest.endList = true;
30964              },
30965              inf: function inf() {
30966                if (!('mediaSequence' in this.manifest)) {
30967                  this.manifest.mediaSequence = 0;
30968                  this.trigger('info', {
30969                    message: 'defaulting media sequence to zero'
30970                  });
30971                }
30972
30973                if (!('discontinuitySequence' in this.manifest)) {
30974                  this.manifest.discontinuitySequence = 0;
30975                  this.trigger('info', {
30976                    message: 'defaulting discontinuity sequence to zero'
30977                  });
30978                }
30979
30980                if (entry.duration > 0) {
30981                  currentUri.duration = entry.duration;
30982                }
30983
30984                if (entry.duration === 0) {
30985                  currentUri.duration = 0.01;
30986                  this.trigger('info', {
30987                    message: 'updating zero segment duration to a small value'
30988                  });
30989                }
30990
30991                this.manifest.segments = uris;
30992              },
30993              key: function key() {
30994                if (!entry.attributes) {
30995                  this.trigger('warn', {
30996                    message: 'ignoring key declaration without attribute list'
30997                  });
30998                  return;
30999                } // clear the active encryption key
31000
31001
31002                if (entry.attributes.METHOD === 'NONE') {
31003                  _key = null;
31004                  return;
31005                }
31006
31007                if (!entry.attributes.URI) {
31008                  this.trigger('warn', {
31009                    message: 'ignoring key declaration without URI'
31010                  });
31011                  return;
31012                } // check if the content is encrypted for Widevine
31013                // Widevine/HLS spec: https://storage.googleapis.com/wvdocs/Widevine_DRM_HLS.pdf
31014
31015
31016                if (entry.attributes.KEYFORMAT === widevineUuid) {
31017                  var VALID_METHODS = ['SAMPLE-AES', 'SAMPLE-AES-CTR', 'SAMPLE-AES-CENC'];
31018
31019                  if (VALID_METHODS.indexOf(entry.attributes.METHOD) === -1) {
31020                    this.trigger('warn', {
31021                      message: 'invalid key method provided for Widevine'
31022                    });
31023                    return;
31024                  }
31025
31026                  if (entry.attributes.METHOD === 'SAMPLE-AES-CENC') {
31027                    this.trigger('warn', {
31028                      message: 'SAMPLE-AES-CENC is deprecated, please use SAMPLE-AES-CTR instead'
31029                    });
31030                  }
31031
31032                  if (entry.attributes.URI.substring(0, 23) !== 'data:text/plain;base64,') {
31033                    this.trigger('warn', {
31034                      message: 'invalid key URI provided for Widevine'
31035                    });
31036                    return;
31037                  }
31038
31039                  if (!(entry.attributes.KEYID && entry.attributes.KEYID.substring(0, 2) === '0x')) {
31040                    this.trigger('warn', {
31041                      message: 'invalid key ID provided for Widevine'
31042                    });
31043                    return;
31044                  } // if Widevine key attributes are valid, store them as `contentProtection`
31045                  // on the manifest to emulate Widevine tag structure in a DASH mpd
31046
31047
31048                  this.manifest.contentProtection = {
31049                    'com.widevine.alpha': {
31050                      attributes: {
31051                        schemeIdUri: entry.attributes.KEYFORMAT,
31052                        // remove '0x' from the key id string
31053                        keyId: entry.attributes.KEYID.substring(2)
31054                      },
31055                      // decode the base64-encoded PSSH box
31056                      pssh: decodeB64ToUint8Array(entry.attributes.URI.split(',')[1])
31057                    }
31058                  };
31059                  return;
31060                }
31061
31062                if (!entry.attributes.METHOD) {
31063                  this.trigger('warn', {
31064                    message: 'defaulting key method to AES-128'
31065                  });
31066                } // setup an encryption key for upcoming segments
31067
31068
31069                _key = {
31070                  method: entry.attributes.METHOD || 'AES-128',
31071                  uri: entry.attributes.URI
31072                };
31073
31074                if (typeof entry.attributes.IV !== 'undefined') {
31075                  _key.iv = entry.attributes.IV;
31076                }
31077              },
31078              'media-sequence': function mediaSequence() {
31079                if (!isFinite(entry.number)) {
31080                  this.trigger('warn', {
31081                    message: 'ignoring invalid media sequence: ' + entry.number
31082                  });
31083                  return;
31084                }
31085
31086                this.manifest.mediaSequence = entry.number;
31087              },
31088              'discontinuity-sequence': function discontinuitySequence() {
31089                if (!isFinite(entry.number)) {
31090                  this.trigger('warn', {
31091                    message: 'ignoring invalid discontinuity sequence: ' + entry.number
31092                  });
31093                  return;
31094                }
31095
31096                this.manifest.discontinuitySequence = entry.number;
31097                currentTimeline = entry.number;
31098              },
31099              'playlist-type': function playlistType() {
31100                if (!/VOD|EVENT/.test(entry.playlistType)) {
31101                  this.trigger('warn', {
31102                    message: 'ignoring unknown playlist type: ' + entry.playlist
31103                  });
31104                  return;
31105                }
31106
31107                this.manifest.playlistType = entry.playlistType;
31108              },
31109              map: function map() {
31110                currentMap = {};
31111
31112                if (entry.uri) {
31113                  currentMap.uri = entry.uri;
31114                }
31115
31116                if (entry.byterange) {
31117                  currentMap.byterange = entry.byterange;
31118                }
31119              },
31120              'stream-inf': function streamInf() {
31121                this.manifest.playlists = uris;
31122                this.manifest.mediaGroups = this.manifest.mediaGroups || defaultMediaGroups;
31123
31124                if (!entry.attributes) {
31125                  this.trigger('warn', {
31126                    message: 'ignoring empty stream-inf attributes'
31127                  });
31128                  return;
31129                }
31130
31131                if (!currentUri.attributes) {
31132                  currentUri.attributes = {};
31133                }
31134
31135                _extends(currentUri.attributes, entry.attributes);
31136              },
31137              media: function media() {
31138                this.manifest.mediaGroups = this.manifest.mediaGroups || defaultMediaGroups;
31139
31140                if (!(entry.attributes && entry.attributes.TYPE && entry.attributes['GROUP-ID'] && entry.attributes.NAME)) {
31141                  this.trigger('warn', {
31142                    message: 'ignoring incomplete or missing media group'
31143                  });
31144                  return;
31145                } // find the media group, creating defaults as necessary
31146
31147
31148                var mediaGroupType = this.manifest.mediaGroups[entry.attributes.TYPE];
31149                mediaGroupType[entry.attributes['GROUP-ID']] = mediaGroupType[entry.attributes['GROUP-ID']] || {};
31150                mediaGroup = mediaGroupType[entry.attributes['GROUP-ID']]; // collect the rendition metadata
31151
31152                rendition = {
31153                  "default": /yes/i.test(entry.attributes.DEFAULT)
31154                };
31155
31156                if (rendition["default"]) {
31157                  rendition.autoselect = true;
31158                } else {
31159                  rendition.autoselect = /yes/i.test(entry.attributes.AUTOSELECT);
31160                }
31161
31162                if (entry.attributes.LANGUAGE) {
31163                  rendition.language = entry.attributes.LANGUAGE;
31164                }
31165
31166                if (entry.attributes.URI) {
31167                  rendition.uri = entry.attributes.URI;
31168                }
31169
31170                if (entry.attributes['INSTREAM-ID']) {
31171                  rendition.instreamId = entry.attributes['INSTREAM-ID'];
31172                }
31173
31174                if (entry.attributes.CHARACTERISTICS) {
31175                  rendition.characteristics = entry.attributes.CHARACTERISTICS;
31176                }
31177
31178                if (entry.attributes.FORCED) {
31179                  rendition.forced = /yes/i.test(entry.attributes.FORCED);
31180                } // insert the new rendition
31181
31182
31183                mediaGroup[entry.attributes.NAME] = rendition;
31184              },
31185              discontinuity: function discontinuity() {
31186                currentTimeline += 1;
31187                currentUri.discontinuity = true;
31188                this.manifest.discontinuityStarts.push(uris.length);
31189              },
31190              'program-date-time': function programDateTime() {
31191                if (typeof this.manifest.dateTimeString === 'undefined') {
31192                  // PROGRAM-DATE-TIME is a media-segment tag, but for backwards
31193                  // compatibility, we add the first occurence of the PROGRAM-DATE-TIME tag
31194                  // to the manifest object
31195                  // TODO: Consider removing this in future major version
31196                  this.manifest.dateTimeString = entry.dateTimeString;
31197                  this.manifest.dateTimeObject = entry.dateTimeObject;
31198                }
31199
31200                currentUri.dateTimeString = entry.dateTimeString;
31201                currentUri.dateTimeObject = entry.dateTimeObject;
31202              },
31203              targetduration: function targetduration() {
31204                if (!isFinite(entry.duration) || entry.duration < 0) {
31205                  this.trigger('warn', {
31206                    message: 'ignoring invalid target duration: ' + entry.duration
31207                  });
31208                  return;
31209                }
31210
31211                this.manifest.targetDuration = entry.duration;
31212              },
31213              totalduration: function totalduration() {
31214                if (!isFinite(entry.duration) || entry.duration < 0) {
31215                  this.trigger('warn', {
31216                    message: 'ignoring invalid total duration: ' + entry.duration
31217                  });
31218                  return;
31219                }
31220
31221                this.manifest.totalDuration = entry.duration;
31222              },
31223              start: function start() {
31224                if (!entry.attributes || isNaN(entry.attributes['TIME-OFFSET'])) {
31225                  this.trigger('warn', {
31226                    message: 'ignoring start declaration without appropriate attribute list'
31227                  });
31228                  return;
31229                }
31230
31231                this.manifest.start = {
31232                  timeOffset: entry.attributes['TIME-OFFSET'],
31233                  precise: entry.attributes.PRECISE
31234                };
31235              },
31236              'cue-out': function cueOut() {
31237                currentUri.cueOut = entry.data;
31238              },
31239              'cue-out-cont': function cueOutCont() {
31240                currentUri.cueOutCont = entry.data;
31241              },
31242              'cue-in': function cueIn() {
31243                currentUri.cueIn = entry.data;
31244              }
31245            })[entry.tagType] || noop).call(self);
31246          },
31247          uri: function uri() {
31248            currentUri.uri = entry.uri;
31249            uris.push(currentUri); // if no explicit duration was declared, use the target duration
31250
31251            if (this.manifest.targetDuration && !('duration' in currentUri)) {
31252              this.trigger('warn', {
31253                message: 'defaulting segment duration to the target duration'
31254              });
31255              currentUri.duration = this.manifest.targetDuration;
31256            } // annotate with encryption information, if necessary
31257
31258
31259            if (_key) {
31260              currentUri.key = _key;
31261            }
31262
31263            currentUri.timeline = currentTimeline; // annotate with initialization segment information, if necessary
31264
31265            if (currentMap) {
31266              currentUri.map = currentMap;
31267            } // prepare for the next URI
31268
31269
31270            currentUri = {};
31271          },
31272          comment: function comment() {// comments are not important for playback
31273          },
31274          custom: function custom() {
31275            // if this is segment-level data attach the output to the segment
31276            if (entry.segment) {
31277              currentUri.custom = currentUri.custom || {};
31278              currentUri.custom[entry.customType] = entry.data; // if this is manifest-level data attach to the top level manifest object
31279            } else {
31280              this.manifest.custom = this.manifest.custom || {};
31281              this.manifest.custom[entry.customType] = entry.data;
31282            }
31283          }
31284        })[entry.type].call(self);
31285      });
31286
31287      return _this;
31288    }
31289    /**
31290     * Parse the input string and update the manifest object.
31291     *
31292     * @param {string} chunk a potentially incomplete portion of the manifest
31293     */
31294
31295
31296    var _proto = Parser.prototype;
31297
31298    _proto.push = function push(chunk) {
31299      this.lineStream.push(chunk);
31300    }
31301    /**
31302     * Flush any remaining input. This can be handy if the last line of an M3U8
31303     * manifest did not contain a trailing newline but the file has been
31304     * completely received.
31305     */
31306    ;
31307
31308    _proto.end = function end() {
31309      // flush any buffered input
31310      this.lineStream.push('\n');
31311    }
31312    /**
31313     * Add an additional parser for non-standard tags
31314     *
31315     * @param {Object}   options              a map of options for the added parser
31316     * @param {RegExp}   options.expression   a regular expression to match the custom header
31317     * @param {string}   options.type         the type to register to the output
31318     * @param {Function} [options.dataParser] function to parse the line into an object
31319     * @param {boolean}  [options.segment]    should tag data be attached to the segment object
31320     */
31321    ;
31322
31323    _proto.addParser = function addParser(options) {
31324      this.parseStream.addParser(options);
31325    }
31326    /**
31327     * Add a custom header mapper
31328     *
31329     * @param {Object}   options
31330     * @param {RegExp}   options.expression   a regular expression to match the custom header
31331     * @param {Function} options.map          function to translate tag into a different tag
31332     */
31333    ;
31334
31335    _proto.addTagMapper = function addTagMapper(options) {
31336      this.parseStream.addTagMapper(options);
31337    };
31338
31339    return Parser;
31340  }(Stream);
31341
31342  /*! @name mpd-parser @version 0.8.1 @license Apache-2.0 */
31343
31344  var isObject$1 = function isObject(obj) {
31345    return !!obj && typeof obj === 'object';
31346  };
31347
31348  var merge = function merge() {
31349    for (var _len = arguments.length, objects = new Array(_len), _key = 0; _key < _len; _key++) {
31350      objects[_key] = arguments[_key];
31351    }
31352
31353    return objects.reduce(function (result, source) {
31354      Object.keys(source).forEach(function (key) {
31355        if (Array.isArray(result[key]) && Array.isArray(source[key])) {
31356          result[key] = result[key].concat(source[key]);
31357        } else if (isObject$1(result[key]) && isObject$1(source[key])) {
31358          result[key] = merge(result[key], source[key]);
31359        } else {
31360          result[key] = source[key];
31361        }
31362      });
31363      return result;
31364    }, {});
31365  };
31366
31367  var values = function values(o) {
31368    return Object.keys(o).map(function (k) {
31369      return o[k];
31370    });
31371  };
31372
31373  var range = function range(start, end) {
31374    var result = [];
31375
31376    for (var i = start; i < end; i++) {
31377      result.push(i);
31378    }
31379
31380    return result;
31381  };
31382
31383  var flatten = function flatten(lists) {
31384    return lists.reduce(function (x, y) {
31385      return x.concat(y);
31386    }, []);
31387  };
31388
31389  var from = function from(list) {
31390    if (!list.length) {
31391      return [];
31392    }
31393
31394    var result = [];
31395
31396    for (var i = 0; i < list.length; i++) {
31397      result.push(list[i]);
31398    }
31399
31400    return result;
31401  };
31402
31403  var findIndexes = function findIndexes(l, key) {
31404    return l.reduce(function (a, e, i) {
31405      if (e[key]) {
31406        a.push(i);
31407      }
31408
31409      return a;
31410    }, []);
31411  };
31412
31413  var errors = {
31414    INVALID_NUMBER_OF_PERIOD: 'INVALID_NUMBER_OF_PERIOD',
31415    DASH_EMPTY_MANIFEST: 'DASH_EMPTY_MANIFEST',
31416    DASH_INVALID_XML: 'DASH_INVALID_XML',
31417    NO_BASE_URL: 'NO_BASE_URL',
31418    MISSING_SEGMENT_INFORMATION: 'MISSING_SEGMENT_INFORMATION',
31419    SEGMENT_TIME_UNSPECIFIED: 'SEGMENT_TIME_UNSPECIFIED',
31420    UNSUPPORTED_UTC_TIMING_SCHEME: 'UNSUPPORTED_UTC_TIMING_SCHEME'
31421  };
31422
31423  function createCommonjsModule$1(fn, module) {
31424    return module = {
31425      exports: {}
31426    }, fn(module, module.exports), module.exports;
31427  }
31428
31429  var urlToolkit$1 = createCommonjsModule$1(function (module, exports) {
31430    // see https://tools.ietf.org/html/rfc1808
31431
31432    /* jshint ignore:start */
31433    (function (root) {
31434      /* jshint ignore:end */
31435      var URL_REGEX = /^((?:[a-zA-Z0-9+\-.]+:)?)(\/\/[^\/?#]*)?((?:[^\/\?#]*\/)*.*?)??(;.*?)?(\?.*?)?(#.*?)?$/;
31436      var FIRST_SEGMENT_REGEX = /^([^\/?#]*)(.*)$/;
31437      var SLASH_DOT_REGEX = /(?:\/|^)\.(?=\/)/g;
31438      var SLASH_DOT_DOT_REGEX = /(?:\/|^)\.\.\/(?!\.\.\/).*?(?=\/)/g;
31439      var URLToolkit = {
31440        // jshint ignore:line
31441        // If opts.alwaysNormalize is true then the path will always be normalized even when it starts with / or //
31442        // E.g
vendor: 4,132 bytes, lines 31443-31536
31443        // With opts.alwaysNormalize = false (default, spec compliant)
31444        // http://a.com/b/cd + /e/f/../g => http://a.com/e/f/../g
31445        // With opts.alwaysNormalize = true (not spec compliant)
31446        // http://a.com/b/cd + /e/f/../g => http://a.com/e/g
31447        buildAbsoluteURL: function buildAbsoluteURL(baseURL, relativeURL, opts) {
31448          opts = opts || {}; // remove any remaining space and CRLF
31449
31450          baseURL = baseURL.trim();
31451          relativeURL = relativeURL.trim();
31452
31453          if (!relativeURL) {
31454            // 2a) If the embedded URL is entirely empty, it inherits the
31455            // entire base URL (i.e., is set equal to the base URL)
31456            // and we are done.
31457            if (!opts.alwaysNormalize) {
31458              return baseURL;
31459            }
31460
31461            var basePartsForNormalise = URLToolkit.parseURL(baseURL);
31462
31463            if (!basePartsForNormalise) {
31464              throw new Error('Error trying to parse base URL.');
31465            }
31466
31467            basePartsForNormalise.path = URLToolkit.normalizePath(basePartsForNormalise.path);
31468            return URLToolkit.buildURLFromParts(basePartsForNormalise);
31469          }
31470
31471          var relativeParts = URLToolkit.parseURL(relativeURL);
31472
31473          if (!relativeParts) {
31474            throw new Error('Error trying to parse relative URL.');
31475          }
31476
31477          if (relativeParts.scheme) {
31478            // 2b) If the embedded URL starts with a scheme name, it is
31479            // interpreted as an absolute URL and we are done.
31480            if (!opts.alwaysNormalize) {
31481              return relativeURL;
31482            }
31483
31484            relativeParts.path = URLToolkit.normalizePath(relativeParts.path);
31485            return URLToolkit.buildURLFromParts(relativeParts);
31486          }
31487
31488          var baseParts = URLToolkit.parseURL(baseURL);
31489
31490          if (!baseParts) {
31491            throw new Error('Error trying to parse base URL.');
31492          }
31493
31494          if (!baseParts.netLoc && baseParts.path && baseParts.path[0] !== '/') {
31495            // If netLoc missing and path doesn't start with '/', assume everthing before the first '/' is the netLoc
31496            // This causes 'example.com/a' to be handled as '//example.com/a' instead of '/example.com/a'
31497            var pathParts = FIRST_SEGMENT_REGEX.exec(baseParts.path);
31498            baseParts.netLoc = pathParts[1];
31499            baseParts.path = pathParts[2];
31500          }
31501
31502          if (baseParts.netLoc && !baseParts.path) {
31503            baseParts.path = '/';
31504          }
31505
31506          var builtParts = {
31507            // 2c) Otherwise, the embedded URL inherits the scheme of
31508            // the base URL.
31509            scheme: baseParts.scheme,
31510            netLoc: relativeParts.netLoc,
31511            path: null,
31512            params: relativeParts.params,
31513            query: relativeParts.query,
31514            fragment: relativeParts.fragment
31515          };
31516
31517          if (!relativeParts.netLoc) {
31518            // 3) If the embedded URL's <net_loc> is non-empty, we skip to
31519            // Step 7.  Otherwise, the embedded URL inherits the <net_loc>
31520            // (if any) of the base URL.
31521            builtParts.netLoc = baseParts.netLoc; // 4) If the embedded URL path is preceded by a slash "/", the
31522            // path is not relative and we skip to Step 7.
31523
31524            if (relativeParts.path[0] !== '/') {
31525              if (!relativeParts.path) {
31526                // 5) If the embedded URL path is empty (and not preceded by a
31527                // slash), then the embedded URL inherits the base URL path
31528                builtParts.path = baseParts.path; // 5a) if the embedded URL's <params> is non-empty, we skip to
31529                // step 7; otherwise, it inherits the <params> of the base
31530                // URL (if any) and
31531
31532                if (!relativeParts.params) {
31533                  builtParts.params = baseParts.params; // 5b) if the embedded URL's <query> is non-empty, we skip to
31534                  // step 7; otherwise, it inherits the <query> of the base
31535                  // URL (if any) and we skip to step 7.
31536
vendor: 4,111 bytes, lines 31537-31639
31537                  if (!relativeParts.query) {
31538                    builtParts.query = baseParts.query;
31539                  }
31540                }
31541              } else {
31542                // 6) The last segment of the base URL's path (anything
31543                // following the rightmost slash "/", or the entire path if no
31544                // slash is present) is removed and the embedded URL's path is
31545                // appended in its place.
31546                var baseURLPath = baseParts.path;
31547                var newPath = baseURLPath.substring(0, baseURLPath.lastIndexOf('/') + 1) + relativeParts.path;
31548                builtParts.path = URLToolkit.normalizePath(newPath);
31549              }
31550            }
31551          }
31552
31553          if (builtParts.path === null) {
31554            builtParts.path = opts.alwaysNormalize ? URLToolkit.normalizePath(relativeParts.path) : relativeParts.path;
31555          }
31556
31557          return URLToolkit.buildURLFromParts(builtParts);
31558        },
31559        parseURL: function parseURL(url) {
31560          var parts = URL_REGEX.exec(url);
31561
31562          if (!parts) {
31563            return null;
31564          }
31565
31566          return {
31567            scheme: parts[1] || '',
31568            netLoc: parts[2] || '',
31569            path: parts[3] || '',
31570            params: parts[4] || '',
31571            query: parts[5] || '',
31572            fragment: parts[6] || ''
31573          };
31574        },
31575        normalizePath: function normalizePath(path) {
31576          // The following operations are
31577          // then applied, in order, to the new path:
31578          // 6a) All occurrences of "./", where "." is a complete path
31579          // segment, are removed.
31580          // 6b) If the path ends with "." as a complete path segment,
31581          // that "." is removed.
31582          path = path.split('').reverse().join('').replace(SLASH_DOT_REGEX, ''); // 6c) All occurrences of "<segment>/../", where <segment> is a
31583          // complete path segment not equal to "..", are removed.
31584          // Removal of these path segments is performed iteratively,
31585          // removing the leftmost matching pattern on each iteration,
31586          // until no matching pattern remains.
31587          // 6d) If the path ends with "<segment>/..", where <segment> is a
31588          // complete path segment not equal to "..", that
31589          // "<segment>/.." is removed.
31590
31591          while (path.length !== (path = path.replace(SLASH_DOT_DOT_REGEX, '')).length) {} // jshint ignore:line
31592
31593
31594          return path.split('').reverse().join('');
31595        },
31596        buildURLFromParts: function buildURLFromParts(parts) {
31597          return parts.scheme + parts.netLoc + parts.path + parts.params + parts.query + parts.fragment;
31598        }
31599      };
31600      /* jshint ignore:start */
31601
31602      module.exports = URLToolkit;
31603    })();
31604    /* jshint ignore:end */
31605
31606  });
31607
31608  var resolveUrl = function resolveUrl(baseUrl, relativeUrl) {
31609    // return early if we don't need to resolve
31610    if (/^[a-z]+:/i.test(relativeUrl)) {
31611      return relativeUrl;
31612    } // if the base URL is relative then combine with the current location
31613
31614
31615    if (!/\/\//i.test(baseUrl)) {
31616      baseUrl = urlToolkit$1.buildAbsoluteURL(window$1.location.href, baseUrl);
31617    }
31618
31619    return urlToolkit$1.buildAbsoluteURL(baseUrl, relativeUrl);
31620  };
31621  /**
31622   * @typedef {Object} SingleUri
31623   * @property {string} uri - relative location of segment
31624   * @property {string} resolvedUri - resolved location of segment
31625   * @property {Object} byterange - Object containing information on how to make byte range
31626   *   requests following byte-range-spec per RFC2616.
31627   * @property {String} byterange.length - length of range request
31628   * @property {String} byterange.offset - byte offset of range request
31629   *
31630   * @see https://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.35.1
31631   */
31632
31633  /**
31634   * Converts a URLType node (5.3.9.2.3 Table 13) to a segment object
31635   * that conforms to how m3u8-parser is structured
31636   *
31637   * @see https://github.com/videojs/m3u8-parser
31638   *
31639   * @param {string} baseUrl - baseUrl provided by <BaseUrl>
31639 nodes
31640   * @param {string} source - source url for segment
31641   * @param {string} range - optional range used for range calls,
31642   *   follows  RFC 2616, Clause 14.35.1
31643   * @return {SingleUri} full segment information transformed into a format similar
31644   *   to m3u8-parser
31645   */
31646
31647
31648  var urlTypeToSegment = function urlTypeToSegment(_ref) {
31649    var _ref$baseUrl = _ref.baseUrl,
31650        baseUrl = _ref$baseUrl === void 0 ? '' : _ref$baseUrl,
31651        _ref$source = _ref.source,
31652        source = _ref$source === void 0 ? '' : _ref$source,
31653        _ref$range = _ref.range,
31654        range = _ref$range === void 0 ? '' : _ref$range,
31655        _ref$indexRange = _ref.indexRange,
31656        indexRange = _ref$indexRange === void 0 ? '' : _ref$indexRange;
31657    var segment = {
31658      uri: source,
31659      resolvedUri: resolveUrl(baseUrl || '', source)
31660    };
31661
31662    if (range || indexRange) {
31663      var rangeStr = range ? range : indexRange;
31664      var ranges = rangeStr.split('-');
31665      var startRange = parseInt(ranges[0], 10);
31666      var endRange = parseInt(ranges[1], 10); // byterange should be inclusive according to
31667      // RFC 2616, Clause 14.35.1
31668
31669      segment.byterange = {
31670        length: endRange - startRange + 1,
31671        offset: startRange
31672      };
31673    }
31674
31675    return segment;
31676  };
31677
31678  var byteRangeToString = function byteRangeToString(byterange) {
31679    // `endRange` is one less than `offset + length` because the HTTP range
31680    // header uses inclusive ranges
31681    var endRange = byterange.offset + byterange.length - 1;
31682    return byterange.offset + "-" + endRange;
31683  };
31684  /**
31685   * Functions for calculating the range of available segments in static and dynamic
31686   * manifests.
31687   */
31688
31689
31690  var segmentRange = {
31691    /**
31692     * Returns the entire range of available segments for a static MPD
31693     *
31694     * @param {Object} attributes
31695     *        Inheritied MPD attributes
31696     * @return {{ start: number, end: number }}
31697     *         The start and end numbers for available segments
31698     */
31699    "static": function _static(attributes) {
31700      var duration = attributes.duration,
31701          _attributes$timescale = attributes.timescale,
31702          timescale = _attributes$timescale === void 0 ? 1 : _attributes$timescale,
31703          sourceDuration = attributes.sourceDuration;
31704      return {
31705        start: 0,
31706        end: Math.ceil(sourceDuration / (duration / timescale))
31707      };
31708    },
31709
31710    /**
31711     * Returns the current live window range of available segments for a dynamic MPD
31712     *
31713     * @param {Object} attributes
31714     *        Inheritied MPD attributes
31715     * @return {{ start: number, end: number }}
31716     *         The start and end numbers for available segments
31717     */
31718    dynamic: function dynamic(attributes) {
31719      var NOW = attributes.NOW,
31720          clientOffset = attributes.clientOffset,
31721          availabilityStartTime = attributes.availabilityStartTime,
31722          _attributes$timescale2 = attributes.timescale,
31723          timescale = _attributes$timescale2 === void 0 ? 1 : _attributes$timescale2,
31724          duration = attributes.duration,
31725          _attributes$start = attributes.start,
31726          start = _attributes$start === void 0 ? 0 : _attributes$start,
31727          _attributes$minimumUp = attributes.minimumUpdatePeriod,
31728          minimumUpdatePeriod = _attributes$minimumUp === void 0 ? 0 : _attributes$minimumUp,
31729          _attributes$timeShift = attributes.timeShiftBufferDepth,
31730          timeShiftBufferDepth = _attributes$timeShift === void 0 ? Infinity : _attributes$timeShift;
31731      var now = (NOW + clientOffset) / 1000;
31732      var periodStartWC = availabilityStartTime + start;
31733      var periodEndWC = now + minimumUpdatePeriod;
31734      var periodDuration = periodEndWC - periodStartWC;
31735      var segmentCount = Math.ceil(periodDuration * timescale / duration);
31736      var availableStart = Math.floor((now - periodStartWC - timeShiftBufferDepth) * timescale / duration);
31737      var availableEnd = Math.floor((now - periodStartWC) * timescale / duration);
31738      return {
31739        start: Math.max(0, availableStart),
31740        end: Math.min(segmentCount, availableEnd)
31741      };
31742    }
31743  };
31744  /**
31745   * Maps a range of numbers to objects with information needed to build the corresponding
31746   * segment list
31747   *
31748   * @name toSegmentsCallback
31749   * @function
31750   * @param {number} number
31751   *        Number of the segment
31752   * @param {number} index
31753   *        Index of the number in the range list
31754   * @return {{ number: Number, duration: Number, timeline: Number, time: Number }}
31755   *         Object with segment timing and duration info
31756   */
31757
31758  /**
31759   * Returns a callback for Array.prototype.map for mapping a range of numbers to
31760   * information needed to build the segment list.
31761   *
31762   * @param {Object} attributes
31763   *        Inherited MPD attributes
31764   * @return {toSegmentsCallback}
31765   *         Callback map function
31766   */
31767
31768  var toSegments = function toSegments(attributes) {
31769    return function (number, index) {
31770      var duration = attributes.duration,
31771          _attributes$timescale3 = attributes.timescale,
31772          timescale = _attributes$timescale3 === void 0 ? 1 : _attributes$timescale3,
31773          periodIndex = attributes.periodIndex,
31774          _attributes$startNumb = attributes.startNumber,
31775          startNumber = _attributes$startNumb === void 0 ? 1 : _attributes$startNumb;
31776      return {
31777        number: startNumber + number,
31778        duration: duration / timescale,
31779        timeline: periodIndex,
31780        time: index * duration
31781      };
31782    };
31783  };
31784  /**
31785   * Returns a list of objects containing segment timing and duration info used for
31786   * building the list of segments. This uses the @duration attribute specified
31787   * in the MPD manifest to derive the range of segments.
31788   *
31789   * @param {Object} attributes
31790   *        Inherited MPD attributes
31791   * @return {{number: number, duration: number, time: number, timeline: number}[]}
31792   *         List of Objects with segment timing and duration info
31793   */
31794
31795
31796  var parseByDuration = function parseByDuration(attributes) {
31797    var _attributes$type = attributes.type,
31798        type = _attributes$type === void 0 ? 'static' : _attributes$type,
31799        duration = attributes.duration,
31800        _attributes$timescale4 = attributes.timescale,
31801        timescale = _attributes$timescale4 === void 0 ? 1 : _attributes$timescale4,
31802        sourceDuration = attributes.sourceDuration;
31803
31804    var _segmentRange$type = segmentRange[type](attributes),
31805        start = _segmentRange$type.start,
31806        end = _segmentRange$type.end;
31807
31808    var segments = range(start, end).map(toSegments(attributes));
31809
31810    if (type === 'static') {
31811      var index = segments.length - 1; // final segment may be less than full segment duration
31812
31813      segments[index].duration = sourceDuration - duration / timescale * index;
31814    }
31815
31816    return segments;
31817  };
31818  /**
31819   * Translates SegmentBase into a set of segments.
31820   * (DASH SPEC Section 5.3.9.3.2) contains a set of <SegmentURL> nodes.  Each
31821   * node should be translated into segment.
31822   *
31823   * @param {Object} attributes
31824   *   Object containing all inherited attributes from parent elements with attribute
31825   *   names as keys
31826   * @return {Object.<Array>} list of segments
31827   */
31828
31829
31830  var segmentsFromBase = function segmentsFromBase(attributes) {
31831    var baseUrl = attributes.baseUrl,
31832        _attributes$initializ = attributes.initialization,
31833        initialization = _attributes$initializ === void 0 ? {}
31833 : _attributes$initializ,
31834        sourceDuration = attributes.sourceDuration,
31835        _attributes$timescale = attributes.timescale,
31836        timescale = _attributes$timescale === void 0 ? 1 : _attributes$timescale,
31837        _attributes$indexRang = attributes.indexRange,
31838        indexRange = _attributes$indexRang === void 0 ? '' : _attributes$indexRang,
31839        duration = attributes.duration; // base url is required for SegmentBase to work, per spec (Section 5.3.9.2.1)
31840
31841    if (!baseUrl) {
31842      throw new Error(errors.NO_BASE_URL);
31843    }
31844
31845    var initSegment = urlTypeToSegment({
31846      baseUrl: baseUrl,
31847      source: initialization.sourceURL,
31848      range: initialization.range
31849    });
31850    var segment = urlTypeToSegment({
31851      baseUrl: baseUrl,
31852      source: baseUrl,
31853      indexRange: indexRange
31854    });
31855    segment.map = initSegment; // If there is a duration, use it, otherwise use the given duration of the source
31856    // (since SegmentBase is only for one total segment)
31857
31858    if (duration) {
31859      var segmentTimeInfo = parseByDuration(attributes);
31860
31861      if (segmentTimeInfo.length) {
31862        segment.duration = segmentTimeInfo[0].duration;
31863        segment.timeline = segmentTimeInfo[0].timeline;
31864      }
31865    } else if (sourceDuration) {
31866      segment.duration = sourceDuration / timescale;
31867      segment.timeline = 0;
31868    } // This is used for mediaSequence
31869
31870
31871    segment.number = 0;
31872    return [segment];
31873  };
31874  /**
31875   * Given a playlist, a sidx box, and a baseUrl, update the segment list of the playlist
31876   * according to the sidx information given.
31877   *
31878   * playlist.sidx has metadadata about the sidx where-as the sidx param
31879   * is the parsed sidx box itself.
31880   *
31881   * @param {Object} playlist the playlist to update the sidx information for
31882   * @param {Object} sidx the parsed sidx box
31883   * @return {Object} the playlist object with the updated sidx information
31884   */
31885
31886
31887  var addSegmentsToPlaylist = function addSegmentsToPlaylist(playlist, sidx, baseUrl) {
31888    // Retain init segment information
31889    var initSegment = playlist.sidx.map ? playlist.sidx.map : null; // Retain source duration from initial master manifest parsing
31890
31891    var sourceDuration = playlist.sidx.duration; // Retain source timeline
31892
31893    var timeline = playlist.timeline || 0;
31894    var sidxByteRange = playlist.sidx.byterange;
31895    var sidxEnd = sidxByteRange.offset + sidxByteRange.length; // Retain timescale of the parsed sidx
31896
31897    var timescale = sidx.timescale; // referenceType 1 refers to other sidx boxes
31898
31899    var mediaReferences = sidx.references.filter(function (r) {
31900      return r.referenceType !== 1;
31901    });
31902    var segments = []; // firstOffset is the offset from the end of the sidx box
31903
31904    var startIndex = sidxEnd + sidx.firstOffset;
31905
31906    for (var i = 0; i < mediaReferences.length; i++) {
31907      var reference = sidx.references[i]; // size of the referenced (sub)segment
31908
31909      var size = reference.referencedSize; // duration of the referenced (sub)segment, in  the  timescale
31910      // this will be converted to seconds when generating segments
31911
31912      var duration = reference.subsegmentDuration; // should be an inclusive range
31913
31914      var endIndex = startIndex + size - 1;
31915      var indexRange = startIndex + "-" + endIndex;
31916      var attributes = {
31917        baseUrl: baseUrl,
31918        timescale: timescale,
31919        timeline: timeline,
31920        // this is used in parseByDuration
31921        periodIndex: timeline,
31922        duration: duration,
31923        sourceDuration: sourceDuration,
31924        indexRange: indexRange
31925      };
31926      var segment = segmentsFromBase(attributes)[0];
31927
31928      if (initSegment) {
31929        segment.map = initSegment;
31930      }
31931
31932      segments.push(segment);
31933      startIndex += size;
31934    }
31935
31936    playlist.segments = segments;
31937    return playlist;
31938  };
31939
31940  var mergeDiscontiguousPlaylists = function mergeDiscontiguousPlaylists(playlists) {
31941    var mergedPlaylists = values(playlists.reduce(function (acc, playlist) {
31942      // assuming playlist IDs are the same across periods
31943      // TODO: handle multiperiod where representation sets are not the same
31944      // across periods
31945      var name = playlist.attributes.id + (playlist.attributes.lang || ''); // Periods after first
31946
31947      if (acc[name]) {
31948        var _acc$name$segments; // first segment of subsequent periods signal a discontinuity
31949
31950
31951        if (playlist.segments[0]) {
31952          playlist.segments[0].discontinuity = true;
31953        }
31954
31955        (_acc$name$segments = acc[name].segments).push.apply(_acc$name$segments, playlist.segments); // bubble up contentProtection, this assumes all DRM content
31956        // has the same contentProtection
31957
31958
31959        if (playlist.attributes.contentProtection) {
31960          acc[name].attributes.contentProtection = playlist.attributes.contentProtection;
31961        }
31962      } else {
31963        // first Period
31964        acc[name] = playlist;
31965      }
31966
31967      return acc;
31968    }, {}));
31969    return mergedPlaylists.map(function (playlist) {
31970      playlist.discontinuityStarts = findIndexes(playlist.segments, 'discontinuity');
31971      return playlist;
31972    });
vendor: 16,384 bytes, lines 31973-32457
31973  };
31974
31975  var addSegmentInfoFromSidx = function addSegmentInfoFromSidx(playlists, sidxMapping) {
31976    if (sidxMapping === void 0) {
31977      sidxMapping = {};
31978    }
31979
31980    if (!Object.keys(sidxMapping).length) {
31981      return playlists;
31982    }
31983
31984    for (var i in playlists) {
31985      var playlist = playlists[i];
31986
31987      if (!playlist.sidx) {
31988        continue;
31989      }
31990
31991      var sidxKey = playlist.sidx.uri + '-' + byteRangeToString(playlist.sidx.byterange);
31992      var sidxMatch = sidxMapping[sidxKey] && sidxMapping[sidxKey].sidx;
31993
31994      if (playlist.sidx && sidxMatch) {
31995        addSegmentsToPlaylist(playlist, sidxMatch, playlist.sidx.resolvedUri);
31996      }
31997    }
31998
31999    return playlists;
32000  };
32001
32002  var formatAudioPlaylist = function formatAudioPlaylist(_ref) {
32003    var _attributes;
32004
32005    var attributes = _ref.attributes,
32006        segments = _ref.segments,
32007        sidx = _ref.sidx;
32008    var playlist = {
32009      attributes: (_attributes = {
32010        NAME: attributes.id,
32011        BANDWIDTH: attributes.bandwidth,
32012        CODECS: attributes.codecs
32013      }, _attributes['PROGRAM-ID'] = 1, _attributes),
32014      uri: '',
32015      endList: (attributes.type || 'static') === 'static',
32016      timeline: attributes.periodIndex,
32017      resolvedUri: '',
32018      targetDuration: attributes.duration,
32019      segments: segments,
32020      mediaSequence: segments.length ? segments[0].number : 1
32021    };
32022
32023    if (attributes.contentProtection) {
32024      playlist.contentProtection = attributes.contentProtection;
32025    }
32026
32027    if (sidx) {
32028      playlist.sidx = sidx;
32029    }
32030
32031    return playlist;
32032  };
32033
32034  var formatVttPlaylist = function formatVttPlaylist(_ref2) {
32035    var _attributes2;
32036
32037    var attributes = _ref2.attributes,
32038        segments = _ref2.segments;
32039
32040    if (typeof segments === 'undefined') {
32041      // vtt tracks may use single file in BaseURL
32042      segments = [{
32043        uri: attributes.baseUrl,
32044        timeline: attributes.periodIndex,
32045        resolvedUri: attributes.baseUrl || '',
32046        duration: attributes.sourceDuration,
32047        number: 0
32048      }]; // targetDuration should be the same duration as the only segment
32049
32050      attributes.duration = attributes.sourceDuration;
32051    }
32052
32053    return {
32054      attributes: (_attributes2 = {
32055        NAME: attributes.id,
32056        BANDWIDTH: attributes.bandwidth
32057      }, _attributes2['PROGRAM-ID'] = 1, _attributes2),
32058      uri: '',
32059      endList: (attributes.type || 'static') === 'static',
32060      timeline: attributes.periodIndex,
32061      resolvedUri: attributes.baseUrl || '',
32062      targetDuration: attributes.duration,
32063      segments: segments,
32064      mediaSequence: segments.length ? segments[0].number : 1
32065    };
32066  };
32067
32068  var organizeAudioPlaylists = function organizeAudioPlaylists(playlists, sidxMapping) {
32069    if (sidxMapping === void 0) {
32070      sidxMapping = {};
32071    }
32072
32073    var mainPlaylist;
32074    var formattedPlaylists = playlists.reduce(function (a, playlist) {
32075      var role = playlist.attributes.role && playlist.attributes.role.value || '';
32076      var language = playlist.attributes.lang || '';
32077      var label = 'main';
32078
32079      if (language) {
32080        var roleLabel = role ? " (" + role + ")" : '';
32081        label = "" + playlist.attributes.lang + roleLabel;
32082      } // skip if we already have the highest quality audio for a language
32083
32084
32085      if (a[label] && a[label].playlists[0].attributes.BANDWIDTH > playlist.attributes.bandwidth) {
32086        return a;
32087      }
32088
32089      a[label] = {
32090        language: language,
32091        autoselect: true,
32092        "default": role === 'main',
32093        playlists: addSegmentInfoFromSidx([formatAudioPlaylist(playlist)], sidxMapping),
32094        uri: ''
32095      };
32096
32097      if (typeof mainPlaylist === 'undefined' && role === 'main') {
32098        mainPlaylist = playlist;
32099        mainPlaylist["default"] = true;
32100      }
32101
32102      return a;
32103    }, {}); // if no playlists have role "main", mark the first as main
32104
32105    if (!mainPlaylist) {
32106      var firstLabel = Object.keys(formattedPlaylists)[0];
32107      formattedPlaylists[firstLabel]["default"] = true;
32108    }
32109
32110    return formattedPlaylists;
32111  };
32112
32113  var organizeVttPlaylists = function organizeVttPlaylists(playlists, sidxMapping) {
32114    if (sidxMapping === void 0) {
32115      sidxMapping = {};
32116    }
32117
32118    return playlists.reduce(function (a, playlist) {
32119      var label = playlist.attributes.lang || 'text'; // skip if we already have subtitles
32120
32121      if (a[label]) {
32122        return a;
32123      }
32124
32125      a[label] = {
32126        language: label,
32127        "default": false,
32128        autoselect: false,
32129        playlists: addSegmentInfoFromSidx([formatVttPlaylist(playlist)], sidxMapping),
32130        uri: ''
32131      };
32132      return a;
32133    }, {});
32134  };
32135
32136  var formatVideoPlaylist = function formatVideoPlaylist(_ref3) {
32137    var _attributes3;
32138
32139    var attributes = _ref3.attributes,
32140        segments = _ref3.segments,
32141        sidx = _ref3.sidx;
32142    var playlist = {
32143      attributes: (_attributes3 = {
32144        NAME: attributes.id,
32145        AUDIO: 'audio',
32146        SUBTITLES: 'subs',
32147        RESOLUTION: {
32148          width: attributes.width,
32149          height: attributes.height
32150        },
32151        CODECS: attributes.codecs,
32152        BANDWIDTH: attributes.bandwidth
32153      }, _attributes3['PROGRAM-ID'] = 1, _attributes3),
32154      uri: '',
32155      endList: (attributes.type || 'static') === 'static',
32156      timeline: attributes.periodIndex,
32157      resolvedUri: '',
32158      targetDuration: attributes.duration,
32159      segments: segments,
32160      mediaSequence: segments.length ? segments[0].number : 1
32161    };
32162
32163    if (attributes.contentProtection) {
32164      playlist.contentProtection = attributes.contentProtection;
32165    }
32166
32167    if (sidx) {
32168      playlist.sidx = sidx;
32169    }
32170
32171    return playlist;
32172  };
32173
32174  var toM3u8 = function toM3u8(dashPlaylists, sidxMapping) {
32175    var _mediaGroups;
32176
32177    if (sidxMapping === void 0) {
32178      sidxMapping = {};
32179    }
32180
32181    if (!dashPlaylists.length) {
32182      return {};
32183    } // grab all master attributes
32184
32185
32186    var _dashPlaylists$0$attr = dashPlaylists[0].attributes,
32187        duration = _dashPlaylists$0$attr.sourceDuration,
32188        _dashPlaylists$0$attr2 = _dashPlaylists$0$attr.minimumUpdatePeriod,
32189        minimumUpdatePeriod = _dashPlaylists$0$attr2 === void 0 ? 0 : _dashPlaylists$0$attr2;
32190
32191    var videoOnly = function videoOnly(_ref4) {
32192      var attributes = _ref4.attributes;
32193      return attributes.mimeType === 'video/mp4' || attributes.contentType === 'video';
32194    };
32195
32196    var audioOnly = function audioOnly(_ref5) {
32197      var attributes = _ref5.attributes;
32198      return attributes.mimeType === 'audio/mp4' || attributes.contentType === 'audio';
32199    };
32200
32201    var vttOnly = function vttOnly(_ref6) {
32202      var attributes = _ref6.attributes;
32203      return attributes.mimeType === 'text/vtt' || attributes.contentType === 'text';
32204    };
32205
32206    var videoPlaylists = mergeDiscontiguousPlaylists(dashPlaylists.filter(videoOnly)).map(formatVideoPlaylist);
32207    var audioPlaylists = mergeDiscontiguousPlaylists(dashPlaylists.filter(audioOnly));
32208    var vttPlaylists = dashPlaylists.filter(vttOnly);
32209    var master = {
32210      allowCache: true,
32211      discontinuityStarts: [],
32212      segments: [],
32213      endList: true,
32214      mediaGroups: (_mediaGroups = {
32215        AUDIO: {},
32216        VIDEO: {}
32217      }, _mediaGroups['CLOSED-CAPTIONS'] = {}, _mediaGroups.SUBTITLES = {}, _mediaGroups),
32218      uri: '',
32219      duration: duration,
32220      playlists: addSegmentInfoFromSidx(videoPlaylists, sidxMapping),
32221      minimumUpdatePeriod: minimumUpdatePeriod * 1000
32222    };
32223
32224    if (audioPlaylists.length) {
32225      master.mediaGroups.AUDIO.audio = organizeAudioPlaylists(audioPlaylists, sidxMapping);
32226    }
32227
32228    if (vttPlaylists.length) {
32229      master.mediaGroups.SUBTITLES.subs = organizeVttPlaylists(vttPlaylists, sidxMapping);
32230    }
32231
32232    return master;
32233  };
32234  /**
32235   * Calculates the R (repetition) value for a live stream (for the final segment
32236   * in a manifest where the r value is negative 1)
32237   *
32238   * @param {Object} attributes
32239   *        Object containing all inherited attributes from parent elements with attribute
32240   *        names as keys
32241   * @param {number} time
32242   *        current time (typically the total time up until the final segment)
32243   * @param {number} duration
32244   *        duration property for the given <S />
32245   *
32246   * @return {number}
32247   *        R value to reach the end of the given period
32248   */
32249
32250
32251  var getLiveRValue = function getLiveRValue(attributes, time, duration) {
32252    var NOW = attributes.NOW,
32253        clientOffset = attributes.clientOffset,
32254        availabilityStartTime = attributes.availabilityStartTime,
32255        _attributes$timescale = attributes.timescale,
32256        timescale = _attributes$timescale === void 0 ? 1 : _attributes$timescale,
32257        _attributes$start = attributes.start,
32258        start = _attributes$start === void 0 ? 0 : _attributes$start,
32259        _attributes$minimumUp = attributes.minimumUpdatePeriod,
32260        minimumUpdatePeriod = _attributes$minimumUp === void 0 ? 0 : _attributes$minimumUp;
32261    var now = (NOW + clientOffset) / 1000;
32262    var periodStartWC = availabilityStartTime + start;
32263    var periodEndWC = now + minimumUpdatePeriod;
32264    var periodDuration = periodEndWC - periodStartWC;
32265    return Math.ceil((periodDuration * timescale - time) / duration);
32266  };
32267  /**
32268   * Uses information provided by SegmentTemplate.SegmentTimeline to determine segment
32269   * timing and duration
32270   *
32271   * @param {Object} attributes
32272   *        Object containing all inherited attributes from parent elements with attribute
32273   *        names as keys
32274   * @param {Object[]} segmentTimeline
32275   *        List of objects representing the attributes of each S element contained within
32276   *
32277   * @return {{number: number, duration: number, time: number, timeline: number}[]}
32278   *         List of Objects with segment timing and duration info
32279   */
32280
32281
32282  var parseByTimeline = function parseByTimeline(attributes, segmentTimeline) {
32283    var _attributes$type = attributes.type,
32284        type = _attributes$type === void 0 ? 'static' : _attributes$type,
32285        _attributes$minimumUp2 = attributes.minimumUpdatePeriod,
32286        minimumUpdatePeriod = _attributes$minimumUp2 === void 0 ? 0 : _attributes$minimumUp2,
32287        _attributes$media = attributes.media,
32288        media = _attributes$media === void 0 ? '' : _attributes$media,
32289        sourceDuration = attributes.sourceDuration,
32290        _attributes$timescale2 = attributes.timescale,
32291        timescale = _attributes$timescale2 === void 0 ? 1 : _attributes$timescale2,
32292        _attributes$startNumb = attributes.startNumber,
32293        startNumber = _attributes$startNumb === void 0 ? 1 : _attributes$startNumb,
32294        timeline = attributes.periodIndex;
32295    var segments = [];
32296    var time = -1;
32297
32298    for (var sIndex = 0; sIndex < segmentTimeline.length; sIndex++) {
32299      var S = segmentTimeline[sIndex];
32300      var duration = S.d;
32301      var repeat = S.r || 0;
32302      var segmentTime = S.t || 0;
32303
32304      if (time < 0) {
32305        // first segment
32306        time = segmentTime;
32307      }
32308
32309      if (segmentTime && segmentTime > time) {
32310        // discontinuity
32311        // TODO: How to handle this type of discontinuity
32312        // timeline++ here would treat it like HLS discontuity and content would
32313        // get appended without gap
32314        // E.G.
32315        //  <S t="0" d="1" />
32316        //  <S d="1" />
32317        //  <S d="1" />
32318        //  <S t="5" d="1" />
32319        // would have $Time$ values of [0, 1, 2, 5]
32320        // should this be appened at time positions [0, 1, 2, 3],(#EXT-X-DISCONTINUITY)
32321        // or [0, 1, 2, gap, gap, 5]? (#EXT-X-GAP)
32322        // does the value of sourceDuration consider this when calculating arbitrary
32323        // negative @r repeat value?
32324        // E.G. Same elements as above with this added at the end
32325        //  <S d="1" r="-1" />
32326        //  with a sourceDuration of 10
32327        // Would the 2 gaps be included in the time duration calculations resulting in
32328        // 8 segments with $Time$ values of [0, 1, 2, 5, 6, 7, 8, 9] or 10 segments
32329        // with $Time$ values of [0, 1, 2, 5, 6, 7, 8, 9, 10, 11] ?
32330        time = segmentTime;
32331      }
32332
32333      var count = void 0;
32334
32335      if (repeat < 0) {
32336        var nextS = sIndex + 1;
32337
32338        if (nextS === segmentTimeline.length) {
32339          // last segment
32340          if (type === 'dynamic' && minimumUpdatePeriod > 0 && media.indexOf('$Number$') > 0) {
32341            count = getLiveRValue(attributes, time, duration);
32342          } else {
32343            // TODO: This may be incorrect depending on conclusion of TODO above
32344            count = (sourceDuration * timescale - time) / duration;
32345          }
32346        } else {
32347          count = (segmentTimeline[nextS].t - time) / duration;
32348        }
32349      } else {
32350        count = repeat + 1;
32351      }
32352
32353      var end = startNumber + segments.length + count;
32354      var number = startNumber + segments.length;
32355
32356      while (number < end) {
32357        segments.push({
32358          number: number,
32359          duration: duration / timescale,
32360          time: time,
32361          timeline: timeline
32362        });
32363        time += duration;
32364        number++;
32365      }
32366    }
32367
32368    return segments;
32369  };
32370
32371  var identifierPattern = /\$([A-z]*)(?:(%0)([0-9]+)d)?\$/g;
32372  /**
32373   * Replaces template identifiers with corresponding values. To be used as the callback
32374   * for String.prototype.replace
32375   *
32376   * @name replaceCallback
32377   * @function
32378   * @param {string} match
32379   *        Entire match of identifier
32380   * @param {string} identifier
32381   *        Name of matched identifier
32382   * @param {string} format
32383   *        Format tag string. Its presence indicates that padding is expected
32384   * @param {string} width
32385   *        Desired length of the replaced value. Values less than this width shall be left
32386   *        zero padded
32387   * @return {string}
32388   *         Replacement for the matched identifier
32389   */
32390
32391  /**
32392   * Returns a function to be used as a callback for String.prototype.replace to replace
32393   * template identifiers
32394   *
32395   * @param {Obect} values
32396   *        Object containing values that shall be used to replace known identifiers
32397   * @param {number} values.RepresentationID
32398   *        Value of the Representation@id attribute
32399   * @param {number} values.Number
32400   *        Number of the corresponding segment
32401   * @param {number} values.Bandwidth
32402   *        Value of the Representation@bandwidth attribute.
32403   * @param {number} values.Time
32404   *        Timestamp value of the corresponding segment
32405   * @return {replaceCallback}
32406   *         Callback to be used with String.prototype.replace to replace identifiers
32407   */
32408
32409  var identifierReplacement = function identifierReplacement(values) {
32410    return function (match, identifier, format, width) {
32411      if (match === '$$') {
32412        // escape sequence
32413        return '$';
32414      }
32415
32416      if (typeof values[identifier] === 'undefined') {
32417        return match;
32418      }
32419
32420      var value = '' + values[identifier];
32421
32422      if (identifier === 'RepresentationID') {
32423        // Format tag shall not be present with RepresentationID
32424        return value;
32425      }
32426
32427      if (!format) {
32428        width = 1;
32429      } else {
32430        width = parseInt(width, 10);
32431      }
32432
32433      if (value.length >= width) {
32434        return value;
32435      }
32436
32437      return "" + new Array(width - value.length + 1).join('0') + value;
32438    };
32439  };
32440  /**
32441   * Constructs a segment url from a template string
32442   *
32443   * @param {string} url
32444   *        Template string to construct url from
32445   * @param {Obect} values
32446   *        Object containing values that shall be used to replace known identifiers
32447   * @param {number} values.RepresentationID
32448   *        Value of the Representation@id attribute
32449   * @param {number} values.Number
32450   *        Number of the corresponding segment
32451   * @param {number} values.Bandwidth
32452   *        Value of the Representation@bandwidth attribute.
32453   * @param {number} values.Time
32454   *        Timestamp value of the corresponding segment
32455   * @return {string}
32456   *         Segment url with identifiers replaced
32457   
vendor: 8,586 bytes, lines 32457-32684
32457*/
32458
32459
32460  var constructTemplateUrl = function constructTemplateUrl(url, values) {
32461    return url.replace(identifierPattern, identifierReplacement(values));
32462  };
32463  /**
32464   * Generates a list of objects containing timing and duration information about each
32465   * segment needed to generate segment uris and the complete segment object
32466   *
32467   * @param {Object} attributes
32468   *        Object containing all inherited attributes from parent elements with attribute
32469   *        names as keys
32470   * @param {Object[]|undefined} segmentTimeline
32471   *        List of objects representing the attributes of each S element contained within
32472   *        the SegmentTimeline element
32473   * @return {{number: number, duration: number, time: number, timeline: number}[]}
32474   *         List of Objects with segment timing and duration info
32475   */
32476
32477
32478  var parseTemplateInfo = function parseTemplateInfo(attributes, segmentTimeline) {
32479    if (!attributes.duration && !segmentTimeline) {
32480      // if neither @duration or SegmentTimeline are present, then there shall be exactly
32481      // one media segment
32482      return [{
32483        number: attributes.startNumber || 1,
32484        duration: attributes.sourceDuration,
32485        time: 0,
32486        timeline: attributes.periodIndex
32487      }];
32488    }
32489
32490    if (attributes.duration) {
32491      return parseByDuration(attributes);
32492    }
32493
32494    return parseByTimeline(attributes, segmentTimeline);
32495  };
32496  /**
32497   * Generates a list of segments using information provided by the SegmentTemplate element
32498   *
32499   * @param {Object} attributes
32500   *        Object containing all inherited attributes from parent elements with attribute
32501   *        names as keys
32502   * @param {Object[]|undefined} segmentTimeline
32503   *        List of objects representing the attributes of each S element contained within
32504   *        the SegmentTimeline element
32505   * @return {Object[]}
32506   *         List of segment objects
32507   */
32508
32509
32510  var segmentsFromTemplate = function segmentsFromTemplate(attributes, segmentTimeline) {
32511    var templateValues = {
32512      RepresentationID: attributes.id,
32513      Bandwidth: attributes.bandwidth || 0
32514    };
32515    var _attributes$initializ = attributes.initialization,
32516        initialization = _attributes$initializ === void 0 ? {
32517      sourceURL: '',
32518      range: ''
32519    } : _attributes$initializ;
32520    var mapSegment = urlTypeToSegment({
32521      baseUrl: attributes.baseUrl,
32522      source: constructTemplateUrl(initialization.sourceURL, templateValues),
32523      range: initialization.range
32524    });
32525    var segments = parseTemplateInfo(attributes, segmentTimeline);
32526    return segments.map(function (segment) {
32527      templateValues.Number = segment.number;
32528      templateValues.Time = segment.time;
32529      var uri = constructTemplateUrl(attributes.media || '', templateValues);
32530      return {
32531        uri: uri,
32532        timeline: segment.timeline,
32533        duration: segment.duration,
32534        resolvedUri: resolveUrl(attributes.baseUrl || '', uri),
32535        map: mapSegment,
32536        number: segment.number
32537      };
32538    });
32539  };
32540  /**
32541   * Converts a <SegmentUrl> (of type URLType from the DASH spec 5.3.9.2 Table 14)
32542   * to an object that matches the output of a segment in videojs/mpd-parser
32543   *
32544   * @param {Object} attributes
32545   *   Object containing all inherited attributes from parent elements with attribute
32546   *   names as keys
32547   * @param {Object} segmentUrl
32548   *   <SegmentURL> node to translate into a segment object
32549   * @return {Object} translated segment object
32550   */
32551
32552
32553  var SegmentURLToSegmentObject = function SegmentURLToSegmentObject(attributes, segmentUrl) {
32554    var baseUrl = attributes.baseUrl,
32555        _attributes$initializ = attributes.initialization,
32556        initialization = _attributes$initializ === void 0 ? {} : _attributes$initializ;
32557    var initSegment = urlTypeToSegment({
32558      baseUrl: baseUrl,
32559      source: initialization.sourceURL,
32560      range: initialization.range
32561    });
32562    var segment = urlTypeToSegment({
32563      baseUrl: baseUrl,
32564      source: segmentUrl.media,
32565      range: segmentUrl.mediaRange
32566    });
32567    segment.map = initSegment;
32568    return segment;
32569  };
32570  /**
32571   * Generates a list of segments using information provided by the SegmentList element
32572   * SegmentList (DASH SPEC Section 5.3.9.3.2) contains a set of <SegmentURL> nodes.  Each
32573   * node should be translated into segment.
32574   *
32575   * @param {Object} attributes
32576   *   Object containing all inherited attributes from parent elements with attribute
32577   *   names as keys
32578   * @param {Object[]|undefined} segmentTimeline
32579   *        List of objects representing the attributes of each S element contained within
32580   *        the SegmentTimeline element
32581   * @return {Object.<Array>} list of segments
32582   */
32583
32584
32585  var segmentsFromList = function segmentsFromList(attributes, segmentTimeline) {
32586    var duration = attributes.duration,
32587        _attributes$segmentUr = attributes.segmentUrls,
32588        segmentUrls = _attributes$segmentUr === void 0 ? [] : _attributes$segmentUr; // Per spec (5.3.9.2.1) no way to determine segment duration OR
32589    // if both SegmentTimeline and @duration are defined, it is outside of spec.
32590
32591    if (!duration && !segmentTimeline || duration && segmentTimeline) {
32592      throw new Error(errors.SEGMENT_TIME_UNSPECIFIED);
32593    }
32594
32595    var segmentUrlMap = segmentUrls.map(function (segmentUrlObject) {
32596      return SegmentURLToSegmentObject(attributes, segmentUrlObject);
32597    });
32598    var segmentTimeInfo;
32599
32600    if (duration) {
32601      segmentTimeInfo = parseByDuration(attributes);
32602    }
32603
32604    if (segmentTimeline) {
32605      segmentTimeInfo = parseByTimeline(attributes, segmentTimeline);
32606    }
32607
32608    var segments = segmentTimeInfo.map(function (segmentTime, index) {
32609      if (segmentUrlMap[index]) {
32610        var segment = segmentUrlMap[index];
32611        segment.timeline = segmentTime.timeline;
32612        segment.duration = segmentTime.duration;
32613        segment.number = segmentTime.number;
32614        return segment;
32615      } // Since we're mapping we should get rid of any blank segments (in case
32616      // the given SegmentTimeline is handling for more elements than we have
32617      // SegmentURLs for).
32618
32619    }).filter(function (segment) {
32620      return segment;
32621    });
32622    return segments;
32623  };
32624
32625  var generateSegments = function generateSegments(_ref) {
32626    var attributes = _ref.attributes,
32627        segmentInfo = _ref.segmentInfo;
32628    var segmentAttributes;
32629    var segmentsFn;
32630
32631    if (segmentInfo.template) {
32632      segmentsFn = segmentsFromTemplate;
32633      segmentAttributes = merge(attributes, segmentInfo.template);
32634    } else if (segmentInfo.base) {
32635      segmentsFn = segmentsFromBase;
32636      segmentAttributes = merge(attributes, segmentInfo.base);
32637    } else if (segmentInfo.list) {
32638      segmentsFn = segmentsFromList;
32639      segmentAttributes = merge(attributes, segmentInfo.list);
32640    }
32641
32642    var segmentsInfo = {
32643      attributes: attributes
32644    };
32645
32646    if (!segmentsFn) {
32647      return segmentsInfo;
32648    }
32649
32650    var segments = segmentsFn(segmentAttributes, segmentInfo.timeline); // The @duration attribute will be used to determin the playlist's targetDuration which
32651    // must be in seconds. Since we've generated the segment list, we no longer need
32652    // @duration to be in @timescale units, so we can convert it here.
32653
32654    if (segmentAttributes.duration) {
32655      var _segmentAttributes = segmentAttributes,
32656          duration = _segmentAttributes.duration,
32657          _segmentAttributes$ti = _segmentAttributes.timescale,
32658          timescale = _segmentAttributes$ti === void 0 ? 1 : _segmentAttributes$ti;
32659      segmentAttributes.duration = duration / timescale;
32660    } else if (segments.length) {
32661      // if there is no @duration attribute, use the largest segment duration as
32662      // as target duration
32663      segmentAttributes.duration = segments.reduce(function (max, segment) {
32664        return Math.max(max, Math.ceil(segment.duration));
32665      }, 0);
32666    } else {
32667      segmentAttributes.duration = 0;
32668    }
32669
32670    segmentsInfo.attributes = segmentAttributes;
32671    segmentsInfo.segments = segments; // This is a sidx box without actual segment information
32672
32673    if (segmentInfo.base && segmentAttributes.indexRange) {
32674      segmentsInfo.sidx = segments[0];
32675      segmentsInfo.segments = [];
32676    }
32677
32678    return segmentsInfo;
32679  };
32680
32681  var toPlaylists = function toPlaylists(representations) {
32682    return representations.map(generateSegments);
32683  };
32684
vendor: 13,526 bytes, lines 32685-33081
32685  var findChildren = function findChildren(element, name) {
32686    return from(element.childNodes).filter(function (_ref) {
32687      var tagName = _ref.tagName;
32688      return tagName === name;
32689    });
32690  };
32691
32692  var getContent = function getContent(element) {
32693    return element.textContent.trim();
32694  };
32695
32696  var parseDuration = function parseDuration(str) {
32697    var SECONDS_IN_YEAR = 365 * 24 * 60 * 60;
32698    var SECONDS_IN_MONTH = 30 * 24 * 60 * 60;
32699    var SECONDS_IN_DAY = 24 * 60 * 60;
32700    var SECONDS_IN_HOUR = 60 * 60;
32701    var SECONDS_IN_MIN = 60; // P10Y10M10DT10H10M10.1S
32702
32703    var durationRegex = /P(?:(\d*)Y)?(?:(\d*)M)?(?:(\d*)D)?(?:T(?:(\d*)H)?(?:(\d*)M)?(?:([\d.]*)S)?)?/;
32704    var match = durationRegex.exec(str);
32705
32706    if (!match) {
32707      return 0;
32708    }
32709
32710    var _match$slice = match.slice(1),
32711        year = _match$slice[0],
32712        month = _match$slice[1],
32713        day = _match$slice[2],
32714        hour = _match$slice[3],
32715        minute = _match$slice[4],
32716        second = _match$slice[5];
32717
32718    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);
32719  };
32720
32721  var parseDate = function parseDate(str) {
32722    // Date format without timezone according to ISO 8601
32723    // YYY-MM-DDThh:mm:ss.ssssss
32724    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
32725    // expressed by ending with 'Z'
32726
32727    if (dateRegex.test(str)) {
32728      str += 'Z';
32729    }
32730
32731    return Date.parse(str);
32732  };
32733
32734  var parsers = {
32735    /**
32736     * Specifies the duration of the entire Media Presentation. Format is a duration string
32737     * as specified in ISO 8601
32738     *
32739     * @param {string} value
32740     *        value of attribute as a string
32741     * @return {number}
32742     *         The duration in seconds
32743     */
32744    mediaPresentationDuration: function mediaPresentationDuration(value) {
32745      return parseDuration(value);
32746    },
32747
32748    /**
32749     * Specifies the Segment availability start time for all Segments referred to in this
32750     * MPD. For a dynamic manifest, it specifies the anchor for the earliest availability
32751     * time. Format is a date string as specified in ISO 8601
32752     *
32753     * @param {string} value
32754     *        value of attribute as a string
32755     * @return {number}
32756     *         The date as seconds from unix epoch
32757     */
32758    availabilityStartTime: function availabilityStartTime(value) {
32759      return parseDate(value) / 1000;
32760    },
32761
32762    /**
32763     * Specifies the smallest period between potential changes to the MPD. Format is a
32764     * duration string as specified in ISO 8601
32765     *
32766     * @param {string} value
32767     *        value of attribute as a string
32768     * @return {number}
32769     *         The duration in seconds
32770     */
32771    minimumUpdatePeriod: function minimumUpdatePeriod(value) {
32772      return parseDuration(value);
32773    },
32774
32775    /**
32776     * Specifies the duration of the smallest time shifting buffer for any Representation
32777     * in the MPD. Format is a duration string as specified in ISO 8601
32778     *
32779     * @param {string} value
32780     *        value of attribute as a string
32781     * @return {number}
32782     *         The duration in seconds
32783     */
32784    timeShiftBufferDepth: function timeShiftBufferDepth(value) {
32785      return parseDuration(value);
32786    },
32787
32788    /**
32789     * Specifies the PeriodStart time of the Period relative to the availabilityStarttime.
32790     * Format is a duration string as specified in ISO 8601
32791     *
32792     * @param {string} value
32793     *        value of attribute as a string
32794     * @return {number}
32795     *         The duration in seconds
32796     */
32797    start: function start(value) {
32798      return parseDuration(value);
32799    },
32800
32801    /**
32802     * Specifies the width of the visual presentation
32803     *
32804     * @param {string} value
32805     *        value of attribute as a string
32806     * @return {number}
32807     *         The parsed width
32808     */
32809    width: function width(value) {
32810      return parseInt(value, 10);
32811    },
32812
32813    /**
32814     * Specifies the height of the visual presentation
32815     *
32816     * @param {string} value
32817     *        value of attribute as a string
32818     * @return {number}
32819     *         The parsed height
32820     */
32821    height: function height(value) {
32822      return parseInt(value, 10);
32823    },
32824
32825    /**
32826     * Specifies the bitrate of the representation
32827     *
32828     * @param {string} value
32829     *        value of attribute as a string
32830     * @return {number}
32831     *         The parsed bandwidth
32832     */
32833    bandwidth: function bandwidth(value) {
32834      return parseInt(value, 10);
32835    },
32836
32837    /**
32838     * Specifies the number of the first Media Segment in this Representation in the Period
32839     *
32840     * @param {string} value
32841     *        value of attribute as a string
32842     * @return {number}
32843     *         The parsed number
32844     */
32845    startNumber: function startNumber(value) {
32846      return parseInt(value, 10);
32847    },
32848
32849    /**
32850     * Specifies the timescale in units per seconds
32851     *
32852     * @param {string} value
32853     *        value of attribute as a string
32854     * @return {number}
32855     *         The aprsed timescale
32856     */
32857    timescale: function timescale(value) {
32858      return parseInt(value, 10);
32859    },
32860
32861    /**
32862     * Specifies the constant approximate Segment duration
32863     * NOTE: The <Period> element also contains an @duration attribute. This duration
32864     *       specifies the duration of the Period. This attribute is currently not
32865     *       supported by the rest of the parser, however we still check for it to prevent
32866     *       errors.
32867     *
32868     * @param {string} value
32869     *        value of attribute as a string
32870     * @return {number}
32871     *         The parsed duration
32872     */
32873    duration: function duration(value) {
32874      var parsedValue = parseInt(value, 10);
32875
32876      if (isNaN(parsedValue)) {
32877        return parseDuration(value);
32878      }
32879
32880      return parsedValue;
32881    },
32882
32883    /**
32884     * Specifies the Segment duration, in units of the value of the @timescale.
32885     *
32886     * @param {string} value
32887     *        value of attribute as a string
32888     * @return {number}
32889     *         The parsed duration
32890     */
32891    d: function d(value) {
32892      return parseInt(value, 10);
32893    },
32894
32895    /**
32896     * Specifies the MPD start time, in @timescale units, the first Segment in the series
32897     * starts relative to the beginning of the Period
32898     *
32899     * @param {string} value
32900     *        value of attribute as a string
32901     * @return {number}
32902     *         The parsed time
32903     */
32904    t: function t(value) {
32905      return parseInt(value, 10);
32906    },
32907
32908    /**
32909     * Specifies the repeat count of the number of following contiguous Segments with the
32910     * same duration expressed by the value of @d
32911     *
32912     * @param {string} value
32913     *        value of attribute as a string
32914     * @return {number}
32915     *         The parsed number
32916     */
32917    r: function r(value) {
32918      return parseInt(value, 10);
32919    },
32920
32921    /**
32922     * Default parser for all other attributes. Acts as a no-op and just returns the value
32923     * as a string
32924     *
32925     * @param {string} value
32926     *        value of attribute as a string
32927     * @return {string}
32928     *         Unparsed value
32929     */
32930    DEFAULT: function DEFAULT(value) {
32931      return value;
32932    }
32933  };
32934  /**
32935   * Gets all the attributes and values of the provided node, parses attributes with known
32936   * types, and returns an object with attribute names mapped to values.
32937   *
32938   * @param {Node} el
32939   *        The node to parse attributes from
32940   * @return {Object}
32941   *         Object with all attributes of el parsed
32942   */
32943
32944  var parseAttributes$1 = function parseAttributes(el) {
32945    if (!(el && el.attributes)) {
32946      return {};
32947    }
32948
32949    return from(el.attributes).reduce(function (a, e) {
32950      var parseFn = parsers[e.name] || parsers.DEFAULT;
32951      a[e.name] = parseFn(e.value);
32952      return a;
32953    }, {});
32954  };
32955
32956  function decodeB64ToUint8Array$1(b64Text) {
32957    var decodedString = window$1.atob(b64Text);
32958    var array = new Uint8Array(decodedString.length);
32959
32960    for (var i = 0; i < decodedString.length; i++) {
32961      array[i] = decodedString.charCodeAt(i);
32962    }
32963
32964    return array;
32965  }
32966
32967  var keySystemsMap = {
32968    'urn:uuid:1077efec-c0b2-4d02-ace3-3c1e52e2fb4b': 'org.w3.clearkey',
32969    'urn:uuid:edef8ba9-79d6-4ace-a3c8-27dcd51d21ed': 'com.widevine.alpha',
32970    'urn:uuid:9a04f079-9840-4286-ab92-e65be0885f95': 'com.microsoft.playready',
32971    'urn:uuid:f239e769-efa3-4850-9c16-a903c6932efb': 'com.adobe.primetime'
32972  };
32973  /**
32974   * Builds a list of urls that is the product of the reference urls and BaseURL values
32975   *
32976   * @param {string[]} referenceUrls
32977   *        List of reference urls to resolve to
32978   * @param {Node[]} baseUrlElements
32979   *        List of BaseURL nodes from the mpd
32980   * @return {string[]}
32981   *         List of resolved urls
32982   */
32983
32984  var buildBaseUrls = function buildBaseUrls(referenceUrls, baseUrlElements) {
32985    if (!baseUrlElements.length) {
32986      return referenceUrls;
32987    }
32988
32989    return flatten(referenceUrls.map(function (reference) {
32990      return baseUrlElements.map(function (baseUrlElement) {
32991        return resolveUrl(reference, getContent(baseUrlElement));
32992      });
32993    }));
32994  };
32995  /**
32996   * Contains all Segment information for its containing AdaptationSet
32997   *
32998   * @typedef {Object} SegmentInformation
32999   * @property {Object|undefined} template
33000   *           Contains the attributes for the SegmentTemplate node
33001   * @property {Object[]|undefined} timeline
33002   *           Contains a list of atrributes for each S node within the SegmentTimeline node
33003   * @property {Object|undefined} list
33004   *           Contains the attributes for the SegmentList node
33005   * @property {Object|undefined} base
33006   *           Contains the attributes for the SegmentBase node
33007   */
33008
33009  /**
33010   * Returns all available Segment information contained within the AdaptationSet node
33011   *
33012   * @param {Node} adaptationSet
33013   *        The AdaptationSet node to get Segment information from
33014   * @return {SegmentInformation}
33015   *         The Segment information contained within the provided AdaptationSet
33016   */
33017
33018
33019  var getSegmentInformation = function getSegmentInformation(adaptationSet) {
33020    var segmentTemplate = findChildren(adaptationSet, 'SegmentTemplate')[0];
33021    var segmentList = findChildren(adaptationSet, 'SegmentList')[0];
33022    var segmentUrls = segmentList && findChildren(segmentList, 'SegmentURL').map(function (s) {
33023      return merge({
33024        tag: 'SegmentURL'
33025      }, parseAttributes$1(s));
33026    });
33027    var segmentBase = findChildren(adaptationSet, 'SegmentBase')[0];
33028    var segmentTimelineParentNode = segmentList || segmentTemplate;
33029    var segmentTimeline = segmentTimelineParentNode && findChildren(segmentTimelineParentNode, 'SegmentTimeline')[0];
33030    var segmentInitializationParentNode = segmentList || segmentBase || segmentTemplate;
33031    var segmentInitialization = segmentInitializationParentNode && findChildren(segmentInitializationParentNode, 'Initialization')[0]; // SegmentTemplate is handled slightly differently, since it can have both
33032    // @initialization and an <Initialization> node.  @initialization can be templated,
33033    // while the node can have a url and range specified.  If the <SegmentTemplate> has
33034    // both @initialization and an <Initialization> subelement we opt to override with
33035    // the node, as this interaction is not defined in the spec.
33036
33037    var template = segmentTemplate && parseAttributes$1(segmentTemplate);
33038
33039    if (template && segmentInitialization) {
33040      template.initialization = segmentInitialization && parseAttributes$1(segmentInitialization);
33041    } else if (template && template.initialization) {
33042      // If it is @initialization we convert it to an object since this is the format that
33043      // later functions will rely on for the initialization segment.  This is only valid
33044      // for <SegmentTemplate>
33045      template.initialization = {
33046        sourceURL: template.initialization
33047      };
33048    }
33049
33050    var segmentInfo = {
33051      template: template,
33052      timeline: segmentTimeline && findChildren(segmentTimeline, 'S').map(function (s) {
33053        return parseAttributes$1(s);
33054      }),
33055      list: segmentList && merge(parseAttributes$1(segmentList), {
33056        segmentUrls: segmentUrls,
33057        initialization: parseAttributes$1(segmentInitialization)
33058      }),
33059      base: segmentBase && merge(parseAttributes$1(segmentBase), {
33060        initialization: parseAttributes$1(segmentInitialization)
33061      })
33062    };
33063    Object.keys(segmentInfo).forEach(function (key) {
33064      if (!segmentInfo[key]) {
33065        delete segmentInfo[key];
33066      }
33067    });
33068    return segmentInfo;
33069  };
33070  /**
33071   * Contains Segment information and attributes needed to construct a Playlist object
33072   * from a Representation
33073   *
33074   * @typedef {Object} RepresentationInformation
33075   * @property {SegmentInformation} segmentInfo
33076   *           Segment information for this Representation
33077   * @property {Object} attributes
33078   *           Inherited attributes for this Representation
33079   */
33080
33081  /**
33082   * Maps a Representation node to an object containing Segment information and attributes
33083   *
33084   * @name inheritBaseUrlsCallback
33085   * @function
33086   * @param {Node} representation
33087   *        Representation node from the mpd
33088   * @return {RepresentationInformation}
33089   *         Representation information needed to construct a Playlist object
33090   */
33091
33092  /**
33093   * Returns a callback for Array.prototype.map for mapping Representation nodes to
33094   * Segment information and attributes using inherited BaseURL nodes.
33095   *
33096   * @param {Object} adaptationSetAttributes
33097   *        Contains attributes inherited by the AdaptationSet
33098   * @param {string[]} adaptationSetBaseUrls
33099   *        Contains list of resolved base urls inherited by the AdaptationSet
33100   * @param {SegmentInformation} adaptationSetSegmentInfo
33101   *        Contains Segment information for the AdaptationSet
33102   * @return {inheritBaseUrlsCallback}
33103   *         Callback map function
33104   */
33105
33106
33107  var inheritBaseUrls = function inheritBaseUrls(adaptationSetAttributes, adaptationSetBaseUrls, adaptationSetSegmentInfo) {
33108    return function (representation) {
33109      var repBaseUrlElements = findChildren(representation, 'BaseURL');
33110      var repBaseUrls = buildBaseUrls(adaptationSetBaseUrls, repBaseUrlElements);
33111      var attributes = merge(adaptationSetAttributes, parseAttributes$1(representation));
33112      var representationSegmentInfo = getSegmentInformation(representation);
33113      return repBaseUrls.map(function (baseUrl) {
33114        return {
33115          segmentInfo: merge(adaptationSetSegmentInfo, representationSegmentInfo),
33116          attributes: merge(attributes, {
33117            baseUrl: baseUrl
33118          })
33119        };
33120      });
33121    };
33122  };
33123  /**
33124   * Tranforms a series of content protection nodes to
33125   * an object containing pssh data by key system
33126   *
33127   * @param {Node[]} contentProtectionNodes
33128   *        Content protection nodes
33129   * @return {Object}
33130   *        Object containing pssh data by key system
33131   */
33132
33133
33134  var generateKeySystemInformation = function generateKeySystemInformation(contentProtectionNodes) {
33135    return contentProtectionNodes.reduce(function (acc, node) {
33136      var attributes = parseAttributes$1(node);
33137      var keySystem = keySystemsMap[attributes.schemeIdUri];
33138
33139      if (keySystem) {
33140        acc[keySystem] = {
33141          attributes: attributes
33142        };
33143        var psshNode = findChildren(node, 'cenc:pssh')[0];
33144
33145        if (psshNode) {
33146          var pssh = getContent(psshNode);
33147          var psshBuffer = pssh && decodeB64ToUint8Array$1(pssh);
33148          acc[keySystem].pssh = psshBuffer;
33149        }
33150      }
33151
33152      return acc;
33153    }, {});
33154  };
33155  /**
33156   * Maps an AdaptationSet node to a list of Representation information objects
33157   *
33158   * @name toRepresentationsCallback
33159   * @function
33160   * @param {Node} adaptationSet
33161   *        AdaptationSet node from the mpd
33162   * @return {RepresentationInformation[]}
33163   *         List of objects containing Representaion information
33164   */
33165
33166  /**
33167   * Returns a callback for Array.prototype.map for mapping AdaptationSet nodes to a list of
33168   * Representation information objects
33169   *
33170   * @param {Object} periodAttributes
33171   *        Contains attributes inherited by the Period
33172   * @param {string[]} periodBaseUrls
33173   *        Contains list of resolved base urls inherited by the Period
33174   * @param {string[]} periodSegmentInfo
33175   *        Contains Segment Information at the period level
33176   * @return {toRepresentationsCallback}
33177   *         Callback map function
33178   */
33179
33180
33181  var toRepresentations = function toRepresentations(periodAttributes, periodBaseUrls, periodSegmentInfo) {
33182    return function (adaptationSet) {
33183      var adaptationSetAttributes = parseAttributes$1(adaptationSet);
33184      var adaptationSetBaseUrls = buildBaseUrls(periodBaseUrls, findChildren(adaptationSet, 'BaseURL'));
33185      var role = findChildren(adaptationSet, 'Role')[0];
33186      var roleAttributes = {
33187        role: parseAttributes$1(role)
33188      };
33189      var attrs = merge(periodAttributes, adaptationSetAttributes, roleAttributes);
33190      var contentProtection = generateKeySystemInformation(findChildren(adaptationSet, 'ContentProtection'));
33191
33192      if (Object.keys(contentProtection).length) {
33193        attrs = merge(attrs, {
33194          contentProtection: contentProtection
33195        });
33196      }
33197
33198      var segmentInfo = getSegmentInformation(adaptationSet);
33199      var representations = findChildren(adaptationSet, 'Representation');
33200      var adaptationSetSegmentInfo = merge(periodSegmentInfo, segmentInfo);
33201      return flatten(representations.map(inheritBaseUrls(attrs, adaptationSetBaseUrls, adaptationSetSegmentInfo)));
33202    };
33203  };
33204  /**
33205   * Maps an Period node to a list of Representation inforamtion objects for all
33206   * AdaptationSet nodes contained within the Period
33207   *
33208   * @name toAdaptationSetsCallback
33209   * @function
33210   * @param {Node} period
33211   *        Period node from the mpd
33212   * @param {number} periodIndex
33213   *        Index of the Period within the mpd
33214   * @return {RepresentationInformation[]}
33215   *         List of objects containing Representaion information
33216   */
33217
33218  /**
33219   * Returns a callback for Array.prototype.map for mapping Period nodes to a list of
33220   * Representation information objects
33221   *
33222   * @param {Object} mpdAttributes
33223   *        Contains attributes inherited by the mpd
33224   * @param {string[]} mpdBaseUrls
33225   *        Contains list of resolved base urls inherited by the mpd
33226   * @return {toAdaptationSetsCallback}
33227   *         Callback map function
33228   */
33229
33230
33231  var toAdaptationSets = function toAdaptationSets(mpdAttributes, mpdBaseUrls) {
33232    return function (period, index) {
33233      var periodBaseUrls = buildBaseUrls(mpdBaseUrls, findChildren(period, 'BaseURL'));
33234      var periodAtt = parseAttributes$1(period);
33235      var parsedPeriodId = parseInt(periodAtt.id, 10); // fallback to mapping index if Period@id is not a number
33236
33237      var periodIndex = window$1.isNaN(parsedPeriodId) ? index : parsedPeriodId;
33238      var periodAttributes = merge(mpdAttributes, {
33239        periodIndex: periodIndex
33240      });
33241      var adaptationSets = findChildren(period, 'AdaptationSet');
33242      var periodSegmentInfo = getSegmentInformation(period);
33243      return flatten(adaptationSets.map(toRepresentations(periodAttributes, periodBaseUrls, periodSegmentInfo)));
33244    };
33245  };
33246  /**
33247   * Traverses the mpd xml tree to generate a list of Representation information objects
33248   * that have inherited attributes from parent nodes
33249   *
33250   * @param {Node} mpd
33251   *        The root node of the mpd
33252   * @param {Object} options
33253   *        Available options for inheritAttributes
33254   * @param {string} options.manifestUri
33255   *        The uri source of the mpd
33256   * @param {number} options.NOW
vendor: 6,393 bytes, lines 33257-33456
33257   *        Current time per DASH IOP.  Default is current time in ms since epoch
33258   * @param {number} options.clientOffset
33259   *        Client time difference from NOW (in milliseconds)
33260   * @return {RepresentationInformation[]}
33261   *         List of objects containing Representation information
33262   */
33263
33264
33265  var inheritAttributes = function inheritAttributes(mpd, options) {
33266    if (options === void 0) {
33267      options = {};
33268    }
33269
33270    var _options = options,
33271        _options$manifestUri = _options.manifestUri,
33272        manifestUri = _options$manifestUri === void 0 ? '' : _options$manifestUri,
33273        _options$NOW = _options.NOW,
33274        NOW = _options$NOW === void 0 ? Date.now() : _options$NOW,
33275        _options$clientOffset = _options.clientOffset,
33276        clientOffset = _options$clientOffset === void 0 ? 0 : _options$clientOffset;
33277    var periods = findChildren(mpd, 'Period');
33278
33279    if (!periods.length) {
33280      throw new Error(errors.INVALID_NUMBER_OF_PERIOD);
33281    }
33282
33283    var mpdAttributes = parseAttributes$1(mpd);
33284    var mpdBaseUrls = buildBaseUrls([manifestUri], findChildren(mpd, 'BaseURL'));
33285    mpdAttributes.sourceDuration = mpdAttributes.mediaPresentationDuration || 0;
33286    mpdAttributes.NOW = NOW;
33287    mpdAttributes.clientOffset = clientOffset;
33288    return flatten(periods.map(toAdaptationSets(mpdAttributes, mpdBaseUrls)));
33289  };
33290
33291  var stringToMpdXml = function stringToMpdXml(manifestString) {
33292    if (manifestString === '') {
33293      throw new Error(errors.DASH_EMPTY_MANIFEST);
33294    }
33295
33296    var parser = new window$1.DOMParser();
33297    var xml = parser.parseFromString(manifestString, 'application/xml');
33298    var mpd = xml && xml.documentElement.tagName === 'MPD' ? xml.documentElement : null;
33299
33300    if (!mpd || mpd && mpd.getElementsByTagName('parsererror').length > 0) {
33301      throw new Error(errors.DASH_INVALID_XML);
33302    }
33303
33304    return mpd;
33305  };
33306  /**
33307   * Parses the manifest for a UTCTiming node, returning the nodes attributes if found
33308   *
33309   * @param {string} mpd
33310   *        XML string of the MPD manifest
33311   * @return {Object|null}
33312   *         Attributes of UTCTiming node specified in the manifest. Null if none found
33313   */
33314
33315
33316  var parseUTCTimingScheme = function parseUTCTimingScheme(mpd) {
33317    var UTCTimingNode = findChildren(mpd, 'UTCTiming')[0];
33318
33319    if (!UTCTimingNode) {
33320      return null;
33321    }
33322
33323    var attributes = parseAttributes$1(UTCTimingNode);
33324
33325    switch (attributes.schemeIdUri) {
33326      case 'urn:mpeg:dash:utc:http-head:2014':
33327      case 'urn:mpeg:dash:utc:http-head:2012':
33328        attributes.method = 'HEAD';
33329        break;
33330
33331      case 'urn:mpeg:dash:utc:http-xsdate:2014':
33332      case 'urn:mpeg:dash:utc:http-iso:2014':
33333      case 'urn:mpeg:dash:utc:http-xsdate:2012':
33334      case 'urn:mpeg:dash:utc:http-iso:2012':
33335        attributes.method = 'GET';
33336        break;
33337
33338      case 'urn:mpeg:dash:utc:direct:2014':
33339      case 'urn:mpeg:dash:utc:direct:2012':
33340        attributes.method = 'DIRECT';
33341        attributes.value = Date.parse(attributes.value);
33342        break;
33343
33344      case 'urn:mpeg:dash:utc:http-ntp:2014':
33345      case 'urn:mpeg:dash:utc:ntp:2014':
33346      case 'urn:mpeg:dash:utc:sntp:2014':
33347      default:
33348        throw new Error(errors.UNSUPPORTED_UTC_TIMING_SCHEME);
33349    }
33350
33351    return attributes;
33352  };
33353
33354  var parse = function parse(manifestString, options) {
33355    if (options === void 0) {
33356      options = {};
33357    }
33358
33359    return toM3u8(toPlaylists(inheritAttributes(stringToMpdXml(manifestString), options)), options.sidxMapping);
33360  };
33361  /**
33362   * Parses the manifest for a UTCTiming node, returning the nodes attributes if found
33363   *
33364   * @param {string} manifestString
33365   *        XML string of the MPD manifest
33366   * @return {Object|null}
33367   *         Attributes of UTCTiming node specified in the manifest. Null if none found
33368   */
33369
33370
33371  var parseUTCTiming = function parseUTCTiming(manifestString) {
33372    return parseUTCTimingScheme(stringToMpdXml(manifestString));
33373  };
33374
33375  /**
33376   * mux.js
33377   *
33378   * Copyright (c) Brightcove
33379   * Licensed Apache-2.0 https://github.com/videojs/mux.js/blob/master/LICENSE
33380   */
33381  var toUnsigned = function toUnsigned(value) {
33382    return value >>> 0;
33383  };
33384
33385  var toHexString = function toHexString(value) {
33386    return ('00' + value.toString(16)).slice(-2);
33387  };
33388
33389  var bin = {
33390    toUnsigned: toUnsigned,
33391    toHexString: toHexString
33392  };
33393
33394  var toUnsigned$1 = bin.toUnsigned;
33395  var toHexString$1 = bin.toHexString;
33396
33397  var _findBox, parseType, timescale, startTime, getVideoTrackIds, getTracks; // Find the data for a box specified by its path
33398
33399
33400  _findBox = function findBox(data, path) {
33401    var results = [],
33402        i,
33403        size,
33404        type,
33405        end,
33406        subresults;
33407
33408    if (!path.length) {
33409      // short-circuit the search for empty paths
33410      return null;
33411    }
33412
33413    for (i = 0; i < data.byteLength;) {
33414      size = toUnsigned$1(data[i] << 24 | data[i + 1] << 16 | data[i + 2] << 8 | data[i + 3]);
33415      type = parseType(data.subarray(i + 4, i + 8));
33416      end = size > 1 ? i + size : data.byteLength;
33417
33418      if (type === path[0]) {
33419        if (path.length === 1) {
33420          // this is the end of the path and we've found the box we were
33421          // looking for
33422          results.push(data.subarray(i + 8, end));
33423        } else {
33424          // recursively search for the next box along the path
33425          subresults = _findBox(data.subarray(i + 8, end), path.slice(1));
33426
33427          if (subresults.length) {
33428            results = results.concat(subresults);
33429          }
33430        }
33431      }
33432
33433      i = end;
33434    } // we've finished searching all of data
33435
33436
33437    return results;
33438  };
33439  /**
33440   * Returns the string representation of an ASCII encoded four byte buffer.
33441   * @param buffer {Uint8Array} a four-byte buffer to translate
33442   * @return {string} the corresponding string
33443   */
33444
33445
33446  parseType = function parseType(buffer) {
33447    var result = '';
33448    result += String.fromCharCode(buffer[0]);
33449    result += String.fromCharCode(buffer[1]);
33450    result += String.fromCharCode(buffer[2]);
33451    result += String.fromCharCode(buffer[3]);
33452    return result;
33453  };
33454  /**
33455   * Parses an MP4 initialization segment and extracts the timescale
33456   * values for any declared tracks. Timescale values indicate the
vendor: 4,143 bytes, lines 33457-33575
33457   * number of clock ticks per second to assume for time-based values
33458   * elsewhere in the MP4.
33459   *
33460   * To determine the start time of an MP4, you need two pieces of
33461   * information: the timescale unit and the earliest base media decode
33462   * time. Multiple timescales can be specified within an MP4 but the
33463   * base media decode time is always expressed in the timescale from
33464   * the media header box for the track:
33465   * ```
33466   * moov > trak > mdia > mdhd.timescale
33467   * ```
33468   * @param init {Uint8Array} the bytes of the init segment
33469   * @return {object} a hash of track ids to timescale values or null if
33470   * the init segment is malformed.
33471   */
33472
33473
33474  timescale = function timescale(init) {
33475    var result = {},
33476        traks = _findBox(init, ['moov', 'trak']); // mdhd timescale
33477
33478
33479    return traks.reduce(function (result, trak) {
33480      var tkhd, version, index, id, mdhd;
33481      tkhd = _findBox(trak, ['tkhd'])[0];
33482
33483      if (!tkhd) {
33484        return null;
33485      }
33486
33487      version = tkhd[0];
33488      index = version === 0 ? 12 : 20;
33489      id = toUnsigned$1(tkhd[index] << 24 | tkhd[index + 1] << 16 | tkhd[index + 2] << 8 | tkhd[index + 3]);
33490      mdhd = _findBox(trak, ['mdia', 'mdhd'])[0];
33491
33492      if (!mdhd) {
33493        return null;
33494      }
33495
33496      version = mdhd[0];
33497      index = version === 0 ? 12 : 20;
33498      result[id] = toUnsigned$1(mdhd[index] << 24 | mdhd[index + 1] << 16 | mdhd[index + 2] << 8 | mdhd[index + 3]);
33499      return result;
33500    }, result);
33501  };
33502  /**
33503   * Determine the base media decode start time, in seconds, for an MP4
33504   * fragment. If multiple fragments are specified, the earliest time is
33505   * returned.
33506   *
33507   * The base media decode time can be parsed from track fragment
33508   * metadata:
33509   * ```
33510   * moof > traf > tfdt.baseMediaDecodeTime
33511   * ```
33512   * It requires the timescale value from the mdhd to interpret.
33513   *
33514   * @param timescale {object} a hash of track ids to timescale values.
33515   * @return {number} the earliest base media decode start time for the
33516   * fragment, in seconds
33517   */
33518
33519
33520  startTime = function startTime(timescale, fragment) {
33521    var trafs, baseTimes, result; // we need info from two childrend of each track fragment box
33522
33523    trafs = _findBox(fragment, ['moof', 'traf']); // determine the start times for each track
33524
33525    baseTimes = [].concat.apply([], trafs.map(function (traf) {
33526      return _findBox(traf, ['tfhd']).map(function (tfhd) {
33527        var id, scale, baseTime; // get the track id from the tfhd
33528
33529        id = toUnsigned$1(tfhd[4] << 24 | tfhd[5] << 16 | tfhd[6] << 8 | tfhd[7]); // assume a 90kHz clock if no timescale was specified
33530
33531        scale = timescale[id] || 90e3; // get the base media decode time from the tfdt
33532
33533        baseTime = _findBox(traf, ['tfdt']).map(function (tfdt) {
33534          var version, result;
33535          version = tfdt[0];
33536          result = toUnsigned$1(tfdt[4] << 24 | tfdt[5] << 16 | tfdt[6] << 8 | tfdt[7]);
33537
33538          if (version === 1) {
33539            result *= Math.pow(2, 32);
33540            result += toUnsigned$1(tfdt[8] << 24 | tfdt[9] << 16 | tfdt[10] << 8 | tfdt[11]);
33541          }
33542
33543          return result;
33544        })[0];
33545        baseTime = baseTime || Infinity; // convert base time to seconds
33546
33547        return baseTime / scale;
33548      });
33549    })); // return the minimum
33550
33551    result = Math.min.apply(null, baseTimes);
33552    return isFinite(result) ? result : 0;
33553  };
33554  /**
33555    * Find the trackIds of the video tracks in this source.
33556    * Found by parsing the Handler Reference and Track Header Boxes:
33557    *   moov > trak > mdia > hdlr
33558    *   moov > trak > tkhd
33559    *
33560    * @param {Uint8Array} init - The bytes of the init segment for this source
33561    * @return {Number[]} A list of trackIds
33562    *
33563    * @see ISO-BMFF-12/2015, Section 8.4.3
33564   **/
33565
33566
33567  getVideoTrackIds = function getVideoTrackIds(init) {
33568    var traks = _findBox(init, ['moov', 'trak']);
33569
33570    var videoTrackIds = [];
33571    traks.forEach(function (trak) {
33572      var hdlrs = _findBox(trak, ['mdia', 'hdlr']);
33573
33574      var tkhds = _findBox(trak, ['tkhd']);
33575
vendor: 12,860 bytes, lines 33576-33972
33576      hdlrs.forEach(function (hdlr, index) {
33577        var handlerType = parseType(hdlr.subarray(8, 12));
33578        var tkhd = tkhds[index];
33579        var view;
33580        var version;
33581        var trackId;
33582
33583        if (handlerType === 'vide') {
33584          view = new DataView(tkhd.buffer, tkhd.byteOffset, tkhd.byteLength);
33585          version = view.getUint8(0);
33586          trackId = version === 0 ? view.getUint32(12) : view.getUint32(20);
33587          videoTrackIds.push(trackId);
33588        }
33589      });
33590    });
33591    return videoTrackIds;
33592  };
33593  /**
33594   * Get all the video, audio, and hint tracks from a non fragmented
33595   * mp4 segment
33596   */
33597
33598
33599  getTracks = function getTracks(init) {
33600    var traks = _findBox(init, ['moov', 'trak']);
33601
33602    var tracks = [];
33603    traks.forEach(function (trak) {
33604      var track = {};
33605
33606      var tkhd = _findBox(trak, ['tkhd'])[0];
33607
33608      var view, version; // id
33609
33610      if (tkhd) {
33611        view = new DataView(tkhd.buffer, tkhd.byteOffset, tkhd.byteLength);
33612        version = view.getUint8(0);
33613        track.id = version === 0 ? view.getUint32(12) : view.getUint32(20);
33614      }
33615
33616      var hdlr = _findBox(trak, ['mdia', 'hdlr'])[0]; // type
33617
33618
33619      if (hdlr) {
33620        var type = parseType(hdlr.subarray(8, 12));
33621
33622        if (type === 'vide') {
33623          track.type = 'video';
33624        } else if (type === 'soun') {
33625          track.type = 'audio';
33626        } else {
33627          track.type = type;
33628        }
33629      } // codec
33630
33631
33632      var stsd = _findBox(trak, ['mdia', 'minf', 'stbl', 'stsd'])[0];
33633
33634      if (stsd) {
33635        var sampleDescriptions = stsd.subarray(8); // gives the codec type string
33636
33637        track.codec = parseType(sampleDescriptions.subarray(4, 8));
33638
33639        var codecBox = _findBox(sampleDescriptions, [track.codec])[0];
33640
33641        var codecConfig, codecConfigType;
33642
33643        if (codecBox) {
33644          // https://tools.ietf.org/html/rfc6381#section-3.3
33645          if (/^[a-z]vc[1-9]$/i.test(track.codec)) {
33646            // we don't need anything but the "config" parameter of the
33647            // avc1 codecBox
33648            codecConfig = codecBox.subarray(78);
33649            codecConfigType = parseType(codecConfig.subarray(4, 8));
33650
33651            if (codecConfigType === 'avcC' && codecConfig.length > 11) {
33652              track.codec += '.'; // left padded with zeroes for single digit hex
33653              // profile idc
33654
33655              track.codec += toHexString$1(codecConfig[9]); // the byte containing the constraint_set flags
33656
33657              track.codec += toHexString$1(codecConfig[10]); // level idc
33658
33659              track.codec += toHexString$1(codecConfig[11]);
33660            } else {
33661              // TODO: show a warning that we couldn't parse the codec
33662              // and are using the default
33663              track.codec = 'avc1.4d400d';
33664            }
33665          } else if (/^mp4[a,v]$/i.test(track.codec)) {
33666            // we do not need anything but the streamDescriptor of the mp4a codecBox
33667            codecConfig = codecBox.subarray(28);
33668            codecConfigType = parseType(codecConfig.subarray(4, 8));
33669
33670            if (codecConfigType === 'esds' && codecConfig.length > 20 && codecConfig[19] !== 0) {
33671              track.codec += '.' + toHexString$1(codecConfig[19]); // this value is only a single digit
33672
33673              track.codec += '.' + toHexString$1(codecConfig[20] >>> 2 & 0x3f).replace(/^0/, '');
33674            } else {
33675              // TODO: show a warning that we couldn't parse the codec
33676              // and are using the default
33677              track.codec = 'mp4a.40.2';
33678            }
33679          }
33680        }
33681      }
33682
33683      var mdhd = _findBox(trak, ['mdia', 'mdhd'])[0];
33684
33685      if (mdhd && tkhd) {
33686        var index = version === 0 ? 12 : 20;
33687        track.timescale = toUnsigned$1(mdhd[index] << 24 | mdhd[index + 1] << 16 | mdhd[index + 2] << 8 | mdhd[index + 3]);
33688      }
33689
33690      tracks.push(track);
33691    });
33692    return tracks;
33693  };
33694
33695  var probe = {
33696    findBox: _findBox,
33697    parseType: parseType,
33698    timescale: timescale,
33699    startTime: startTime,
33700    videoTrackIds: getVideoTrackIds,
33701    tracks: getTracks
33702  };
33703
33704  var inspectMp4,
33705      _textifyMp,
33706      parseType$1 = probe.parseType,
33707      parseMp4Date = function parseMp4Date(seconds) {
33708    return new Date(seconds * 1000 - 2082844800000);
33709  },
33710      parseSampleFlags = function parseSampleFlags(flags) {
33711    return {
33712      isLeading: (flags[0] & 0x0c) >>> 2,
33713      dependsOn: flags[0] & 0x03,
33714      isDependedOn: (flags[1] & 0xc0) >>> 6,
33715      hasRedundancy: (flags[1] & 0x30) >>> 4,
33716      paddingValue: (flags[1] & 0x0e) >>> 1,
33717      isNonSyncSample: flags[1] & 0x01,
33718      degradationPriority: flags[2] << 8 | flags[3]
33719    };
33720  },
33721      nalParse = function nalParse(avcStream) {
33722    var avcView = new DataView(avcStream.buffer, avcStream.byteOffset, avcStream.byteLength),
33723        result = [],
33724        i,
33725        length;
33726
33727    for (i = 0; i + 4 < avcStream.length; i += length) {
33728      length = avcView.getUint32(i);
33729      i += 4; // bail if this doesn't appear to be an H264 stream
33730
33731      if (length <= 0) {
33732        result.push('<span style=\'color:red;\'>MALFORMED DATA</span>');
33733        continue;
33734      }
33735
33736      switch (avcStream[i] & 0x1F) {
33737        case 0x01:
33738          result.push('slice_layer_without_partitioning_rbsp');
33739          break;
33740
33741        case 0x05:
33742          result.push('slice_layer_without_partitioning_rbsp_idr');
33743          break;
33744
33745        case 0x06:
33746          result.push('sei_rbsp');
33747          break;
33748
33749        case 0x07:
33750          result.push('seq_parameter_set_rbsp');
33751          break;
33752
33753        case 0x08:
33754          result.push('pic_parameter_set_rbsp');
33755          break;
33756
33757        case 0x09:
33758          result.push('access_unit_delimiter_rbsp');
33759          break;
33760
33761        default:
33762          result.push('UNKNOWN NAL - ' + avcStream[i] & 0x1F);
33763          break;
33764      }
33765    }
33766
33767    return result;
33768  },
33769      // registry of handlers for individual mp4 box types
33770  parse$1 = {
33771    // codingname, not a first-class box type. stsd entries share the
33772    // same format as real boxes so the parsing infrastructure can be
33773    // shared
33774    avc1: function avc1(data) {
33775      var view = new DataView(data.buffer, data.byteOffset, data.byteLength);
33776      return {
33777        dataReferenceIndex: view.getUint16(6),
33778        width: view.getUint16(24),
33779        height: view.getUint16(26),
33780        horizresolution: view.getUint16(28) + view.getUint16(30) / 16,
33781        vertresolution: view.getUint16(32) + view.getUint16(34) / 16,
33782        frameCount: view.getUint16(40),
33783        depth: view.getUint16(74),
33784        config: inspectMp4(data.subarray(78, data.byteLength))
33785      };
33786    },
33787    avcC: function avcC(data) {
33788      var view = new DataView(data.buffer, data.byteOffset, data.byteLength),
33789          result = {
33790        configurationVersion: data[0],
33791        avcProfileIndication: data[1],
33792        profileCompatibility: data[2],
33793        avcLevelIndication: data[3],
33794        lengthSizeMinusOne: data[4] & 0x03,
33795        sps: [],
33796        pps: []
33797      },
33798          numOfSequenceParameterSets = data[5] & 0x1f,
33799          numOfPictureParameterSets,
33800          nalSize,
33801          offset,
33802          i; // iterate past any SPSs
33803
33804      offset = 6;
33805
33806      for (i = 0; i < numOfSequenceParameterSets; i++) {
33807        nalSize = view.getUint16(offset);
33808        offset += 2;
33809        result.sps.push(new Uint8Array(data.subarray(offset, offset + nalSize)));
33810        offset += nalSize;
33811      } // iterate past any PPSs
33812
33813
33814      numOfPictureParameterSets = data[offset];
33815      offset++;
33816
33817      for (i = 0; i < numOfPictureParameterSets; i++) {
33818        nalSize = view.getUint16(offset);
33819        offset += 2;
33820        result.pps.push(new Uint8Array(data.subarray(offset, offset + nalSize)));
33821        offset += nalSize;
33822      }
33823
33824      return result;
33825    },
33826    btrt: function btrt(data) {
33827      var view = new DataView(data.buffer, data.byteOffset, data.byteLength);
33828      return {
33829        bufferSizeDB: view.getUint32(0),
33830        maxBitrate: view.getUint32(4),
33831        avgBitrate: view.getUint32(8)
33832      };
33833    },
33834    esds: function esds(data) {
33835      return {
33836        version: data[0],
33837        flags: new Uint8Array(data.subarray(1, 4)),
33838        esId: data[6] << 8 | data[7],
33839        streamPriority: data[8] & 0x1f,
33840        decoderConfig: {
33841          objectProfileIndication: data[11],
33842          streamType: data[12] >>> 2 & 0x3f,
33843          bufferSize: data[13] << 16 | data[14] << 8 | data[15],
33844          maxBitrate: data[16] << 24 | data[17] << 16 | data[18] << 8 | data[19],
33845          avgBitrate: data[20] << 24 | data[21] << 16 | data[22] << 8 | data[23],
33846          decoderConfigDescriptor: {
33847            tag: data[24],
33848            length: data[25],
33849            audioObjectType: data[26] >>> 3 & 0x1f,
33850            samplingFrequencyIndex: (data[26] & 0x07) << 1 | data[27] >>> 7 & 0x01,
33851            channelConfiguration: data[27] >>> 3 & 0x0f
33852          }
33853        }
33854      };
33855    },
33856    ftyp: function ftyp(data) {
33857      var view = new DataView(data.buffer, data.byteOffset, data.byteLength),
33858          result = {
33859        majorBrand: parseType$1(data.subarray(0, 4)),
33860        minorVersion: view.getUint32(4),
33861        compatibleBrands: []
33862      },
33863          i = 8;
33864
33865      while (i < data.byteLength) {
33866        result.compatibleBrands.push(parseType$1(data.subarray(i, i + 4)));
33867        i += 4;
33868      }
33869
33870      return result;
33871    },
33872    dinf: function dinf(data) {
33873      return {
33874        boxes: inspectMp4(data)
33875      };
33876    },
33877    dref: function dref(data) {
33878      return {
33879        version: data[0],
33880        flags: new Uint8Array(data.subarray(1, 4)),
33881        dataReferences: inspectMp4(data.subarray(8))
33882      };
33883    },
33884    hdlr: function hdlr(data) {
33885      var view = new DataView(data.buffer, data.byteOffset, data.byteLength),
33886          result = {
33887        version: view.getUint8(0),
33888        flags: new Uint8Array(data.subarray(1, 4)),
33889        handlerType: parseType$1(data.subarray(8, 12)),
33890        name: ''
33891      },
33892          i = 8; // parse out the name field
33893
33894      for (i = 24; i < data.byteLength; i++) {
33895        if (data[i] === 0x00) {
33896          // the name field is null-terminated
33897          i++;
33898          break;
33899        }
33900
33901        result.name += String.fromCharCode(data[i]);
33902      } // decode UTF-8 to javascript's internal representation
33903      // see http://ecmanaut.blogspot.com/2006/07/encoding-decoding-utf8-in-javascript.html
33904
33905
33906      result.name = decodeURIComponent(escape(result.name));
33907      return result;
33908    },
33909    mdat: function mdat(data) {
33910      return {
33911        byteLength: data.byteLength,
33912        nals: nalParse(data)
33913      };
33914    },
33915    mdhd: function mdhd(data) {
33916      var view = new DataView(data.buffer, data.byteOffset, data.byteLength),
33917          i = 4,
33918          language,
33919          result = {
33920        version: view.getUint8(0),
33921        flags: new Uint8Array(data.subarray(1, 4)),
33922        language: ''
33923      };
33924
33925      if (result.version === 1) {
33926        i += 4;
33927        result.creationTime = parseMp4Date(view.getUint32(i)); // truncating top 4 bytes
33928
33929        i += 8;
33930        result.modificationTime = parseMp4Date(view.getUint32(i)); // truncating top 4 bytes
33931
33932        i += 4;
33933        result.timescale = view.getUint32(i);
33934        i += 8;
33935        result.duration = view.getUint32(i); // truncating top 4 bytes
33936      } else {
33937        result.creationTime = parseMp4Date(view.getUint32(i));
33938        i += 4;
33939        result.modificationTime = parseMp4Date(view.getUint32(i));
33940        i += 4;
33941        result.timescale = view.getUint32(i);
33942        i += 4;
33943        result.duration = view.getUint32(i);
33944      }
33945
33946      i += 4; // language is stored as an ISO-639-2/T code in an array of three 5-bit fields
33947      // each field is the packed difference between its ASCII value and 0x60
33948
33949      language = view.getUint16(i);
33950      result.language += String.fromCharCode((language >> 10) + 0x60);
33951      result.language += String.fromCharCode(((language & 0x03e0) >> 5) + 0x60);
33952      result.language += String.fromCharCode((language & 0x1f) + 0x60);
33953      return result;
33954    },
33955    mdia: function mdia(data) {
33956      return {
33957        boxes: inspectMp4(data)
33958      };
33959    },
33960    mfhd: function mfhd(data) {
33961      return {
33962        version: data[0],
33963        flags: new Uint8Array(data.subarray(1, 4)),
33964        sequenceNumber: data[4] << 24 | data[5] << 16 | data[6] << 8 | data[7]
33965      };
33966    },
33967    minf: function minf(data) {
33968      return {
33969        boxes: inspectMp4(data)
33970      };
33971    },
33972    // codingname, not a first-class box type. stsd entries share the
vendor: 31,486 bytes, lines 33973-34974
33973    // same format as real boxes so the parsing infrastructure can be
33974    // shared
33975    mp4a: function mp4a(data) {
33976      var view = new DataView(data.buffer, data.byteOffset, data.byteLength),
33977          result = {
33978        // 6 bytes reserved
33979        dataReferenceIndex: view.getUint16(6),
33980        // 4 + 4 bytes reserved
33981        channelcount: view.getUint16(16),
33982        samplesize: view.getUint16(18),
33983        // 2 bytes pre_defined
33984        // 2 bytes reserved
33985        samplerate: view.getUint16(24) + view.getUint16(26) / 65536
33986      }; // if there are more bytes to process, assume this is an ISO/IEC
33987      // 14496-14 MP4AudioSampleEntry and parse the ESDBox
33988
33989      if (data.byteLength > 28) {
33990        result.streamDescriptor = inspectMp4(data.subarray(28))[0];
33991      }
33992
33993      return result;
33994    },
33995    moof: function moof(data) {
33996      return {
33997        boxes: inspectMp4(data)
33998      };
33999    },
34000    moov: function moov(data) {
34001      return {
34002        boxes: inspectMp4(data)
34003      };
34004    },
34005    mvex: function mvex(data) {
34006      return {
34007        boxes: inspectMp4(data)
34008      };
34009    },
34010    mvhd: function mvhd(data) {
34011      var view = new DataView(data.buffer, data.byteOffset, data.byteLength),
34012          i = 4,
34013          result = {
34014        version: view.getUint8(0),
34015        flags: new Uint8Array(data.subarray(1, 4))
34016      };
34017
34018      if (result.version === 1) {
34019        i += 4;
34020        result.creationTime = parseMp4Date(view.getUint32(i)); // truncating top 4 bytes
34021
34022        i += 8;
34023        result.modificationTime = parseMp4Date(view.getUint32(i)); // truncating top 4 bytes
34024
34025        i += 4;
34026        result.timescale = view.getUint32(i);
34027        i += 8;
34028        result.duration = view.getUint32(i); // truncating top 4 bytes
34029      } else {
34030        result.creationTime = parseMp4Date(view.getUint32(i));
34031        i += 4;
34032        result.modificationTime = parseMp4Date(view.getUint32(i));
34033        i += 4;
34034        result.timescale = view.getUint32(i);
34035        i += 4;
34036        result.duration = view.getUint32(i);
34037      }
34038
34039      i += 4; // convert fixed-point, base 16 back to a number
34040
34041      result.rate = view.getUint16(i) + view.getUint16(i + 2) / 16;
34042      i += 4;
34043      result.volume = view.getUint8(i) + view.getUint8(i + 1) / 8;
34044      i += 2;
34045      i += 2;
34046      i += 2 * 4;
34047      result.matrix = new Uint32Array(data.subarray(i, i + 9 * 4));
34048      i += 9 * 4;
34049      i += 6 * 4;
34050      result.nextTrackId = view.getUint32(i);
34051      return result;
34052    },
34053    pdin: function pdin(data) {
34054      var view = new DataView(data.buffer, data.byteOffset, data.byteLength);
34055      return {
34056        version: view.getUint8(0),
34057        flags: new Uint8Array(data.subarray(1, 4)),
34058        rate: view.getUint32(4),
34059        initialDelay: view.getUint32(8)
34060      };
34061    },
34062    sdtp: function sdtp(data) {
34063      var result = {
34064        version: data[0],
34065        flags: new Uint8Array(data.subarray(1, 4)),
34066        samples: []
34067      },
34068          i;
34069
34070      for (i = 4; i < data.byteLength; i++) {
34071        result.samples.push({
34072          dependsOn: (data[i] & 0x30) >> 4,
34073          isDependedOn: (data[i] & 0x0c) >> 2,
34074          hasRedundancy: data[i] & 0x03
34075        });
34076      }
34077
34078      return result;
34079    },
34080    sidx: function sidx(data) {
34081      var view = new DataView(data.buffer, data.byteOffset, data.byteLength),
34082          result = {
34083        version: data[0],
34084        flags: new Uint8Array(data.subarray(1, 4)),
34085        references: [],
34086        referenceId: view.getUint32(4),
34087        timescale: view.getUint32(8),
34088        earliestPresentationTime: view.getUint32(12),
34089        firstOffset: view.getUint32(16)
34090      },
34091          referenceCount = view.getUint16(22),
34092          i;
34093
34094      for (i = 24; referenceCount; i += 12, referenceCount--) {
34095        result.references.push({
34096          referenceType: (data[i] & 0x80) >>> 7,
34097          referencedSize: view.getUint32(i) & 0x7FFFFFFF,
34098          subsegmentDuration: view.getUint32(i + 4),
34099          startsWithSap: !!(data[i + 8] & 0x80),
34100          sapType: (data[i + 8] & 0x70) >>> 4,
34101          sapDeltaTime: view.getUint32(i + 8) & 0x0FFFFFFF
34102        });
34103      }
34104
34105      return result;
34106    },
34107    smhd: function smhd(data) {
34108      return {
34109        version: data[0],
34110        flags: new Uint8Array(data.subarray(1, 4)),
34111        balance: data[4] + data[5] / 256
34112      };
34113    },
34114    stbl: function stbl(data) {
34115      return {
34116        boxes: inspectMp4(data)
34117      };
34118    },
34119    stco: function stco(data) {
34120      var view = new DataView(data.buffer, data.byteOffset, data.byteLength),
34121          result = {
34122        version: data[0],
34123        flags: new Uint8Array(data.subarray(1, 4)),
34124        chunkOffsets: []
34125      },
34126          entryCount = view.getUint32(4),
34127          i;
34128
34129      for (i = 8; entryCount; i += 4, entryCount--) {
34130        result.chunkOffsets.push(view.getUint32(i));
34131      }
34132
34133      return result;
34134    },
34135    stsc: function stsc(data) {
34136      var view = new DataView(data.buffer, data.byteOffset, data.byteLength),
34137          entryCount = view.getUint32(4),
34138          result = {
34139        version: data[0],
34140        flags: new Uint8Array(data.subarray(1, 4)),
34141        sampleToChunks: []
34142      },
34143          i;
34144
34145      for (i = 8; entryCount; i += 12, entryCount--) {
34146        result.sampleToChunks.push({
34147          firstChunk: view.getUint32(i),
34148          samplesPerChunk: view.getUint32(i + 4),
34149          sampleDescriptionIndex: view.getUint32(i + 8)
34150        });
34151      }
34152
34153      return result;
34154    },
34155    stsd: function stsd(data) {
34156      return {
34157        version: data[0],
34158        flags: new Uint8Array(data.subarray(1, 4)),
34159        sampleDescriptions: inspectMp4(data.subarray(8))
34160      };
34161    },
34162    stsz: function stsz(data) {
34163      var view = new DataView(data.buffer, data.byteOffset, data.byteLength),
34164          result = {
34165        version: data[0],
34166        flags: new Uint8Array(data.subarray(1, 4)),
34167        sampleSize: view.getUint32(4),
34168        entries: []
34169      },
34170          i;
34171
34172      for (i = 12; i < data.byteLength; i += 4) {
34173        result.entries.push(view.getUint32(i));
34174      }
34175
34176      return result;
34177    },
34178    stts: function stts(data) {
34179      var view = new DataView(data.buffer, data.byteOffset, data.byteLength),
34180          result = {
34181        version: data[0],
34182        flags: new Uint8Array(data.subarray(1, 4)),
34183        timeToSamples: []
34184      },
34185          entryCount = view.getUint32(4),
34186          i;
34187
34188      for (i = 8; entryCount; i += 8, entryCount--) {
34189        result.timeToSamples.push({
34190          sampleCount: view.getUint32(i),
34191          sampleDelta: view.getUint32(i + 4)
34192        });
34193      }
34194
34195      return result;
34196    },
34197    styp: function styp(data) {
34198      return parse$1.ftyp(data);
34199    },
34200    tfdt: function tfdt(data) {
34201      var result = {
34202        version: data[0],
34203        flags: new Uint8Array(data.subarray(1, 4)),
34204        baseMediaDecodeTime: data[4] << 24 | data[5] << 16 | data[6] << 8 | data[7]
34205      };
34206
34207      if (result.version === 1) {
34208        result.baseMediaDecodeTime *= Math.pow(2, 32);
34209        result.baseMediaDecodeTime += data[8] << 24 | data[9] << 16 | data[10] << 8 | data[11];
34210      }
34211
34212      return result;
34213    },
34214    tfhd: function tfhd(data) {
34215      var view = new DataView(data.buffer, data.byteOffset, data.byteLength),
34216          result = {
34217        version: data[0],
34218        flags: new Uint8Array(data.subarray(1, 4)),
34219        trackId: view.getUint32(4)
34220      },
34221          baseDataOffsetPresent = result.flags[2] & 0x01,
34222          sampleDescriptionIndexPresent = result.flags[2] & 0x02,
34223          defaultSampleDurationPresent = result.flags[2] & 0x08,
34224          defaultSampleSizePresent = result.flags[2] & 0x10,
34225          defaultSampleFlagsPresent = result.flags[2] & 0x20,
34226          durationIsEmpty = result.flags[0] & 0x010000,
34227          defaultBaseIsMoof = result.flags[0] & 0x020000,
34228          i;
34229      i = 8;
34230
34231      if (baseDataOffsetPresent) {
34232        i += 4; // truncate top 4 bytes
34233        // FIXME: should we read the full 64 bits?
34234
34235        result.baseDataOffset = view.getUint32(12);
34236        i += 4;
34237      }
34238
34239      if (sampleDescriptionIndexPresent) {
34240        result.sampleDescriptionIndex = view.getUint32(i);
34241        i += 4;
34242      }
34243
34244      if (defaultSampleDurationPresent) {
34245        result.defaultSampleDuration = view.getUint32(i);
34246        i += 4;
34247      }
34248
34249      if (defaultSampleSizePresent) {
34250        result.defaultSampleSize = view.getUint32(i);
34251        i += 4;
34252      }
34253
34254      if (defaultSampleFlagsPresent) {
34255        result.defaultSampleFlags = view.getUint32(i);
34256      }
34257
34258      if (durationIsEmpty) {
34259        result.durationIsEmpty = true;
34260      }
34261
34262      if (!baseDataOffsetPresent && defaultBaseIsMoof) {
34263        result.baseDataOffsetIsMoof = true;
34264      }
34265
34266      return result;
34267    },
34268    tkhd: function tkhd(data) {
34269      var view = new DataView(data.buffer, data.byteOffset, data.byteLength),
34270          i = 4,
34271          result = {
34272        version: view.getUint8(0),
34273        flags: new Uint8Array(data.subarray(1, 4))
34274      };
34275
34276      if (result.version === 1) {
34277        i += 4;
34278        result.creationTime = parseMp4Date(view.getUint32(i)); // truncating top 4 bytes
34279
34280        i += 8;
34281        result.modificationTime = parseMp4Date(view.getUint32(i)); // truncating top 4 bytes
34282
34283        i += 4;
34284        result.trackId = view.getUint32(i);
34285        i += 4;
34286        i += 8;
34287        result.duration = view.getUint32(i); // truncating top 4 bytes
34288      } else {
34289        result.creationTime = parseMp4Date(view.getUint32(i));
34290        i += 4;
34291        result.modificationTime = parseMp4Date(view.getUint32(i));
34292        i += 4;
34293        result.trackId = view.getUint32(i);
34294        i += 4;
34295        i += 4;
34296        result.duration = view.getUint32(i);
34297      }
34298
34299      i += 4;
34300      i += 2 * 4;
34301      result.layer = view.getUint16(i);
34302      i += 2;
34303      result.alternateGroup = view.getUint16(i);
34304      i += 2; // convert fixed-point, base 16 back to a number
34305
34306      result.volume = view.getUint8(i) + view.getUint8(i + 1) / 8;
34307      i += 2;
34308      i += 2;
34309      result.matrix = new Uint32Array(data.subarray(i, i + 9 * 4));
34310      i += 9 * 4;
34311      result.width = view.getUint16(i) + view.getUint16(i + 2) / 16;
34312      i += 4;
34313      result.height = view.getUint16(i) + view.getUint16(i + 2) / 16;
34314      return result;
34315    },
34316    traf: function traf(data) {
34317      return {
34318        boxes: inspectMp4(data)
34319      };
34320    },
34321    trak: function trak(data) {
34322      return {
34323        boxes: inspectMp4(data)
34324      };
34325    },
34326    trex: function trex(data) {
34327      var view = new DataView(data.buffer, data.byteOffset, data.byteLength);
34328      return {
34329        version: data[0],
34330        flags: new Uint8Array(data.subarray(1, 4)),
34331        trackId: view.getUint32(4),
34332        defaultSampleDescriptionIndex: view.getUint32(8),
34333        defaultSampleDuration: view.getUint32(12),
34334        defaultSampleSize: view.getUint32(16),
34335        sampleDependsOn: data[20] & 0x03,
34336        sampleIsDependedOn: (data[21] & 0xc0) >> 6,
34337        sampleHasRedundancy: (data[21] & 0x30) >> 4,
34338        samplePaddingValue: (data[21] & 0x0e) >> 1,
34339        sampleIsDifferenceSample: !!(data[21] & 0x01),
34340        sampleDegradationPriority: view.getUint16(22)
34341      };
34342    },
34343    trun: function trun(data) {
34344      var result = {
34345        version: data[0],
34346        flags: new Uint8Array(data.subarray(1, 4)),
34347        samples: []
34348      },
34349          view = new DataView(data.buffer, data.byteOffset, data.byteLength),
34350          // Flag interpretation
34351      dataOffsetPresent = result.flags[2] & 0x01,
34352          // compare with 2nd byte of 0x1
34353      firstSampleFlagsPresent = result.flags[2] & 0x04,
34354          // compare with 2nd byte of 0x4
34355      sampleDurationPresent = result.flags[1] & 0x01,
34356          // compare with 2nd byte of 0x100
34357      sampleSizePresent = result.flags[1] & 0x02,
34358          // compare with 2nd byte of 0x200
34359      sampleFlagsPresent = result.flags[1] & 0x04,
34360          // compare with 2nd byte of 0x400
34361      sampleCompositionTimeOffsetPresent = result.flags[1] & 0x08,
34362          // compare with 2nd byte of 0x800
34363      sampleCount = view.getUint32(4),
34364          offset = 8,
34365          sample;
34366
34367      if (dataOffsetPresent) {
34368        // 32 bit signed integer
34369        result.dataOffset = view.getInt32(offset);
34370        offset += 4;
34371      } // Overrides the flags for the first sample only. The order of
34372      // optional values will be: duration, size, compositionTimeOffset
34373
34374
34375      if (firstSampleFlagsPresent && sampleCount) {
34376        sample = {
34377          flags: parseSampleFlags(data.subarray(offset, offset + 4))
34378        };
34379        offset += 4;
34380
34381        if (sampleDurationPresent) {
34382          sample.duration = view.getUint32(offset);
34383          offset += 4;
34384        }
34385
34386        if (sampleSizePresent) {
34387          sample.size = view.getUint32(offset);
34388          offset += 4;
34389        }
34390
34391        if (sampleCompositionTimeOffsetPresent) {
34392          // Note: this should be a signed int if version is 1
34393          sample.compositionTimeOffset = view.getUint32(offset);
34394          offset += 4;
34395        }
34396
34397        result.samples.push(sample);
34398        sampleCount--;
34399      }
34400
34401      while (sampleCount--) {
34402        sample = {};
34403
34404        if (sampleDurationPresent) {
34405          sample.duration = view.getUint32(offset);
34406          offset += 4;
34407        }
34408
34409        if (sampleSizePresent) {
34410          sample.size = view.getUint32(offset);
34411          offset += 4;
34412        }
34413
34414        if (sampleFlagsPresent) {
34415          sample.flags = parseSampleFlags(data.subarray(offset, offset + 4));
34416          offset += 4;
34417        }
34418
34419        if (sampleCompositionTimeOffsetPresent) {
34420          // Note: this should be a signed int if version is 1
34421          sample.compositionTimeOffset = view.getUint32(offset);
34422          offset += 4;
34423        }
34424
34425        result.samples.push(sample);
34426      }
34427
34428      return result;
34429    },
34430    'url ': function url(data) {
34431      return {
34432        version: data[0],
34433        flags: new Uint8Array(data.subarray(1, 4))
34434      };
34435    },
34436    vmhd: function vmhd(data) {
34437      var view = new DataView(data.buffer, data.byteOffset, data.byteLength);
34438      return {
34439        version: data[0],
34440        flags: new Uint8Array(data.subarray(1, 4)),
34441        graphicsmode: view.getUint16(4),
34442        opcolor: new Uint16Array([view.getUint16(6), view.getUint16(8), view.getUint16(10)])
34443      };
34444    }
34445  };
34446  /**
34447   * Return a javascript array of box objects parsed from an ISO base
34448   * media file.
34449   * @param data {Uint8Array} the binary data of the media to be inspected
34450   * @return {array} a javascript array of potentially nested box objects
34451   */
34452
34453
34454  inspectMp4 = function inspectMp4(data) {
34455    var i = 0,
34456        result = [],
34457        view,
34458        size,
34459        type,
34460        end,
34461        box; // Convert data from Uint8Array to ArrayBuffer, to follow Dataview API
34462
34463    var ab = new ArrayBuffer(data.length);
34464    var v = new Uint8Array(ab);
34465
34466    for (var z = 0; z < data.length; ++z) {
34467      v[z] = data[z];
34468    }
34469
34470    view = new DataView(ab);
34471
34472    while (i < data.byteLength) {
34473      // parse box data
34474      size = view.getUint32(i);
34475      type = parseType$1(data.subarray(i + 4, i + 8));
34476      end = size > 1 ? i + size : data.byteLength; // parse type-specific data
34477
34478      box = (parse$1[type] || function (data) {
34479        return {
34480          data: data
34481        };
34482      })(data.subarray(i + 8, end));
34483
34484      box.size = size;
34485      box.type = type; // store this box and move to the next
34486
34487      result.push(box);
34488      i = end;
34489    }
34490
34491    return result;
34492  };
34493  /**
34494   * Returns a textual representation of the javascript represtentation
34495   * of an MP4 file. You can use it as an alternative to
34496   * JSON.stringify() to compare inspected MP4s.
34497   * @param inspectedMp4 {array} the parsed array of boxes in an MP4
34498   * file
34499   * @param depth {number} (optional) the number of ancestor boxes of
34500   * the elements of inspectedMp4. Assumed to be zero if unspecified.
34501   * @return {string} a text representation of the parsed MP4
34502   */
34503
34504
34505  _textifyMp = function textifyMp4(inspectedMp4, depth) {
34506    var indent;
34507    depth = depth || 0;
34508    indent = new Array(depth * 2 + 1).join(' '); // iterate over all the boxes
34509
34510    return inspectedMp4.map(function (box, index) {
34511      // list the box type first at the current indentation level
34512      return indent + box.type + '\n' + // the type is already included and handle child boxes separately
34513      Object.keys(box).filter(function (key) {
34514        return key !== 'type' && key !== 'boxes'; // output all the box properties
34515      }).map(function (key) {
34516        var prefix = indent + '  ' + key + ': ',
34517            value = box[key]; // print out raw bytes as hexademical
34518
34519        if (value instanceof Uint8Array || value instanceof Uint32Array) {
34520          var bytes = Array.prototype.slice.call(new Uint8Array(value.buffer, value.byteOffset, value.byteLength)).map(function (_byte) {
34521            return ' ' + ('00' + _byte.toString(16)).slice(-2);
34522          }).join('').match(/.{1,24}/g);
34523
34524          if (!bytes) {
34525            return prefix + '<>';
34526          }
34527
34528          if (bytes.length === 1) {
34529            return prefix + '<' + bytes.join('').slice(1) + '>';
34530          }
34531
34532          return prefix + '<\n' + bytes.map(function (line) {
34533            return indent + '  ' + line;
34534          }).join('\n') + '\n' + indent + '  >';
34535        } // stringify generic objects
34536
34537
34538        return prefix + JSON.stringify(value, null, 2).split('\n').map(function (line, index) {
34539          if (index === 0) {
34540            return line;
34541          }
34542
34543          return indent + '  ' + line;
34544        }).join('\n');
34545      }).join('\n') + ( // recursively textify the child boxes
34546      box.boxes ? '\n' + _textifyMp(box.boxes, depth + 1) : '');
34547    }).join('\n');
34548  };
34549
34550  var mp4Inspector = {
34551    inspect: inspectMp4,
34552    textify: _textifyMp,
34553    parseTfdt: parse$1.tfdt,
34554    parseHdlr: parse$1.hdlr,
34555    parseTfhd: parse$1.tfhd,
34556    parseTrun: parse$1.trun,
34557    parseSidx: parse$1.sidx
34558  };
34559
34560  /**
34561   * mux.js
34562   *
34563   * Copyright (c) Brightcove
34564   * Licensed Apache-2.0 https://github.com/videojs/mux.js/blob/master/LICENSE
34565   *
34566   * Reads in-band caption information from a video elementary
34567   * stream. Captions must follow the CEA-708 standard for injection
34568   * into an MPEG-2 transport streams.
34569   * @see https://en.wikipedia.org/wiki/CEA-708
34570   * @see https://www.gpo.gov/fdsys/pkg/CFR-2007-title47-vol1/pdf/CFR-2007-title47-vol1-sec15-119.pdf
34571   */
34572  // payload type field to indicate how they are to be
34573  // interpreted. CEAS-708 caption content is always transmitted with
34574  // payload type 0x04.
34575
34576  var USER_DATA_REGISTERED_ITU_T_T35 = 4,
34577      RBSP_TRAILING_BITS = 128;
34578  /**
34579    * Parse a supplemental enhancement information (SEI) NAL unit.
34580    * Stops parsing once a message of type ITU T T35 has been found.
34581    *
34582    * @param bytes {Uint8Array} the bytes of a SEI NAL unit
34583    * @return {object} the parsed SEI payload
34584    * @see Rec. ITU-T H.264, 7.3.2.3.1
34585    */
34586
34587  var parseSei = function parseSei(bytes) {
34588    var i = 0,
34589        result = {
34590      payloadType: -1,
34591      payloadSize: 0
34592    },
34593        payloadType = 0,
34594        payloadSize = 0; // go through the sei_rbsp parsing each each individual sei_message
34595
34596    while (i < bytes.byteLength) {
34597      // stop once we have hit the end of the sei_rbsp
34598      if (bytes[i] === RBSP_TRAILING_BITS) {
34599        break;
34600      } // Parse payload type
34601
34602
34603      while (bytes[i] === 0xFF) {
34604        payloadType += 255;
34605        i++;
34606      }
34607
34608      payloadType += bytes[i++]; // Parse payload size
34609
34610      while (bytes[i] === 0xFF) {
34611        payloadSize += 255;
34612        i++;
34613      }
34614
34615      payloadSize += bytes[i++]; // this sei_message is a 608/708 caption so save it and break
34616      // there can only ever be one caption message in a frame's sei
34617
34618      if (!result.payload && payloadType === USER_DATA_REGISTERED_ITU_T_T35) {
34619        result.payloadType = payloadType;
34620        result.payloadSize = payloadSize;
34621        result.payload = bytes.subarray(i, i + payloadSize);
34622        break;
34623      } // skip the payload and parse the next message
34624
34625
34626      i += payloadSize;
34627      payloadType = 0;
34628      payloadSize = 0;
34629    }
34630
34631    return result;
34632  }; // see ANSI/SCTE 128-1 (2013), section 8.1
34633
34634
34635  var parseUserData = function parseUserData(sei) {
34636    // itu_t_t35_contry_code must be 181 (United States) for
34637    // captions
34638    if (sei.payload[0] !== 181) {
34639      return null;
34640    } // itu_t_t35_provider_code should be 49 (ATSC) for captions
34641
34642
34643    if ((sei.payload[1] << 8 | sei.payload[2]) !== 49) {
34644      return null;
34645    } // the user_identifier should be "GA94" to indicate ATSC1 data
34646
34647
34648    if (String.fromCharCode(sei.payload[3], sei.payload[4], sei.payload[5], sei.payload[6]) !== 'GA94') {
34649      return null;
34650    } // finally, user_data_type_code should be 0x03 for caption data
34651
34652
34653    if (sei.payload[7] !== 0x03) {
34654      return null;
34655    } // return the user_data_type_structure and strip the trailing
34656    // marker bits
34657
34658
34659    return sei.payload.subarray(8, sei.payload.length - 1);
34660  }; // see CEA-708-D, section 4.4
34661
34662
34663  var parseCaptionPackets = function parseCaptionPackets(pts, userData) {
34664    var results = [],
34665        i,
34666        count,
34667        offset,
34668        data; // if this is just filler, return immediately
34669
34670    if (!(userData[0] & 0x40)) {
34671      return results;
34672    } // parse out the cc_data_1 and cc_data_2 fields
34673
34674
34675    count = userData[0] & 0x1f;
34676
34677    for (i = 0; i < count; i++) {
34678      offset = i * 3;
34679      data = {
34680        type: userData[offset + 2] & 0x03,
34681        pts: pts
34682      }; // capture cc data when cc_valid is 1
34683
34684      if (userData[offset + 2] & 0x04) {
34685        data.ccData = userData[offset + 3] << 8 | userData[offset + 4];
34686        results.push(data);
34687      }
34688    }
34689
34690    return results;
34691  };
34692
34693  var discardEmulationPreventionBytes = function discardEmulationPreventionBytes(data) {
34694    var length = data.byteLength,
34695        emulationPreventionBytesPositions = [],
34696        i = 1,
34697        newLength,
34698        newData; // Find all `Emulation Prevention Bytes`
34699
34700    while (i < length - 2) {
34701      if (data[i] === 0 && data[i + 1] === 0 && data[i + 2] === 0x03) {
34702        emulationPreventionBytesPositions.push(i + 2);
34703        i += 2;
34704      } else {
34705        i++;
34706      }
34707    } // If no Emulation Prevention Bytes were found just return the original
34708    // array
34709
34710
34711    if (emulationPreventionBytesPositions.length === 0) {
34712      return data;
34713    } // Create a new array to hold the NAL unit data
34714
34715
34716    newLength = length - emulationPreventionBytesPositions.length;
34717    newData = new Uint8Array(newLength);
34718    var sourceIndex = 0;
34719
34720    for (i = 0; i < newLength; sourceIndex++, i++) {
34721      if (sourceIndex === emulationPreventionBytesPositions[0]) {
34722        // Skip this byte
34723        sourceIndex++; // Remove this position index
34724
34725        emulationPreventionBytesPositions.shift();
34726      }
34727
34728      newData[i] = data[sourceIndex];
34729    }
34730
34731    return newData;
34732  }; // exports
34733
34734
34735  var captionPacketParser = {
34736    parseSei: parseSei,
34737    parseUserData: parseUserData,
34738    parseCaptionPackets: parseCaptionPackets,
34739    discardEmulationPreventionBytes: discardEmulationPreventionBytes,
34740    USER_DATA_REGISTERED_ITU_T_T35: USER_DATA_REGISTERED_ITU_T_T35
34741  };
34742
34743  /**
34744   * mux.js
34745   *
34746   * Copyright (c) Brightcove
34747   * Licensed Apache-2.0 https://github.com/videojs/mux.js/blob/master/LICENSE
34748   *
34749   * A lightweight readable stream implemention that handles event dispatching.
34750   * Objects that inherit from streams should call init in their constructors.
34751   */
34752
34753  var Stream$1 = function Stream() {
34754    this.init = function () {
34755      var listeners = {};
34756      /**
34757       * Add a listener for a specified event type.
34758       * @param type {string} the event name
34759       * @param listener {function} the callback to be invoked when an event of
34760       * the specified type occurs
34761       */
34762
34763      this.on = function (type, listener) {
34764        if (!listeners[type]) {
34765          listeners[type] = [];
34766        }
34767
34768        listeners[type] = listeners[type].concat(listener);
34769      };
34770      /**
34771       * Remove a listener for a specified event type.
34772       * @param type {string} the event name
34773       * @param listener {function} a function previously registered for this
34774       * type of event through `on`
34775       */
34776
34777
34778      this.off = function (type, listener) {
34779        var index;
34780
34781        if (!listeners[type]) {
34782          return false;
34783        }
34784
34785        index = listeners[type].indexOf(listener);
34786        listeners[type] = listeners[type].slice();
34787        listeners[type].splice(index, 1);
34788        return index > -1;
34789      };
34790      /**
34791       * Trigger an event of the specified type on this stream. Any additional
34792       * arguments to this function are passed as parameters to event listeners.
34793       * @param type {string} the event name
34794       */
34795
34796
34797      this.trigger = function (type) {
34798        var callbacks, i, length, args;
34799        callbacks = listeners[type];
34800
34801        if (!callbacks) {
34802          return;
34803        } // Slicing the arguments on every invocation of this method
34804        // can add a significant amount of overhead. Avoid the
34805        // intermediate object creation for the common case of a
34806        // single callback argument
34807
34808
34809        if (arguments.length === 2) {
34810          length = callbacks.length;
34811
34812          for (i = 0; i < length; ++i) {
34813            callbacks[i].call(this, arguments[1]);
34814          }
34815        } else {
34816          args = [];
34817          i = arguments.length;
34818
34819          for (i = 1; i < arguments.length; ++i) {
34820            args.push(arguments[i]);
34821          }
34822
34823          length = callbacks.length;
34824
34825          for (i = 0; i < length; ++i) {
34826            callbacks[i].apply(this, args);
34827          }
34828        }
34829      };
34830      /**
34831       * Destroys the stream and cleans up.
34832       */
34833
34834
34835      this.dispose = function () {
34836        listeners = {};
34837      };
34838    };
34839  };
34840  /**
34841   * Forwards all `data` events on this stream to the destination stream. The
34842   * destination stream should provide a method `push` to receive the data
34843   * events as they arrive.
34844   * @param destination {stream} the stream that will receive all `data` events
34845   * @param autoFlush {boolean} if false, we will not call `flush` on the destination
34846   *                            when the current stream emits a 'done' event
34847   * @see http://nodejs.org/api/stream.html#stream_readable_pipe_destination_options
34848   */
34849
34850
34851  Stream$1.prototype.pipe = function (destination) {
34852    this.on('data', function (data) {
34853      destination.push(data);
34854    });
34855    this.on('done', function (flushSource) {
34856      destination.flush(flushSource);
34857    });
34858    this.on('partialdone', function (flushSource) {
34859      destination.partialFlush(flushSource);
34860    });
34861    this.on('endedtimeline', function (flushSource) {
34862      destination.endTimeline(flushSource);
34863    });
34864    this.on('reset', function (flushSource) {
34865      destination.reset(flushSource);
34866    });
34867    return destination;
34868  }; // Default stream functions that are expected to be overridden to perform
34869  // actual work. These are provided by the prototype as a sort of no-op
34870  // implementation so that we don't have to check for their existence in the
34871  // `pipe` function above.
34872
34873
34874  Stream$1.prototype.push = function (data) {
34875    this.trigger('data', data);
34876  };
34877
34878  Stream$1.prototype.flush = function (flushSource) {
34879    this.trigger('done', flushSource);
34880  };
34881
34882  Stream$1.prototype.partialFlush = function (flushSource) {
34883    this.trigger('partialdone', flushSource);
34884  };
34885
34886  Stream$1.prototype.endTimeline = function (flushSource) {
34887    this.trigger('endedtimeline', flushSource);
34888  };
34889
34890  Stream$1.prototype.reset = function (flushSource) {
34891    this.trigger('reset', flushSource);
34892  };
34893
34894  var stream = Stream$1;
34895
34896  // Link To Transport
34897  // -----------------
34898
34899
34900  var CaptionStream = function CaptionStream() {
34901    CaptionStream.prototype.init.call(this);
34902    this.captionPackets_ = [];
34903    this.ccStreams_ = [new Cea608Stream(0, 0), // eslint-disable-line no-use-before-define
34904    new Cea608Stream(0, 1), // eslint-disable-line no-use-before-define
34905    new Cea608Stream(1, 0), // eslint-disable-line no-use-before-define
34906    new Cea608Stream(1, 1) // eslint-disable-line no-use-before-define
34907    ];
34908    this.reset(); // forward data and done events from CCs to this CaptionStream
34909
34910    this.ccStreams_.forEach(function (cc) {
34911      cc.on('data', this.trigger.bind(this, 'data'));
34912      cc.on('partialdone', this.trigger.bind(this, 'partialdone'));
34913      cc.on('done', this.trigger.bind(this, 'done'));
34914    }, this);
34915  };
34916
34917  CaptionStream.prototype = new stream();
34918
34919  CaptionStream.prototype.push = function (event) {
34920    var sei, userData, newCaptionPackets; // only examine SEI NALs
34921
34922    if (event.nalUnitType !== 'sei_rbsp') {
34923      return;
34924    } // parse the sei
34925
34926
34927    sei = captionPacketParser.parseSei(event.escapedRBSP); // ignore everything but user_data_registered_itu_t_t35
34928
34929    if (sei.payloadType !== captionPacketParser.USER_DATA_REGISTERED_ITU_T_T35) {
34930      return;
34931    } // parse out the user data payload
34932
34933
34934    userData = captionPacketParser.parseUserData(sei); // ignore unrecognized userData
34935
34936    if (!userData) {
34937      return;
34938    } // Sometimes, the same segment # will be downloaded twice. To stop the
34939    // caption data from being processed twice, we track the latest dts we've
34940    // received and ignore everything with a dts before that. However, since
34941    // data for a specific dts can be split across packets on either side of
34942    // a segment boundary, we need to make sure we *don't* ignore the packets
34943    // from the *next* segment that have dts === this.latestDts_. By constantly
34944    // tracking the number of packets received with dts === this.latestDts_, we
34945    // know how many should be ignored once we start receiving duplicates.
34946
34947
34948    if (event.dts < this.latestDts_) {
34949      // We've started getting older data, so set the flag.
34950      this.ignoreNextEqualDts_ = true;
34951      return;
34952    } else if (event.dts === this.latestDts_ && this.ignoreNextEqualDts_) {
34953      this.numSameDts_--;
34954
34955      if (!this.numSameDts_) {
34956        // We've received the last duplicate packet, time to start processing again
34957        this.ignoreNextEqualDts_ = false;
34958      }
34959
34960      return;
34961    } // parse out CC data packets and save them for later
34962
34963
34964    newCaptionPackets = captionPacketParser.parseCaptionPackets(event.pts, userData);
34965    this.captionPackets_ = this.captionPackets_.concat(newCaptionPackets);
34966
34967    if (this.latestDts_ !== event.dts) {
34968      this.numSameDts_ = 0;
34969    }
34970
34971    this.numSameDts_++;
34972    this.latestDts_ = event.dts;
34973  };
34974
vendor: 4,788 bytes, lines 34975-35108
34975  CaptionStream.prototype.flushCCStreams = function (flushType) {
34976    this.ccStreams_.forEach(function (cc) {
34977      return flushType === 'flush' ? cc.flush() : cc.partialFlush();
34978    }, this);
34979  };
34980
34981  CaptionStream.prototype.flushStream = function (flushType) {
34982    // make sure we actually parsed captions before proceeding
34983    if (!this.captionPackets_.length) {
34984      this.flushCCStreams(flushType);
34985      return;
34986    } // In Chrome, the Array#sort function is not stable so add a
34987    // presortIndex that we can use to ensure we get a stable-sort
34988
34989
34990    this.captionPackets_.forEach(function (elem, idx) {
34991      elem.presortIndex = idx;
34992    }); // sort caption byte-pairs based on their PTS values
34993
34994    this.captionPackets_.sort(function (a, b) {
34995      if (a.pts === b.pts) {
34996        return a.presortIndex - b.presortIndex;
34997      }
34998
34999      return a.pts - b.pts;
35000    });
35001    this.captionPackets_.forEach(function (packet) {
35002      if (packet.type < 2) {
35003        // Dispatch packet to the right Cea608Stream
35004        this.dispatchCea608Packet(packet);
35005      } // this is where an 'else' would go for a dispatching packets
35006      // to a theoretical Cea708Stream that handles SERVICEn data
35007
35008    }, this);
35009    this.captionPackets_.length = 0;
35010    this.flushCCStreams(flushType);
35011  };
35012
35013  CaptionStream.prototype.flush = function () {
35014    return this.flushStream('flush');
35015  }; // Only called if handling partial data
35016
35017
35018  CaptionStream.prototype.partialFlush = function () {
35019    return this.flushStream('partialFlush');
35020  };
35021
35022  CaptionStream.prototype.reset = function () {
35023    this.latestDts_ = null;
35024    this.ignoreNextEqualDts_ = false;
35025    this.numSameDts_ = 0;
35026    this.activeCea608Channel_ = [null, null];
35027    this.ccStreams_.forEach(function (ccStream) {
35028      ccStream.reset();
35029    });
35030  }; // From the CEA-608 spec:
35031
35032  /*
35033   * When XDS sub-packets are interleaved with other services, the end of each sub-packet shall be followed
35034   * by a control pair to change to a different service. When any of the control codes from 0x10 to 0x1F is
35035   * used to begin a control code pair, it indicates the return to captioning or Text data. The control code pair
35036   * and subsequent data should then be processed according to the FCC rules. It may be necessary for the
35037   * line 21 data encoder to automatically insert a control code pair (i.e. RCL, RU2, RU3, RU4, RDC, or RTD)
35038   * to switch to captioning or Text.
35039  */
35040  // With that in mind, we ignore any data between an XDS control code and a
35041  // subsequent closed-captioning control code.
35042
35043
35044  CaptionStream.prototype.dispatchCea608Packet = function (packet) {
35045    // NOTE: packet.type is the CEA608 field
35046    if (this.setsTextOrXDSActive(packet)) {
35047      this.activeCea608Channel_[packet.type] = null;
35048    } else if (this.setsChannel1Active(packet)) {
35049      this.activeCea608Channel_[packet.type] = 0;
35050    } else if (this.setsChannel2Active(packet)) {
35051      this.activeCea608Channel_[packet.type] = 1;
35052    }
35053
35054    if (this.activeCea608Channel_[packet.type] === null) {
35055      // If we haven't received anything to set the active channel, or the
35056      // packets are Text/XDS data, discard the data; we don't want jumbled
35057      // captions
35058      return;
35059    }
35060
35061    this.ccStreams_[(packet.type << 1) + this.activeCea608Channel_[packet.type]].push(packet);
35062  };
35063
35064  CaptionStream.prototype.setsChannel1Active = function (packet) {
35065    return (packet.ccData & 0x7800) === 0x1000;
35066  };
35067
35068  CaptionStream.prototype.setsChannel2Active = function (packet) {
35069    return (packet.ccData & 0x7800) === 0x1800;
35070  };
35071
35072  CaptionStream.prototype.setsTextOrXDSActive = function (packet) {
35073    return (packet.ccData & 0x7100) === 0x0100 || (packet.ccData & 0x78fe) === 0x102a || (packet.ccData & 0x78fe) === 0x182a;
35074  }; // ----------------------
35075  // Session to Application
35076  // ----------------------
35077  // This hash maps non-ASCII, special, and extended character codes to their
35078  // proper Unicode equivalent. The first keys that are only a single byte
35079  // are the non-standard ASCII characters, which simply map the CEA608 byte
35080  // to the standard ASCII/Unicode. The two-byte keys that follow are the CEA608
35081  // character codes, but have their MSB bitmasked with 0x03 so that a lookup
35082  // can be performed regardless of the field and data channel on which the
35083  // character code was received.
35084
35085
35086  var CHARACTER_TRANSLATION = {
35087    0x2a: 0xe1,
35088    // á
35089    0x5c: 0xe9,
35090    // é
35091    0x5e: 0xed,
35092    // í
35093    0x5f: 0xf3,
35094    // ó
35095    0x60: 0xfa,
35096    // ú
35097    0x7b: 0xe7,
35098    // ç
35099    0x7c: 0xf7,
35100    // ÷
35101    0x7d: 0xd1,
35102    // Ñ
35103    0x7e: 0xf1,
35104    // ñ
35105    0x7f: 0x2588,
35106    // █
35107    0x0130: 0xae,
35108    // ®
35109    0x0131: 0xb0,
35110    // °
35111    0x0132: 0xbd,
35112    // ½
35113    0x0133: 0xbf,
35114    // ¿
35115    0x0134: 0x2122,
35116    // ™
35117    0x0135: 0xa2,
35118    // ¢
35119    0x0136: 0xa3,
35120    // £
35121    0x0137: 0x266a,
35122    // ♪
35123    0x0138: 0xe0,
35124    // à
35125    0x0139: 0xa0,
35126    //
35127    0x013a: 0xe8,
35128    // è
35129    0x013b: 0xe2,
35130    // â
35131    0x013c: 0xea,
35132    // ê
35133    0x013d: 0xee,
35134    // î
35135    0x013e: 0xf4,
35136    // ô
35137    0x013f: 0xfb,
35138    // û
35139    0x0220: 0xc1,
35140    // Á
35141    0x0221: 0xc9,
35142    // É
35143    0x0222: 0xd3,
35144    // Ó
35145    0x0223: 0xda,
35146    // Ú
35147    0x0224: 0xdc,
35148    // Ü
35149    0x0225: 0xfc,
35150    // ü
35151    0x0226: 0x2018,
35152    // ‘
35153    0x0227: 0xa1,
35154    // ¡
35155    0x0228: 0x2a,
35156    // *
35157    0x0229: 0x27,
35158    // '
35159    0x022a: 0x2014,
35160    // —
35161    0x022b: 0xa9,
35162    // ©
35163    0x022c: 0x2120,
35164    // ℠
35165    0x022d: 0x2022,
35166    // •
35167    0x022e: 0x201c,
35168    // “
35169    0x022f: 0x201d,
35170    // ”
35171    0x0230: 0xc0,
35172    // À
35173    0x0231: 0xc2,
35174    // Â
35175    0x0232: 0xc7,
35176    // Ç
35177    0x0233: 0xc8,
35178    // È
35179    0x0234: 0xca,
35180    // Ê
35181    0x0235: 0xcb,
35182    // Ë
35183    0x0236: 0xeb,
35184    // ë
35185    0x0237: 0xce,
35186    // Î
35187    0x0238: 0xcf,
35188    // Ï
35189    0x0239: 0xef,
35190    // ï
35191    0x023a: 0xd4,
35192    // Ô
35193    0x023b: 0xd9,
35194    // Ù
35195    0x023c: 0xf9,
35196    // ù
35197    0x023d: 0xdb,
35198    // Û
35199    0x023e: 0xab,
35200    // «
35201    0x023f: 0xbb,
35202    // »
35203    0x0320: 0xc3,
35204    // Ã
35205    0x0321: 0xe3,
35206    // ã
35207    0x0322: 0xcd,
35208    // Í
35209    0x0323: 0xcc,
35210    // Ì
35211    0x0324: 0xec,
35212    // ì
35213    0x0325: 0xd2,
35214    // Ò
35215    0x0326: 0xf2,
35216    // ò
35217    0x0327: 0xd5,
35218    // Õ
35219    0x0328: 0xf5,
35220    // õ
35221    0x0329: 0x7b,
35222    // {
35223    0x032a: 0x7d,
35224    // }
35225    0x032b: 0x5c,
35226    // \
35227    0x032c: 0x5e,
35228    // ^
35229    0x032d: 0x5f,
35230    // _
35231    0x032e: 0x7c,
35232    // |
35233    0x032f: 0x7e,
35234    // ~
35235    0x0330: 0xc4,
35236    // Ä
35237    0x0331: 0xe4,
vendor: 18,474 bytes, lines 35238-35737
35238    // ä
35239    0x0332: 0xd6,
35240    // Ö
35241    0x0333: 0xf6,
35242    // ö
35243    0x0334: 0xdf,
35244    // ß
35245    0x0335: 0xa5,
35246    // ¥
35247    0x0336: 0xa4,
35248    // ¤
35249    0x0337: 0x2502,
35250    // │
35251    0x0338: 0xc5,
35252    // Å
35253    0x0339: 0xe5,
35254    // å
35255    0x033a: 0xd8,
35256    // Ø
35257    0x033b: 0xf8,
35258    // ø
35259    0x033c: 0x250c,
35260    // ┌
35261    0x033d: 0x2510,
35262    // ┐
35263    0x033e: 0x2514,
35264    // └
35265    0x033f: 0x2518 // ┘
35266
35267  };
35268
35269  var getCharFromCode = function getCharFromCode(code) {
35270    if (code === null) {
35271      return '';
35272    }
35273
35274    code = CHARACTER_TRANSLATION[code] || code;
35275    return String.fromCharCode(code);
35276  }; // the index of the last row in a CEA-608 display buffer
35277
35278
35279  var BOTTOM_ROW = 14; // This array is used for mapping PACs -> row #, since there's no way of
35280  // getting it through bit logic.
35281
35282  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
35283  // cells. The "bottom" row is the last element in the outer array.
35284
35285  var createDisplayBuffer = function createDisplayBuffer() {
35286    var result = [],
35287        i = BOTTOM_ROW + 1;
35288
35289    while (i--) {
35290      result.push('');
35291    }
35292
35293    return result;
35294  };
35295
35296  var Cea608Stream = function Cea608Stream(field, dataChannel) {
35297    Cea608Stream.prototype.init.call(this);
35298    this.field_ = field || 0;
35299    this.dataChannel_ = dataChannel || 0;
35300    this.name_ = 'CC' + ((this.field_ << 1 | this.dataChannel_) + 1);
35301    this.setConstants();
35302    this.reset();
35303
35304    this.push = function (packet) {
35305      var data, swap, char0, char1, text; // remove the parity bits
35306
35307      data = packet.ccData & 0x7f7f; // ignore duplicate control codes; the spec demands they're sent twice
35308
35309      if (data === this.lastControlCode_) {
35310        this.lastControlCode_ = null;
35311        return;
35312      } // Store control codes
35313
35314
35315      if ((data & 0xf000) === 0x1000) {
35316        this.lastControlCode_ = data;
35317      } else if (data !== this.PADDING_) {
35318        this.lastControlCode_ = null;
35319      }
35320
35321      char0 = data >>> 8;
35322      char1 = data & 0xff;
35323
35324      if (data === this.PADDING_) {
35325        return;
35326      } else if (data === this.RESUME_CAPTION_LOADING_) {
35327        this.mode_ = 'popOn';
35328      } else if (data === this.END_OF_CAPTION_) {
35329        // If an EOC is received while in paint-on mode, the displayed caption
35330        // text should be swapped to non-displayed memory as if it was a pop-on
35331        // caption. Because of that, we should explicitly switch back to pop-on
35332        // mode
35333        this.mode_ = 'popOn';
35334        this.clearFormatting(packet.pts); // if a caption was being displayed, it's gone now
35335
35336        this.flushDisplayed(packet.pts); // flip memory
35337
35338        swap = this.displayed_;
35339        this.displayed_ = this.nonDisplayed_;
35340        this.nonDisplayed_ = swap; // start measuring the time to display the caption
35341
35342        this.startPts_ = packet.pts;
35343      } else if (data === this.ROLL_UP_2_ROWS_) {
35344        this.rollUpRows_ = 2;
35345        this.setRollUp(packet.pts);
35346      } else if (data === this.ROLL_UP_3_ROWS_) {
35347        this.rollUpRows_ = 3;
35348        this.setRollUp(packet.pts);
35349      } else if (data === this.ROLL_UP_4_ROWS_) {
35350        this.rollUpRows_ = 4;
35351        this.setRollUp(packet.pts);
35352      } else if (data === this.CARRIAGE_RETURN_) {
35353        this.clearFormatting(packet.pts);
35354        this.flushDisplayed(packet.pts);
35355        this.shiftRowsUp_();
35356        this.startPts_ = packet.pts;
35357      } else if (data === this.BACKSPACE_) {
35358        if (this.mode_ === 'popOn') {
35359          this.nonDisplayed_[this.row_] = this.nonDisplayed_[this.row_].slice(0, -1);
35360        } else {
35361          this.displayed_[this.row_] = this.displayed_[this.row_].slice(0, -1);
35362        }
35363      } else if (data === this.ERASE_DISPLAYED_MEMORY_) {
35364        this.flushDisplayed(packet.pts);
35365        this.displayed_ = createDisplayBuffer();
35366      } else if (data === this.ERASE_NON_DISPLAYED_MEMORY_) {
35367        this.nonDisplayed_ = createDisplayBuffer();
35368      } else if (data === this.RESUME_DIRECT_CAPTIONING_) {
35369        if (this.mode_ !== 'paintOn') {
35370          // NOTE: This should be removed when proper caption positioning is
35371          // implemented
35372          this.flushDisplayed(packet.pts);
35373          this.displayed_ = createDisplayBuffer();
35374        }
35375
35376        this.mode_ = 'paintOn';
35377        this.startPts_ = packet.pts; // Append special characters to caption text
35378      } else if (this.isSpecialCharacter(char0, char1)) {
35379        // Bitmask char0 so that we can apply character transformations
35380        // regardless of field and data channel.
35381        // Then byte-shift to the left and OR with char1 so we can pass the
35382        // entire character code to `getCharFromCode`.
35383        char0 = (char0 & 0x03) << 8;
35384        text = getCharFromCode(char0 | char1);
35385        this[this.mode_](packet.pts, text);
35386        this.column_++; // Append extended characters to caption text
35387      } else if (this.isExtCharacter(char0, char1)) {
35388        // Extended characters always follow their "non-extended" equivalents.
35389        // IE if a "è" is desired, you'll always receive "eè"; non-compliant
35390        // decoders are supposed to drop the "è", while compliant decoders
35391        // backspace the "e" and insert "è".
35392        // Delete the previous character
35393        if (this.mode_ === 'popOn') {
35394          this.nonDisplayed_[this.row_] = this.nonDisplayed_[this.row_].slice(0, -1);
35395        } else {
35396          this.displayed_[this.row_] = this.displayed_[this.row_].slice(0, -1);
35397        } // Bitmask char0 so that we can apply character transformations
35398        // regardless of field and data channel.
35399        // Then byte-shift to the left and OR with char1 so we can pass the
35400        // entire character code to `getCharFromCode`.
35401
35402
35403        char0 = (char0 & 0x03) << 8;
35404        text = getCharFromCode(char0 | char1);
35405        this[this.mode_](packet.pts, text);
35406        this.column_++; // Process mid-row codes
35407      } else if (this.isMidRowCode(char0, char1)) {
35408        // Attributes are not additive, so clear all formatting
35409        this.clearFormatting(packet.pts); // According to the standard, mid-row codes
35410        // should be replaced with spaces, so add one now
35411
35412        this[this.mode_](packet.pts, ' ');
35413        this.column_++;
35414
35415        if ((char1 & 0xe) === 0xe) {
35416          this.addFormatting(packet.pts, ['i']);
35417        }
35418
35419        if ((char1 & 0x1) === 0x1) {
35420          this.addFormatting(packet.pts, ['u']);
35421        } // Detect offset control codes and adjust cursor
35422
35423      } else if (this.isOffsetControlCode(char0, char1)) {
35424        // Cursor position is set by indent PAC (see below) in 4-column
35425        // increments, with an additional offset code of 1-3 to reach any
35426        // of the 32 columns specified by CEA-608. So all we need to do
35427        // here is increment the column cursor by the given offset.
35428        this.column_ += char1 & 0x03; // Detect PACs (Preamble Address Codes)
35429      } else if (this.isPAC(char0, char1)) {
35430        // There's no logic for PAC -> row mapping, so we have to just
35431        // find the row code in an array and use its index :(
35432        var row = ROWS.indexOf(data & 0x1f20); // Configure the caption window if we're in roll-up mode
35433
35434        if (this.mode_ === 'rollUp') {
35435          // This implies that the base row is incorrectly set.
35436          // As per the recommendation in CEA-608(Base Row Implementation), defer to the number
35437          // of roll-up rows set.
35438          if (row - this.rollUpRows_ + 1 < 0) {
35439            row = this.rollUpRows_ - 1;
35440          }
35441
35442          this.setRollUp(packet.pts, row);
35443        }
35444
35445        if (row !== this.row_) {
35446          // formatting is only persistent for current row
35447          this.clearFormatting(packet.pts);
35448          this.row_ = row;
35449        } // All PACs can apply underline, so detect and apply
35450        // (All odd-numbered second bytes set underline)
35451
35452
35453        if (char1 & 0x1 && this.formatting_.indexOf('u') === -1) {
35454          this.addFormatting(packet.pts, ['u']);
35455        }
35456
35457        if ((data & 0x10) === 0x10) {
35458          // We've got an indent level code. Each successive even number
35459          // increments the column cursor by 4, so we can get the desired
35460          // column position by bit-shifting to the right (to get n/2)
35461          // and multiplying by 4.
35462          this.column_ = ((data & 0xe) >> 1) * 4;
35463        }
35464
35465        if (this.isColorPAC(char1)) {
35466          // it's a color code, though we only support white, which
35467          // can be either normal or italicized. white italics can be
35468          // either 0x4e or 0x6e depending on the row, so we just
35469          // bitwise-and with 0xe to see if italics should be turned on
35470          if ((char1 & 0xe) === 0xe) {
35471            this.addFormatting(packet.pts, ['i']);
35472          }
35473        } // We have a normal character in char0, and possibly one in char1
35474
35475      } else if (this.isNormalChar(char0)) {
35476        if (char1 === 0x00) {
35477          char1 = null;
35478        }
35479
35480        text = getCharFromCode(char0);
35481        text += getCharFromCode(char1);
35482        this[this.mode_](packet.pts, text);
35483        this.column_ += text.length;
35484      } // finish data processing
35485
35486    };
35487  };
35488
35489  Cea608Stream.prototype = new stream(); // Trigger a cue point that captures the current state of the
35490  // display buffer
35491
35492  Cea608Stream.prototype.flushDisplayed = function (pts) {
35493    var content = this.displayed_ // remove spaces from the start and end of the string
35494    .map(function (row) {
35495      try {
35496        return row.trim();
35497      } catch (e) {
35498        // Ordinarily, this shouldn't happen. However, caption
35499        // parsing errors should not throw exceptions and
35500        // break playback.
35501        // eslint-disable-next-line no-console
35502        console.error('Skipping malformed caption.');
35503        return '';
35504      }
35505    }) // combine all text rows to display in one cue
35506    .join('\n') // and remove blank rows from the start and end, but not the middle
35507    .replace(/^\n+|\n+$/g, '');
35508
35509    if (content.length) {
35510      this.trigger('data', {
35511        startPts: this.startPts_,
35512        endPts: pts,
35513        text: content,
35514        stream: this.name_
35515      });
35516    }
35517  };
35518  /**
35519   * Zero out the data, used for startup and on seek
35520   */
35521
35522
35523  Cea608Stream.prototype.reset = function () {
35524    this.mode_ = 'popOn'; // When in roll-up mode, the index of the last row that will
35525    // actually display captions. If a caption is shifted to a row
35526    // with a lower index than this, it is cleared from the display
35527    // buffer
35528
35529    this.topRow_ = 0;
35530    this.startPts_ = 0;
35531    this.displayed_ = createDisplayBuffer();
35532    this.nonDisplayed_ = createDisplayBuffer();
35533    this.lastControlCode_ = null; // Track row and column for proper line-breaking and spacing
35534
35535    this.column_ = 0;
35536    this.row_ = BOTTOM_ROW;
35537    this.rollUpRows_ = 2; // This variable holds currently-applied formatting
35538
35539    this.formatting_ = [];
35540  };
35541  /**
35542   * Sets up control code and related constants for this instance
35543   */
35544
35545
35546  Cea608Stream.prototype.setConstants = function () {
35547    // The following attributes have these uses:
35548    // ext_ :    char0 for mid-row codes, and the base for extended
35549    //           chars (ext_+0, ext_+1, and ext_+2 are char0s for
35550    //           extended codes)
35551    // control_: char0 for control codes, except byte-shifted to the
35552    //           left so that we can do this.control_ | CONTROL_CODE
35553    // offset_:  char0 for tab offset codes
35554    //
35555    // It's also worth noting that control codes, and _only_ control codes,
35556    // differ between field 1 and field2. Field 2 control codes are always
35557    // their field 1 value plus 1. That's why there's the "| field" on the
35558    // control value.
35559    if (this.dataChannel_ === 0) {
35560      this.BASE_ = 0x10;
35561      this.EXT_ = 0x11;
35562      this.CONTROL_ = (0x14 | this.field_) << 8;
35563      this.OFFSET_ = 0x17;
35564    } else if (this.dataChannel_ === 1) {
35565      this.BASE_ = 0x18;
35566      this.EXT_ = 0x19;
35567      this.CONTROL_ = (0x1c | this.field_) << 8;
35568      this.OFFSET_ = 0x1f;
35569    } // Constants for the LSByte command codes recognized by Cea608Stream. This
35570    // list is not exhaustive. For a more comprehensive listing and semantics see
35571    // http://www.gpo.gov/fdsys/pkg/CFR-2010-title47-vol1/pdf/CFR-2010-title47-vol1-sec15-119.pdf
35572    // Padding
35573
35574
35575    this.PADDING_ = 0x0000; // Pop-on Mode
35576
35577    this.RESUME_CAPTION_LOADING_ = this.CONTROL_ | 0x20;
35578    this.END_OF_CAPTION_ = this.CONTROL_ | 0x2f; // Roll-up Mode
35579
35580    this.ROLL_UP_2_ROWS_ = this.CONTROL_ | 0x25;
35581    this.ROLL_UP_3_ROWS_ = this.CONTROL_ | 0x26;
35582    this.ROLL_UP_4_ROWS_ = this.CONTROL_ | 0x27;
35583    this.CARRIAGE_RETURN_ = this.CONTROL_ | 0x2d; // paint-on mode
35584
35585    this.RESUME_DIRECT_CAPTIONING_ = this.CONTROL_ | 0x29; // Erasure
35586
35587    this.BACKSPACE_ = this.CONTROL_ | 0x21;
35588    this.ERASE_DISPLAYED_MEMORY_ = this.CONTROL_ | 0x2c;
35589    this.ERASE_NON_DISPLAYED_MEMORY_ = this.CONTROL_ | 0x2e;
35590  };
35591  /**
35592   * Detects if the 2-byte packet data is a special character
35593   *
35594   * Special characters have a second byte in the range 0x30 to 0x3f,
35595   * with the first byte being 0x11 (for data channel 1) or 0x19 (for
35596   * data channel 2).
35597   *
35598   * @param  {Integer} char0 The first byte
35599   * @param  {Integer} char1 The second byte
35600   * @return {Boolean}       Whether the 2 bytes are an special character
35601   */
35602
35603
35604  Cea608Stream.prototype.isSpecialCharacter = function (char0, char1) {
35605    return char0 === this.EXT_ && char1 >= 0x30 && char1 <= 0x3f;
35606  };
35607  /**
35608   * Detects if the 2-byte packet data is an extended character
35609   *
35610   * Extended characters have a second byte in the range 0x20 to 0x3f,
35611   * with the first byte being 0x12 or 0x13 (for data channel 1) or
35612   * 0x1a or 0x1b (for data channel 2).
35613   *
35614   * @param  {Integer} char0 The first byte
35615   * @param  {Integer} char1 The second byte
35616   * @return {Boolean}       Whether the 2 bytes are an extended character
35617   */
35618
35619
35620  Cea608Stream.prototype.isExtCharacter = function (char0, char1) {
35621    return (char0 === this.EXT_ + 1 || char0 === this.EXT_ + 2) && char1 >= 0x20 && char1 <= 0x3f;
35622  };
35623  /**
35624   * Detects if the 2-byte packet is a mid-row code
35625   *
35626   * Mid-row codes have a second byte in the range 0x20 to 0x2f, with
35627   * the first byte being 0x11 (for data channel 1) or 0x19 (for data
35628   * channel 2).
35629   *
35630   * @param  {Integer} char0 The first byte
35631   * @param  {Integer} char1 The second byte
35632   * @return {Boolean}       Whether the 2 bytes are a mid-row code
35633   */
35634
35635
35636  Cea608Stream.prototype.isMidRowCode = function (char0, char1) {
35637    return char0 === this.EXT_ && char1 >= 0x20 && char1 <= 0x2f;
35638  };
35639  /**
35640   * Detects if the 2-byte packet is an offset control code
35641   *
35642   * Offset control codes have a second byte in the range 0x21 to 0x23,
35643   * with the first byte being 0x17 (for data channel 1) or 0x1f (for
35644   * data channel 2).
35645   *
35646   * @param  {Integer} char0 The first byte
35647   * @param  {Integer} char1 The second byte
35648   * @return {Boolean}       Whether the 2 bytes are an offset control code
35649   */
35650
35651
35652  Cea608Stream.prototype.isOffsetControlCode = function (char0, char1) {
35653    return char0 === this.OFFSET_ && char1 >= 0x21 && char1 <= 0x23;
35654  };
35655  /**
35656   * Detects if the 2-byte packet is a Preamble Address Code
35657   *
35658   * PACs have a first byte in the range 0x10 to 0x17 (for data channel 1)
35659   * or 0x18 to 0x1f (for data channel 2), with the second byte in the
35660   * range 0x40 to 0x7f.
35661   *
35662   * @param  {Integer} char0 The first byte
35663   * @param  {Integer} char1 The second byte
35664   * @return {Boolean}       Whether the 2 bytes are a PAC
35665   */
35666
35667
35668  Cea608Stream.prototype.isPAC = function (char0, char1) {
35669    return char0 >= this.BASE_ && char0 < this.BASE_ + 8 && char1 >= 0x40 && char1 <= 0x7f;
35670  };
35671  /**
35672   * Detects if a packet's second byte is in the range of a PAC color code
35673   *
35674   * PAC color codes have the second byte be in the range 0x40 to 0x4f, or
35675   * 0x60 to 0x6f.
35676   *
35677   * @param  {Integer} char1 The second byte
35678   * @return {Boolean}       Whether the byte is a color PAC
35679   */
35680
35681
35682  Cea608Stream.prototype.isColorPAC = function (char1) {
35683    return char1 >= 0x40 && char1 <= 0x4f || char1 >= 0x60 && char1 <= 0x7f;
35684  };
35685  /**
35686   * Detects if a single byte is in the range of a normal character
35687   *
35688   * Normal text bytes are in the range 0x20 to 0x7f.
35689   *
35690   * @param  {Integer} char  The byte
35691   * @return {Boolean}       Whether the byte is a normal character
35692   */
35693
35694
35695  Cea608Stream.prototype.isNormalChar = function (_char) {
35696    return _char >= 0x20 && _char <= 0x7f;
35697  };
35698  /**
35699   * Configures roll-up
35700   *
35701   * @param  {Integer} pts         Current PTS
35702   * @param  {Integer} newBaseRow  Used by PACs to slide the current window to
35703   *                               a new position
35704   */
35705
35706
35707  Cea608Stream.prototype.setRollUp = function (pts, newBaseRow) {
35708    // Reset the base row to the bottom row when switching modes
35709    if (this.mode_ !== 'rollUp') {
35710      this.row_ = BOTTOM_ROW;
35711      this.mode_ = 'rollUp'; // Spec says to wipe memories when switching to roll-up
35712
35713      this.flushDisplayed(pts);
35714      this.nonDisplayed_ = createDisplayBuffer();
35715      this.displayed_ = createDisplayBuffer();
35716    }
35717
35718    if (newBaseRow !== undefined && newBaseRow !== this.row_) {
35719      // move currently displayed captions (up or down) to the new base row
35720      for (var i = 0; i < this.rollUpRows_; i++) {
35721        this.displayed_[newBaseRow - i] = this.displayed_[this.row_ - i];
35722        this.displayed_[this.row_ - i] = '';
35723      }
35724    }
35725
35726    if (newBaseRow === undefined) {
35727      newBaseRow = this.row_;
35728    }
35729
35730    this.topRow_ = newBaseRow - this.rollUpRows_ + 1;
35731  }; // Adds the opening HTML tag for the passed character to the caption text,
35732  // and keeps track of it for later closing
35733
35734
35735  Cea608Stream.prototype.addFormatting = function (pts, format) {
35736    this.formatting_ = this.formatting_.concat(format);
35737    var text = format.reduce(function (text, format) {
vendor: 2,997 bytes, lines 35738-35838
35738      return text + '<' + format + '>';
35739    }, '');
35740    this[this.mode_](pts, text);
35741  }; // Adds HTML closing tags for current formatting to caption text and
35742  // clears remembered formatting
35743
35744
35745  Cea608Stream.prototype.clearFormatting = function (pts) {
35746    if (!this.formatting_.length) {
35747      return;
35748    }
35749
35750    var text = this.formatting_.reverse().reduce(function (text, format) {
35751      return text + '</' + format + '>';
35752    }, '');
35753    this.formatting_ = [];
35754    this[this.mode_](pts, text);
35755  }; // Mode Implementations
35756
35757
35758  Cea608Stream.prototype.popOn = function (pts, text) {
35759    var baseRow = this.nonDisplayed_[this.row_]; // buffer characters
35760
35761    baseRow += text;
35762    this.nonDisplayed_[this.row_] = baseRow;
35763  };
35764
35765  Cea608Stream.prototype.rollUp = function (pts, text) {
35766    var baseRow = this.displayed_[this.row_];
35767    baseRow += text;
35768    this.displayed_[this.row_] = baseRow;
35769  };
35770
35771  Cea608Stream.prototype.shiftRowsUp_ = function () {
35772    var i; // clear out inactive rows
35773
35774    for (i = 0; i < this.topRow_; i++) {
35775      this.displayed_[i] = '';
35776    }
35777
35778    for (i = this.row_ + 1; i < BOTTOM_ROW + 1; i++) {
35779      this.displayed_[i] = '';
35780    } // shift displayed rows up
35781
35782
35783    for (i = this.topRow_; i < this.row_; i++) {
35784      this.displayed_[i] = this.displayed_[i + 1];
35785    } // clear out the bottom row
35786
35787
35788    this.displayed_[this.row_] = '';
35789  };
35790
35791  Cea608Stream.prototype.paintOn = function (pts, text) {
35792    var baseRow = this.displayed_[this.row_];
35793    baseRow += text;
35794    this.displayed_[this.row_] = baseRow;
35795  }; // exports
35796
35797
35798  var captionStream = {
35799    CaptionStream: CaptionStream,
35800    Cea608Stream: Cea608Stream
35801  };
35802
35803  var discardEmulationPreventionBytes$1 = captionPacketParser.discardEmulationPreventionBytes;
35804  var CaptionStream$1 = captionStream.CaptionStream;
35805  /**
35806    * Maps an offset in the mdat to a sample based on the the size of the samples.
35807    * Assumes that `parseSamples` has been called first.
35808    *
35809    * @param {Number} offset - The offset into the mdat
35810    * @param {Object[]} samples - An array of samples, parsed using `parseSamples`
35811    * @return {?Object} The matching sample, or null if no match was found.
35812    *
35813    * @see ISO-BMFF-12/2015, Section 8.8.8
35814   **/
35815
35816  var mapToSample = function mapToSample(offset, samples) {
35817    var approximateOffset = offset;
35818
35819    for (var i = 0; i < samples.length; i++) {
35820      var sample = samples[i];
35821
35822      if (approximateOffset < sample.size) {
35823        return sample;
35824      }
35825
35826      approximateOffset -= sample.size;
35827    }
35828
35829    return null;
35830  };
35831  /**
35832    * Finds SEI nal units contained in a Media Data Box.
35833    * Assumes that `parseSamples` has been called first.
35834    *
35835    * @param {Uint8Array} avcStream - The bytes of the mdat
35836    * @param {Object[]} samples - The samples parsed out by `parseSamples`
35837    * @param {Number} trackId - The trackId of this video track
35838    * @return {Object[]}
vendor: 4,299 bytes, lines 35838-35963
35838 seiNals - the parsed SEI NALUs found.
35839    *   The contents of the seiNal should match what is expected by
35840    *   CaptionStream.push (nalUnitType, size, data, escapedRBSP, pts, dts)
35841    *
35842    * @see ISO-BMFF-12/2015, Section 8.1.1
35843    * @see Rec. ITU-T H.264, 7.3.2.3.1
35844   **/
35845
35846
35847  var findSeiNals = function findSeiNals(avcStream, samples, trackId) {
35848    var avcView = new DataView(avcStream.buffer, avcStream.byteOffset, avcStream.byteLength),
35849        result = [],
35850        seiNal,
35851        i,
35852        length,
35853        lastMatchedSample;
35854
35855    for (i = 0; i + 4 < avcStream.length; i += length) {
35856      length = avcView.getUint32(i);
35857      i += 4; // Bail if this doesn't appear to be an H264 stream
35858
35859      if (length <= 0) {
35860        continue;
35861      }
35862
35863      switch (avcStream[i] & 0x1F) {
35864        case 0x06:
35865          var data = avcStream.subarray(i + 1, i + 1 + length);
35866          var matchingSample = mapToSample(i, samples);
35867          seiNal = {
35868            nalUnitType: 'sei_rbsp',
35869            size: length,
35870            data: data,
35871            escapedRBSP: discardEmulationPreventionBytes$1(data),
35872            trackId: trackId
35873          };
35874
35875          if (matchingSample) {
35876            seiNal.pts = matchingSample.pts;
35877            seiNal.dts = matchingSample.dts;
35878            lastMatchedSample = matchingSample;
35879          } else {
35880            // If a matching sample cannot be found, use the last
35881            // sample's values as they should be as close as possible
35882            seiNal.pts = lastMatchedSample.pts;
35883            seiNal.dts = lastMatchedSample.dts;
35884          }
35885
35886          result.push(seiNal);
35887          break;
35888
35889        default:
35890          break;
35891      }
35892    }
35893
35894    return result;
35895  };
35896  /**
35897    * Parses sample information out of Track Run Boxes and calculates
35898    * the absolute presentation and decode timestamps of each sample.
35899    *
35900    * @param {Array<Uint8Array>} truns - The Trun Run boxes to be parsed
35901    * @param {Number} baseMediaDecodeTime - base media decode time from tfdt
35902        @see ISO-BMFF-12/2015, Section 8.8.12
35903    * @param {Object} tfhd - The parsed Track Fragment Header
35904    *   @see inspect.parseTfhd
35905    * @return {Object[]} the parsed samples
35906    *
35907    * @see ISO-BMFF-12/2015, Section 8.8.8
35908   **/
35909
35910
35911  var parseSamples = function parseSamples(truns, baseMediaDecodeTime, tfhd) {
35912    var currentDts = baseMediaDecodeTime;
35913    var defaultSampleDuration = tfhd.defaultSampleDuration || 0;
35914    var defaultSampleSize = tfhd.defaultSampleSize || 0;
35915    var trackId = tfhd.trackId;
35916    var allSamples = [];
35917    truns.forEach(function (trun) {
35918      // Note: We currently do not parse the sample table as well
35919      // as the trun. It's possible some sources will require this.
35920      // moov > trak > mdia > minf > stbl
35921      var trackRun = mp4Inspector.parseTrun(trun);
35922      var samples = trackRun.samples;
35923      samples.forEach(function (sample) {
35924        if (sample.duration === undefined) {
35925          sample.duration = defaultSampleDuration;
35926        }
35927
35928        if (sample.size === undefined) {
35929          sample.size = defaultSampleSize;
35930        }
35931
35932        sample.trackId = trackId;
35933        sample.dts = currentDts;
35934
35935        if (sample.compositionTimeOffset === undefined) {
35936          sample.compositionTimeOffset = 0;
35937        }
35938
35939        sample.pts = currentDts + sample.compositionTimeOffset;
35940        currentDts += sample.duration;
35941      });
35942      allSamples = allSamples.concat(samples);
35943    });
35944    return allSamples;
35945  };
35946  /**
35947    * Parses out caption nals from an FMP4 segment's video tracks.
35948    *
35949    * @param {Uint8Array} segment - The bytes of a single segment
35950    * @param {Number} videoTrackId - The trackId of a video track in the segment
35951    * @return {Object.<Number, Object[]>} A mapping of video trackId to
35952    *   a list of seiNals found in that track
35953   **/
35954
35955
35956  var parseCaptionNals = function parseCaptionNals(segment, videoTrackId) {
35957    // To get the samples
35958    var trafs = probe.findBox(segment, ['moof', 'traf']); // To get SEI NAL units
35959
35960    var mdats = probe.findBox(segment, ['mdat']);
35961    var captionNals = {};
35962    var mdatTrafPairs = []; // Pair up each traf with a mdat as moofs and mdats are in pairs
35963
vendor: 3,771 bytes, lines 35964-36066
35964    mdats.forEach(function (mdat, index) {
35965      var matchingTraf = trafs[index];
35966      mdatTrafPairs.push({
35967        mdat: mdat,
35968        traf: matchingTraf
35969      });
35970    });
35971    mdatTrafPairs.forEach(function (pair) {
35972      var mdat = pair.mdat;
35973      var traf = pair.traf;
35974      var tfhd = probe.findBox(traf, ['tfhd']); // Exactly 1 tfhd per traf
35975
35976      var headerInfo = mp4Inspector.parseTfhd(tfhd[0]);
35977      var trackId = headerInfo.trackId;
35978      var tfdt = probe.findBox(traf, ['tfdt']); // Either 0 or 1 tfdt per traf
35979
35980      var baseMediaDecodeTime = tfdt.length > 0 ? mp4Inspector.parseTfdt(tfdt[0]).baseMediaDecodeTime : 0;
35981      var truns = probe.findBox(traf, ['trun']);
35982      var samples;
35983      var seiNals; // Only parse video data for the chosen video track
35984
35985      if (videoTrackId === trackId && truns.length > 0) {
35986        samples = parseSamples(truns, baseMediaDecodeTime, headerInfo);
35987        seiNals = findSeiNals(mdat, samples, trackId);
35988
35989        if (!captionNals[trackId]) {
35990          captionNals[trackId] = [];
35991        }
35992
35993        captionNals[trackId] = captionNals[trackId].concat(seiNals);
35994      }
35995    });
35996    return captionNals;
35997  };
35998  /**
35999    * Parses out inband captions from an MP4 container and returns
36000    * caption objects that can be used by WebVTT and the TextTrack API.
36001    * @see https://developer.mozilla.org/en-US/docs/Web/API/VTTCue
36002    * @see https://developer.mozilla.org/en-US/docs/Web/API/TextTrack
36003    * Assumes that `probe.getVideoTrackIds` and `probe.timescale` have been called first
36004    *
36005    * @param {Uint8Array} segment - The fmp4 segment containing embedded captions
36006    * @param {Number} trackId - The id of the video track to parse
36007    * @param {Number} timescale - The timescale for the video track from the init segment
36008    *
36009    * @return {?Object[]} parsedCaptions - A list of captions or null if no video tracks
36010    * @return {Number} parsedCaptions[].startTime - The time to show the caption in seconds
36011    * @return {Number} parsedCaptions[].endTime - The time to stop showing the caption in seconds
36012    * @return {String} parsedCaptions[].text - The visible content of the caption
36013   **/
36014
36015
36016  var parseEmbeddedCaptions = function parseEmbeddedCaptions(segment, trackId, timescale) {
36017    var seiNals; // the ISO-BMFF spec says that trackId can't be zero, but there's some broken content out there
36018
36019    if (trackId === null) {
36020      return null;
36021    }
36022
36023    seiNals = parseCaptionNals(segment, trackId);
36024    return {
36025      seiNals: seiNals[trackId],
36026      timescale: timescale
36027    };
36028  };
36029  /**
36030    * Converts SEI NALUs into captions that can be used by video.js
36031   **/
36032
36033
36034  var CaptionParser = function CaptionParser() {
36035    var isInitialized = false;
36036    var captionStream; // Stores segments seen before trackId and timescale are set
36037
36038    var segmentCache; // Stores video track ID of the track being parsed
36039
36040    var trackId; // Stores the timescale of the track being parsed
36041
36042    var timescale; // Stores captions parsed so far
36043
36044    var parsedCaptions; // Stores whether we are receiving partial data or not
36045
36046    var parsingPartial;
36047    /**
36048      * A method to indicate whether a CaptionParser has been initalized
36049      * @returns {Boolean}
36050     **/
36051
36052    this.isInitialized = function () {
36053      return isInitialized;
36054    };
36055    /**
36056      * Initializes the underlying CaptionStream, SEI NAL parsing
36057      * and management, and caption collection
36058     **/
36059
36060
36061    this.init = function (options) {
36062      captionStream = new CaptionStream$1();
36063      isInitialized = true;
36064      parsingPartial = options ? options.isPartial : false; // Collect dispatched captions
36065
36066      captionStream.on('data', function (event) {
vendor: 4,550 bytes, lines 36067-36216
36067        // Convert to seconds in the source's timescale
36068        event.startTime = event.startPts / timescale;
36069        event.endTime = event.endPts / timescale;
36070        parsedCaptions.captions.push(event);
36071        parsedCaptions.captionStreams[event.stream] = true;
36072      });
36073    };
36074    /**
36075      * Determines if a new video track will be selected
36076      * or if the timescale changed
36077      * @return {Boolean}
36078     **/
36079
36080
36081    this.isNewInit = function (videoTrackIds, timescales) {
36082      if (videoTrackIds && videoTrackIds.length === 0 || timescales && typeof timescales === 'object' && Object.keys(timescales).length === 0) {
36083        return false;
36084      }
36085
36086      return trackId !== videoTrackIds[0] || timescale !== timescales[trackId];
36087    };
36088    /**
36089      * Parses out SEI captions and interacts with underlying
36090      * CaptionStream to return dispatched captions
36091      *
36092      * @param {Uint8Array} segment - The fmp4 segment containing embedded captions
36093      * @param {Number[]} videoTrackIds - A list of video tracks found in the init segment
36094      * @param {Object.<Number, Number>} timescales - The timescales found in the init segment
36095      * @see parseEmbeddedCaptions
36096      * @see m2ts/caption-stream.js
36097     **/
36098
36099
36100    this.parse = function (segment, videoTrackIds, timescales) {
36101      var parsedData;
36102
36103      if (!this.isInitialized()) {
36104        return null; // This is not likely to be a video segment
36105      } else if (!videoTrackIds || !timescales) {
36106        return null;
36107      } else if (this.isNewInit(videoTrackIds, timescales)) {
36108        // Use the first video track only as there is no
36109        // mechanism to switch to other video tracks
36110        trackId = videoTrackIds[0];
36111        timescale = timescales[trackId]; // If an init segment has not been seen yet, hold onto segment
36112        // data until we have one.
36113        // the ISO-BMFF spec says that trackId can't be zero, but there's some broken content out there
36114      } else if (trackId === null || !timescale) {
36115        segmentCache.push(segment);
36116        return null;
36117      } // Now that a timescale and trackId is set, parse cached segments
36118
36119
36120      while (segmentCache.length > 0) {
36121        var cachedSegment = segmentCache.shift();
36122        this.parse(cachedSegment, videoTrackIds, timescales);
36123      }
36124
36125      parsedData = parseEmbeddedCaptions(segment, trackId, timescale);
36126
36127      if (parsedData === null || !parsedData.seiNals) {
36128        return null;
36129      }
36130
36131      this.pushNals(parsedData.seiNals); // Force the parsed captions to be dispatched
36132
36133      this.flushStream();
36134      return parsedCaptions;
36135    };
36136    /**
36137      * Pushes SEI NALUs onto CaptionStream
36138      * @param {Object[]} nals - A list of SEI nals parsed using `parseCaptionNals`
36139      * Assumes that `parseCaptionNals` has been called first
36140      * @see m2ts/caption-stream.js
36141      **/
36142
36143
36144    this.pushNals = function (nals) {
36145      if (!this.isInitialized() || !nals || nals.length === 0) {
36146        return null;
36147      }
36148
36149      nals.forEach(function (nal) {
36150        captionStream.push(nal);
36151      });
36152    };
36153    /**
36154      * Flushes underlying CaptionStream to dispatch processed, displayable captions
36155      * @see m2ts/caption-stream.js
36156     **/
36157
36158
36159    this.flushStream = function () {
36160      if (!this.isInitialized()) {
36161        return null;
36162      }
36163
36164      if (!parsingPartial) {
36165        captionStream.flush();
36166      } else {
36167        captionStream.partialFlush();
36168      }
36169    };
36170    /**
36171      * Reset caption buckets for new data
36172     **/
36173
36174
36175    this.clearParsedCaptions = function () {
36176      parsedCaptions.captions = [];
36177      parsedCaptions.captionStreams = {};
36178    };
36179    /**
36180      * Resets underlying CaptionStream
36181      * @see m2ts/caption-stream.js
36182     **/
36183
36184
36185    this.resetCaptionStream = function () {
36186      if (!this.isInitialized()) {
36187        return null;
36188      }
36189
36190      captionStream.reset();
36191    };
36192    /**
36193      * Convenience method to clear all captions flushed from the
36194      * CaptionStream and still being parsed
36195      * @see m2ts/caption-stream.js
36196     **/
36197
36198
36199    this.clearAllCaptions = function () {
36200      this.clearParsedCaptions();
36201      this.resetCaptionStream();
36202    };
36203    /**
36204      * Reset caption parser
36205     **/
36206
36207
36208    this.reset = function () {
36209      segmentCache = [];
36210      trackId = null;
36211      timescale = null;
36212
36213      if (!parsedCaptions) {
36214        parsedCaptions = {
36215          captions: [],
36216          // CC1, CC2, CC3, CC4
vendor: 7,392 bytes, lines 36217-36450
36217          captionStreams: {}
36218        };
36219      } else {
36220        this.clearParsedCaptions();
36221      }
36222
36223      this.resetCaptionStream();
36224    };
36225
36226    this.reset();
36227  };
36228
36229  var captionParser = CaptionParser;
36230
36231  /**
36232   * mux.js
36233   *
36234   * Copyright (c) Brightcove
36235   * Licensed Apache-2.0 https://github.com/videojs/mux.js/blob/master/LICENSE
36236   */
36237
36238  var streamTypes = {
36239    H264_STREAM_TYPE: 0x1B,
36240    ADTS_STREAM_TYPE: 0x0F,
36241    METADATA_STREAM_TYPE: 0x15
36242  };
36243
36244  var MAX_TS = 8589934592;
36245  var RO_THRESH = 4294967296;
36246  var TYPE_SHARED = 'shared';
36247
36248  var handleRollover = function handleRollover(value, reference) {
36249    var direction = 1;
36250
36251    if (value > reference) {
36252      // If the current timestamp value is greater than our reference timestamp and we detect a
36253      // timestamp rollover, this means the roll over is happening in the opposite direction.
36254      // Example scenario: Enter a long stream/video just after a rollover occurred. The reference
36255      // point will be set to a small number, e.g. 1. The user then seeks backwards over the
36256      // rollover point. In loading this segment, the timestamp values will be very large,
36257      // e.g. 2^33 - 1. Since this comes before the data we loaded previously, we want to adjust
36258      // the time stamp to be `value - 2^33`.
36259      direction = -1;
36260    } // Note: A seek forwards or back that is greater than the RO_THRESH (2^32, ~13 hours) will
36261    // cause an incorrect adjustment.
36262
36263
36264    while (Math.abs(reference - value) > RO_THRESH) {
36265      value += direction * MAX_TS;
36266    }
36267
36268    return value;
36269  };
36270
36271  var TimestampRolloverStream = function TimestampRolloverStream(type) {
36272    var lastDTS, referenceDTS;
36273    TimestampRolloverStream.prototype.init.call(this); // The "shared" type is used in cases where a stream will contain muxed
36274    // video and audio. We could use `undefined` here, but having a string
36275    // makes debugging a little clearer.
36276
36277    this.type_ = type || TYPE_SHARED;
36278
36279    this.push = function (data) {
36280      // Any "shared" rollover streams will accept _all_ data. Otherwise,
36281      // streams will only accept data that matches their type.
36282      if (this.type_ !== TYPE_SHARED && data.type !== this.type_) {
36283        return;
36284      }
36285
36286      if (referenceDTS === undefined) {
36287        referenceDTS = data.dts;
36288      }
36289
36290      data.dts = handleRollover(data.dts, referenceDTS);
36291      data.pts = handleRollover(data.pts, referenceDTS);
36292      lastDTS = data.dts;
36293      this.trigger('data', data);
36294    };
36295
36296    this.flush = function () {
36297      referenceDTS = lastDTS;
36298      this.trigger('done');
36299    };
36300
36301    this.endTimeline = function () {
36302      this.flush();
36303      this.trigger('endedtimeline');
36304    };
36305
36306    this.discontinuity = function () {
36307      referenceDTS = void 0;
36308      lastDTS = void 0;
36309    };
36310
36311    this.reset = function () {
36312      this.discontinuity();
36313      this.trigger('reset');
36314    };
36315  };
36316
36317  TimestampRolloverStream.prototype = new stream();
36318  var timestampRolloverStream = {
36319    TimestampRolloverStream: TimestampRolloverStream,
36320    handleRollover: handleRollover
36321  };
36322
36323  var parsePid = function parsePid(packet) {
36324    var pid = packet[1] & 0x1f;
36325    pid <<= 8;
36326    pid |= packet[2];
36327    return pid;
36328  };
36329
36330  var parsePayloadUnitStartIndicator = function parsePayloadUnitStartIndicator(packet) {
36331    return !!(packet[1] & 0x40);
36332  };
36333
36334  var parseAdaptionField = function parseAdaptionField(packet) {
36335    var offset = 0; // if an adaption field is present, its length is specified by the
36336    // fifth byte of the TS packet header. The adaptation field is
36337    // used to add stuffing to PES packets that don't fill a complete
36338    // TS packet, and to specify some forms of timing and control data
36339    // that we do not currently use.
36340
36341    if ((packet[3] & 0x30) >>> 4 > 0x01) {
36342      offset += packet[4] + 1;
36343    }
36344
36345    return offset;
36346  };
36347
36348  var parseType$2 = function parseType(packet, pmtPid) {
36349    var pid = parsePid(packet);
36350
36351    if (pid === 0) {
36352      return 'pat';
36353    } else if (pid === pmtPid) {
36354      return 'pmt';
36355    } else if (pmtPid) {
36356      return 'pes';
36357    }
36358
36359    return null;
36360  };
36361
36362  var parsePat = function parsePat(packet) {
36363    var pusi = parsePayloadUnitStartIndicator(packet);
36364    var offset = 4 + parseAdaptionField(packet);
36365
36366    if (pusi) {
36367      offset += packet[offset] + 1;
36368    }
36369
36370    return (packet[offset + 10] & 0x1f) << 8 | packet[offset + 11];
36371  };
36372
36373  var parsePmt = function parsePmt(packet) {
36374    var programMapTable = {};
36375    var pusi = parsePayloadUnitStartIndicator(packet);
36376    var payloadOffset = 4 + parseAdaptionField(packet);
36377
36378    if (pusi) {
36379      payloadOffset += packet[payloadOffset] + 1;
36380    } // PMTs can be sent ahead of the time when they should actually
36381    // take effect. We don't believe this should ever be the case
36382    // for HLS but we'll ignore "forward" PMT declarations if we see
36383    // them. Future PMT declarations have the current_next_indicator
36384    // set to zero.
36385
36386
36387    if (!(packet[payloadOffset + 5] & 0x01)) {
36388      return;
36389    }
36390
36391    var sectionLength, tableEnd, programInfoLength; // the mapping table ends at the end of the current section
36392
36393    sectionLength = (packet[payloadOffset + 1] & 0x0f) << 8 | packet[payloadOffset + 2];
36394    tableEnd = 3 + sectionLength - 4; // to determine where the table is, we have to figure out how
36395    // long the program info descriptors are
36396
36397    programInfoLength = (packet[payloadOffset + 10] & 0x0f) << 8 | packet[payloadOffset + 11]; // advance the offset to the first entry in the mapping table
36398
36399    var offset = 12 + programInfoLength;
36400
36401    while (offset < tableEnd) {
36402      var i = payloadOffset + offset; // add an entry that maps the elementary_pid to the stream_type
36403
36404      programMapTable[(packet[i + 1] & 0x1F) << 8 | packet[i + 2]] = packet[i]; // move to the next table entry
36405      // skip past the elementary stream descriptors, if present
36406
36407      offset += ((packet[i + 3] & 0x0F) << 8 | packet[i + 4]) + 5;
36408    }
36409
36410    return programMapTable;
36411  };
36412
36413  var parsePesType = function parsePesType(packet, programMapTable) {
36414    var pid = parsePid(packet);
36415    var type = programMapTable[pid];
36416
36417    switch (type) {
36418      case streamTypes.H264_STREAM_TYPE:
36419        return 'video';
36420
36421      case streamTypes.ADTS_STREAM_TYPE:
36422        return 'audio';
36423
36424      case streamTypes.METADATA_STREAM_TYPE:
36425        return 'timed-metadata';
36426
36427      default:
36428        return null;
36429    }
36430  };
36431
36432  var parsePesTime = function parsePesTime(packet) {
36433    var pusi = parsePayloadUnitStartIndicator(packet);
36434
36435    if (!pusi) {
36436      return null;
36437    }
36438
36439    var offset = 4 + parseAdaptionField(packet);
36440
36441    if (offset >= packet.byteLength) {
36442      // From the H 222.0 MPEG-TS spec
36443      // "For transport stream packets carrying PES packets, stuffing is needed when there
36444      //  is insufficient PES packet data to completely fill the transport stream packet
36445      //  payload bytes. Stuffing is accomplished by defining an adaptation field longer than
36446      //  the sum of the lengths of the data elements in it, so that the payload bytes
36447      //  remaining after the adaptation field exactly accommodates the available PES packet
36448      //  data."
36449      //
36450      // If the offset is >
vendor: 10,559 bytes, lines 36450-36777
36450= the length of the packet, then the packet contains no data
36451      // and instead is just adaption field stuffing bytes
36452      return null;
36453    }
36454
36455    var pes = null;
36456    var ptsDtsFlags; // PES packets may be annotated with a PTS value, or a PTS value
36457    // and a DTS value. Determine what combination of values is
36458    // available to work with.
36459
36460    ptsDtsFlags = packet[offset + 7]; // PTS and DTS are normally stored as a 33-bit number.  Javascript
36461    // performs all bitwise operations on 32-bit integers but javascript
36462    // supports a much greater range (52-bits) of integer using standard
36463    // mathematical operations.
36464    // We construct a 31-bit value using bitwise operators over the 31
36465    // most significant bits and then multiply by 4 (equal to a left-shift
36466    // of 2) before we add the final 2 least significant bits of the
36467    // timestamp (equal to an OR.)
36468
36469    if (ptsDtsFlags & 0xC0) {
36470      pes = {}; // the PTS and DTS are not written out directly. For information
36471      // on how they are encoded, see
36472      // http://dvd.sourceforge.net/dvdinfo/pes-hdr.html
36473
36474      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;
36475      pes.pts *= 4; // Left shift by 2
36476
36477      pes.pts += (packet[offset + 13] & 0x06) >>> 1; // OR by the two LSBs
36478
36479      pes.dts = pes.pts;
36480
36481      if (ptsDtsFlags & 0x40) {
36482        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;
36483        pes.dts *= 4; // Left shift by 2
36484
36485        pes.dts += (packet[offset + 18] & 0x06) >>> 1; // OR by the two LSBs
36486      }
36487    }
36488
36489    return pes;
36490  };
36491
36492  var parseNalUnitType = function parseNalUnitType(type) {
36493    switch (type) {
36494      case 0x05:
36495        return 'slice_layer_without_partitioning_rbsp_idr';
36496
36497      case 0x06:
36498        return 'sei_rbsp';
36499
36500      case 0x07:
36501        return 'seq_parameter_set_rbsp';
36502
36503      case 0x08:
36504        return 'pic_parameter_set_rbsp';
36505
36506      case 0x09:
36507        return 'access_unit_delimiter_rbsp';
36508
36509      default:
36510        return null;
36511    }
36512  };
36513
36514  var videoPacketContainsKeyFrame = function videoPacketContainsKeyFrame(packet) {
36515    var offset = 4 + parseAdaptionField(packet);
36516    var frameBuffer = packet.subarray(offset);
36517    var frameI = 0;
36518    var frameSyncPoint = 0;
36519    var foundKeyFrame = false;
36520    var nalType; // advance the sync point to a NAL start, if necessary
36521
36522    for (; frameSyncPoint < frameBuffer.byteLength - 3; frameSyncPoint++) {
36523      if (frameBuffer[frameSyncPoint + 2] === 1) {
36524        // the sync point is properly aligned
36525        frameI = frameSyncPoint + 5;
36526        break;
36527      }
36528    }
36529
36530    while (frameI < frameBuffer.byteLength) {
36531      // look at the current byte to determine if we've hit the end of
36532      // a NAL unit boundary
36533      switch (frameBuffer[frameI]) {
36534        case 0:
36535          // skip past non-sync sequences
36536          if (frameBuffer[frameI - 1] !== 0) {
36537            frameI += 2;
36538            break;
36539          } else if (frameBuffer[frameI - 2] !== 0) {
36540            frameI++;
36541            break;
36542          }
36543
36544          if (frameSyncPoint + 3 !== frameI - 2) {
36545            nalType = parseNalUnitType(frameBuffer[frameSyncPoint + 3] & 0x1f);
36546
36547            if (nalType === 'slice_layer_without_partitioning_rbsp_idr') {
36548              foundKeyFrame = true;
36549            }
36550          } // drop trailing zeroes
36551
36552
36553          do {
36554            frameI++;
36555          } while (frameBuffer[frameI] !== 1 && frameI < frameBuffer.length);
36556
36557          frameSyncPoint = frameI - 2;
36558          frameI += 3;
36559          break;
36560
36561        case 1:
36562          // skip past non-sync sequences
36563          if (frameBuffer[frameI - 1] !== 0 || frameBuffer[frameI - 2] !== 0) {
36564            frameI += 3;
36565            break;
36566          }
36567
36568          nalType = parseNalUnitType(frameBuffer[frameSyncPoint + 3] & 0x1f);
36569
36570          if (nalType === 'slice_layer_without_partitioning_rbsp_idr') {
36571            foundKeyFrame = true;
36572          }
36573
36574          frameSyncPoint = frameI - 2;
36575          frameI += 3;
36576          break;
36577
36578        default:
36579          // the current byte isn't a one or zero, so it cannot be part
36580          // of a sync sequence
36581          frameI += 3;
36582          break;
36583      }
36584    }
36585
36586    frameBuffer = frameBuffer.subarray(frameSyncPoint);
36587    frameI -= frameSyncPoint;
36588    frameSyncPoint = 0; // parse the final nal
36589
36590    if (frameBuffer && frameBuffer.byteLength > 3) {
36591      nalType = parseNalUnitType(frameBuffer[frameSyncPoint + 3] & 0x1f);
36592
36593      if (nalType === 'slice_layer_without_partitioning_rbsp_idr') {
36594        foundKeyFrame = true;
36595      }
36596    }
36597
36598    return foundKeyFrame;
36599  };
36600
36601  var probe$1 = {
36602    parseType: parseType$2,
36603    parsePat: parsePat,
36604    parsePmt: parsePmt,
36605    parsePayloadUnitStartIndicator: parsePayloadUnitStartIndicator,
36606    parsePesType: parsePesType,
36607    parsePesTime: parsePesTime,
36608    videoPacketContainsKeyFrame: videoPacketContainsKeyFrame
36609  };
36610
36611  /**
36612   * mux.js
36613   *
36614   * Copyright (c) Brightcove
36615   * Licensed Apache-2.0 https://github.com/videojs/mux.js/blob/master/LICENSE
36616   *
36617   * Utilities to detect basic properties and metadata about Aac data.
36618   */
36619
36620  var ADTS_SAMPLING_FREQUENCIES = [96000, 88200, 64000, 48000, 44100, 32000, 24000, 22050, 16000, 12000, 11025, 8000, 7350];
36621
36622  var isLikelyAacData = function isLikelyAacData(data) {
36623    if (data[0] === 'I'.charCodeAt(0) && data[1] === 'D'.charCodeAt(0) && data[2] === '3'.charCodeAt(0)) {
36624      return true;
36625    }
36626
36627    return false;
36628  };
36629
36630  var parseSyncSafeInteger = function parseSyncSafeInteger(data) {
36631    return data[0] << 21 | data[1] << 14 | data[2] << 7 | data[3];
36632  }; // return a percent-encoded representation of the specified byte range
36633  // @see http://en.wikipedia.org/wiki/Percent-encoding
36634
36635
36636  var percentEncode = function percentEncode(bytes, start, end) {
36637    var i,
36638        result = '';
36639
36640    for (i = start; i < end; i++) {
36641      result += '%' + ('00' + bytes[i].toString(16)).slice(-2);
36642    }
36643
36644    return result;
36645  }; // return the string representation of the specified byte range,
36646  // interpreted as ISO-8859-1.
36647
36648
36649  var parseIso88591 = function parseIso88591(bytes, start, end) {
36650    return unescape(percentEncode(bytes, start, end)); // jshint ignore:line
36651  };
36652
36653  var parseId3TagSize = function parseId3TagSize(header, byteIndex) {
36654    var returnSize = header[byteIndex + 6] << 21 | header[byteIndex + 7] << 14 | header[byteIndex + 8] << 7 | header[byteIndex + 9],
36655        flags = header[byteIndex + 5],
36656        footerPresent = (flags & 16) >> 4;
36657
36658    if (footerPresent) {
36659      return returnSize + 20;
36660    }
36661
36662    return returnSize + 10;
36663  };
36664
36665  var parseAdtsSize = function parseAdtsSize(header, byteIndex) {
36666    var lowThree = (header[byteIndex + 5] & 0xE0) >> 5,
36667        middle = header[byteIndex + 4] << 3,
36668        highTwo = header[byteIndex + 3] & 0x3 << 11;
36669    return highTwo | middle | lowThree;
36670  };
36671
36672  var parseType$3 = function parseType(header, byteIndex) {
36673    if (header[byteIndex] === 'I'.charCodeAt(0) && header[byteIndex + 1] === 'D'.charCodeAt(0) && header[byteIndex + 2] === '3'.charCodeAt(0)) {
36674      return 'timed-metadata';
36675    } else if (header[byteIndex] & 0xff === 0xff && (header[byteIndex + 1] & 0xf0) === 0xf0) {
36676      return 'audio';
36677    }
36678
36679    return null;
36680  };
36681
36682  var parseSampleRate = function parseSampleRate(packet) {
36683    var i = 0;
36684
36685    while (i + 5 < packet.length) {
36686      if (packet[i] !== 0xFF || (packet[i + 1] & 0xF6) !== 0xF0) {
36687        // If a valid header was not found,  jump one forward and attempt to
36688        // find a valid ADTS header starting at the next byte
36689        i++;
36690        continue;
36691      }
36692
36693      return ADTS_SAMPLING_FREQUENCIES[(packet[i + 2] & 0x3c) >>> 2];
36694    }
36695
36696    return null;
36697  };
36698
36699  var parseAacTimestamp = function parseAacTimestamp(packet) {
36700    var frameStart, frameSize, frame, frameHeader; // find the start of the first frame and the end of the tag
36701
36702    frameStart = 10;
36703
36704    if (packet[5] & 0x40) {
36705      // advance the frame start past the extended header
36706      frameStart += 4; // header size field
36707
36708      frameStart += parseSyncSafeInteger(packet.subarray(10, 14));
36709    } // parse one or more ID3 frames
36710    // http://id3.org/id3v2.3.0#ID3v2_frame_overview
36711
36712
36713    do {
36714      // determine the number of bytes in this frame
36715      frameSize = parseSyncSafeInteger(packet.subarray(frameStart + 4, frameStart + 8));
36716
36717      if (frameSize < 1) {
36718        return null;
36719      }
36720
36721      frameHeader = String.fromCharCode(packet[frameStart], packet[frameStart + 1], packet[frameStart + 2], packet[frameStart + 3]);
36722
36723      if (frameHeader === 'PRIV') {
36724        frame = packet.subarray(frameStart + 10, frameStart + frameSize + 10);
36725
36726        for (var i = 0; i < frame.byteLength; i++) {
36727          if (frame[i] === 0) {
36728            var owner = parseIso88591(frame, 0, i);
36729
36730            if (owner === 'com.apple.streaming.transportStreamTimestamp') {
36731              var d = frame.subarray(i + 1);
36732              var size = (d[3] & 0x01) << 30 | d[4] << 22 | d[5] << 14 | d[6] << 6 | d[7] >>> 2;
36733              size *= 4;
36734              size += d[7] & 0x03;
36735              return size;
36736            }
36737
36738            break;
36739          }
36740        }
36741      }
36742
36743      frameStart += 10; // advance past the frame header
36744
36745      frameStart += frameSize; // advance past the frame body
36746    } while (frameStart < packet.byteLength);
36747
36748    return null;
36749  };
36750
36751  var utils = {
36752    isLikelyAacData: isLikelyAacData,
36753    parseId3TagSize: parseId3TagSize,
36754    parseAdtsSize: parseAdtsSize,
36755    parseType: parseType$3,
36756    parseSampleRate: parseSampleRate,
36757    parseAacTimestamp: parseAacTimestamp
36758  };
36759
36760  /**
36761   * mux.js
36762   *
36763   * Copyright (c) Brightcove
36764   * Licensed Apache-2.0 https://github.com/videojs/mux.js/blob/master/LICENSE
36765   */
36766  var ONE_SECOND_IN_TS = 90000,
36767      // 90kHz clock
36768  secondsToVideoTs,
36769      secondsToAudioTs,
36770      videoTsToSeconds,
36771      audioTsToSeconds,
36772      audioTsToVideoTs,
36773      videoTsToAudioTs,
36774      metadataTsToSeconds;
36775
36776  secondsToVideoTs = function secondsToVideoTs(seconds) {
36777    return seconds * ONE_SECOND_IN_TS;
vendor: 10,932 bytes, lines 36778-37117
36778  };
36779
36780  secondsToAudioTs = function secondsToAudioTs(seconds, sampleRate) {
36781    return seconds * sampleRate;
36782  };
36783
36784  videoTsToSeconds = function videoTsToSeconds(timestamp) {
36785    return timestamp / ONE_SECOND_IN_TS;
36786  };
36787
36788  audioTsToSeconds = function audioTsToSeconds(timestamp, sampleRate) {
36789    return timestamp / sampleRate;
36790  };
36791
36792  audioTsToVideoTs = function audioTsToVideoTs(timestamp, sampleRate) {
36793    return secondsToVideoTs(audioTsToSeconds(timestamp, sampleRate));
36794  };
36795
36796  videoTsToAudioTs = function videoTsToAudioTs(timestamp, sampleRate) {
36797    return secondsToAudioTs(videoTsToSeconds(timestamp), sampleRate);
36798  };
36799  /**
36800   * Adjust ID3 tag or caption timing information by the timeline pts values
36801   * (if keepOriginalTimestamps is false) and convert to seconds
36802   */
36803
36804
36805  metadataTsToSeconds = function metadataTsToSeconds(timestamp, timelineStartPts, keepOriginalTimestamps) {
36806    return videoTsToSeconds(keepOriginalTimestamps ? timestamp : timestamp - timelineStartPts);
36807  };
36808
36809  var clock = {
36810    ONE_SECOND_IN_TS: ONE_SECOND_IN_TS,
36811    secondsToVideoTs: secondsToVideoTs,
36812    secondsToAudioTs: secondsToAudioTs,
36813    videoTsToSeconds: videoTsToSeconds,
36814    audioTsToSeconds: audioTsToSeconds,
36815    audioTsToVideoTs: audioTsToVideoTs,
36816    videoTsToAudioTs: videoTsToAudioTs,
36817    metadataTsToSeconds: metadataTsToSeconds
36818  };
36819
36820  var handleRollover$1 = timestampRolloverStream.handleRollover;
36821  var probe$2 = {};
36822  probe$2.ts = probe$1;
36823  probe$2.aac = utils;
36824  var ONE_SECOND_IN_TS$1 = clock.ONE_SECOND_IN_TS;
36825  var MP2T_PACKET_LENGTH = 188,
36826      // bytes
36827  SYNC_BYTE = 0x47;
36828  /**
36829   * walks through segment data looking for pat and pmt packets to parse out
36830   * program map table information
36831   */
36832
36833  var parsePsi_ = function parsePsi_(bytes, pmt) {
36834    var startIndex = 0,
36835        endIndex = MP2T_PACKET_LENGTH,
36836        packet,
36837        type;
36838
36839    while (endIndex < bytes.byteLength) {
36840      // Look for a pair of start and end sync bytes in the data..
36841      if (bytes[startIndex] === SYNC_BYTE && bytes[endIndex] === SYNC_BYTE) {
36842        // We found a packet
36843        packet = bytes.subarray(startIndex, endIndex);
36844        type = probe$2.ts.parseType(packet, pmt.pid);
36845
36846        switch (type) {
36847          case 'pat':
36848            if (!pmt.pid) {
36849              pmt.pid = probe$2.ts.parsePat(packet);
36850            }
36851
36852            break;
36853
36854          case 'pmt':
36855            if (!pmt.table) {
36856              pmt.table = probe$2.ts.parsePmt(packet);
36857            }
36858
36859            break;
36860
36861          default:
36862            break;
36863        } // Found the pat and pmt, we can stop walking the segment
36864
36865
36866        if (pmt.pid && pmt.table) {
36867          return;
36868        }
36869
36870        startIndex += MP2T_PACKET_LENGTH;
36871        endIndex += MP2T_PACKET_LENGTH;
36872        continue;
36873      } // If we get here, we have somehow become de-synchronized and we need to step
36874      // forward one byte at a time until we find a pair of sync bytes that denote
36875      // a packet
36876
36877
36878      startIndex++;
36879      endIndex++;
36880    }
36881  };
36882  /**
36883   * walks through the segment data from the start and end to get timing information
36884   * for the first and last audio pes packets
36885   */
36886
36887
36888  var parseAudioPes_ = function parseAudioPes_(bytes, pmt, result) {
36889    var startIndex = 0,
36890        endIndex = MP2T_PACKET_LENGTH,
36891        packet,
36892        type,
36893        pesType,
36894        pusi,
36895        parsed;
36896    var endLoop = false; // Start walking from start of segment to get first audio packet
36897
36898    while (endIndex <= bytes.byteLength) {
36899      // Look for a pair of start and end sync bytes in the data..
36900      if (bytes[startIndex] === SYNC_BYTE && (bytes[endIndex] === SYNC_BYTE || endIndex === bytes.byteLength)) {
36901        // We found a packet
36902        packet = bytes.subarray(startIndex, endIndex);
36903        type = probe$2.ts.parseType(packet, pmt.pid);
36904
36905        switch (type) {
36906          case 'pes':
36907            pesType = probe$2.ts.parsePesType(packet, pmt.table);
36908            pusi = probe$2.ts.parsePayloadUnitStartIndicator(packet);
36909
36910            if (pesType === 'audio' && pusi) {
36911              parsed = probe$2.ts.parsePesTime(packet);
36912
36913              if (parsed) {
36914                parsed.type = 'audio';
36915                result.audio.push(parsed);
36916                endLoop = true;
36917              }
36918            }
36919
36920            break;
36921
36922          default:
36923            break;
36924        }
36925
36926        if (endLoop) {
36927          break;
36928        }
36929
36930        startIndex += MP2T_PACKET_LENGTH;
36931        endIndex += MP2T_PACKET_LENGTH;
36932        continue;
36933      } // If we get here, we have somehow become de-synchronized and we need to step
36934      // forward one byte at a time until we find a pair of sync bytes that denote
36935      // a packet
36936
36937
36938      startIndex++;
36939      endIndex++;
36940    } // Start walking from end of segment to get last audio packet
36941
36942
36943    endIndex = bytes.byteLength;
36944    startIndex = endIndex - MP2T_PACKET_LENGTH;
36945    endLoop = false;
36946
36947    while (startIndex >= 0) {
36948      // Look for a pair of start and end sync bytes in the data..
36949      if (bytes[startIndex] === SYNC_BYTE && (bytes[endIndex] === SYNC_BYTE || endIndex === bytes.byteLength)) {
36950        // We found a packet
36951        packet = bytes.subarray(startIndex, endIndex);
36952        type = probe$2.ts.parseType(packet, pmt.pid);
36953
36954        switch (type) {
36955          case 'pes':
36956            pesType = probe$2.ts.parsePesType(packet, pmt.table);
36957            pusi = probe$2.ts.parsePayloadUnitStartIndicator(packet);
36958
36959            if (pesType === 'audio' && pusi) {
36960              parsed = probe$2.ts.parsePesTime(packet);
36961
36962              if (parsed) {
36963                parsed.type = 'audio';
36964                result.audio.push(parsed);
36965                endLoop = true;
36966              }
36967            }
36968
36969            break;
36970
36971          default:
36972            break;
36973        }
36974
36975        if (endLoop) {
36976          break;
36977        }
36978
36979        startIndex -= MP2T_PACKET_LENGTH;
36980        endIndex -= MP2T_PACKET_LENGTH;
36981        continue;
36982      } // If we get here, we have somehow become de-synchronized and we need to step
36983      // forward one byte at a time until we find a pair of sync bytes that denote
36984      // a packet
36985
36986
36987      startIndex--;
36988      endIndex--;
36989    }
36990  };
36991  /**
36992   * walks through the segment data from the start and end to get timing information
36993   * for the first and last video pes packets as well as timing information for the first
36994   * key frame.
36995   */
36996
36997
36998  var parseVideoPes_ = function parseVideoPes_(bytes, pmt, result) {
36999    var startIndex = 0,
37000        endIndex = MP2T_PACKET_LENGTH,
37001        packet,
37002        type,
37003        pesType,
37004        pusi,
37005        parsed,
37006        frame,
37007        i,
37008        pes;
37009    var endLoop = false;
37010    var currentFrame = {
37011      data: [],
37012      size: 0
37013    }; // Start walking from start of segment to get first video packet
37014
37015    while (endIndex < bytes.byteLength) {
37016      // Look for a pair of start and end sync bytes in the data..
37017      if (bytes[startIndex] === SYNC_BYTE && bytes[endIndex] === SYNC_BYTE) {
37018        // We found a packet
37019        packet = bytes.subarray(startIndex, endIndex);
37020        type = probe$2.ts.parseType(packet, pmt.pid);
37021
37022        switch (type) {
37023          case 'pes':
37024            pesType = probe$2.ts.parsePesType(packet, pmt.table);
37025            pusi = probe$2.ts.parsePayloadUnitStartIndicator(packet);
37026
37027            if (pesType === 'video') {
37028              if (pusi && !endLoop) {
37029                parsed = probe$2.ts.parsePesTime(packet);
37030
37031                if (parsed) {
37032                  parsed.type = 'video';
37033                  result.video.push(parsed);
37034                  endLoop = true;
37035                }
37036              }
37037
37038              if (!result.firstKeyFrame) {
37039                if (pusi) {
37040                  if (currentFrame.size !== 0) {
37041                    frame = new Uint8Array(currentFrame.size);
37042                    i = 0;
37043
37044                    while (currentFrame.data.length) {
37045                      pes = currentFrame.data.shift();
37046                      frame.set(pes, i);
37047                      i += pes.byteLength;
37048                    }
37049
37050                    if (probe$2.ts.videoPacketContainsKeyFrame(frame)) {
37051                      var firstKeyFrame = probe$2.ts.parsePesTime(frame); // PTS/DTS may not be available. Simply *not* setting
37052                      // the keyframe seems to work fine with HLS playback
37053                      // and definitely preferable to a crash with TypeError...
37054
37055                      if (firstKeyFrame) {
37056                        result.firstKeyFrame = firstKeyFrame;
37057                        result.firstKeyFrame.type = 'video';
37058                      } else {
37059                        // eslint-disable-next-line
37060                        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.');
37061                      }
37062                    }
37063
37064                    currentFrame.size = 0;
37065                  }
37066                }
37067
37068                currentFrame.data.push(packet);
37069                currentFrame.size += packet.byteLength;
37070              }
37071            }
37072
37073            break;
37074
37075          default:
37076            break;
37077        }
37078
37079        if (endLoop && result.firstKeyFrame) {
37080          break;
37081        }
37082
37083        startIndex += MP2T_PACKET_LENGTH;
37084        endIndex += MP2T_PACKET_LENGTH;
37085        continue;
37086      } // If we get here, we have somehow become de-synchronized and we need to step
37087      // forward one byte at a time until we find a pair of sync bytes that denote
37088      // a packet
37089
37090
37091      startIndex++;
37092      endIndex++;
37093    } // Start walking from end of segment to get last video packet
37094
37095
37096    endIndex = bytes.byteLength;
37097    startIndex = endIndex - MP2T_PACKET_LENGTH;
37098    endLoop = false;
37099
37100    while (startIndex >= 0) {
37101      // Look for a pair of start and end sync bytes in the data..
37102      if (bytes[startIndex] === SYNC_BYTE && bytes[endIndex] === SYNC_BYTE) {
37103        // We found a packet
37104        packet = bytes.subarray(startIndex, endIndex);
37105        type = probe$2.ts.parseType(packet, pmt.pid);
37106
37107        switch (type) {
37108          case 'pes':
37109            pesType = probe$2.ts.parsePesType(packet, pmt.table);
37110            pusi = probe$2.ts.parsePayloadUnitStartIndicator(packet);
37111
37112            if (pesType === 'video' && pusi) {
37113              parsed = probe$2.ts.parsePesTime(packet);
37114
37115              if (parsed) {
37116                parsed.type = 'video';
37117                result.video.push(parsed);
vendor: 42,122 bytes, lines 37118-38413
37118                endLoop = true;
37119              }
37120            }
37121
37122            break;
37123
37124          default:
37125            break;
37126        }
37127
37128        if (endLoop) {
37129          break;
37130        }
37131
37132        startIndex -= MP2T_PACKET_LENGTH;
37133        endIndex -= MP2T_PACKET_LENGTH;
37134        continue;
37135      } // If we get here, we have somehow become de-synchronized and we need to step
37136      // forward one byte at a time until we find a pair of sync bytes that denote
37137      // a packet
37138
37139
37140      startIndex--;
37141      endIndex--;
37142    }
37143  };
37144  /**
37145   * Adjusts the timestamp information for the segment to account for
37146   * rollover and convert to seconds based on pes packet timescale (90khz clock)
37147   */
37148
37149
37150  var adjustTimestamp_ = function adjustTimestamp_(segmentInfo, baseTimestamp) {
37151    if (segmentInfo.audio && segmentInfo.audio.length) {
37152      var audioBaseTimestamp = baseTimestamp;
37153
37154      if (typeof audioBaseTimestamp === 'undefined') {
37155        audioBaseTimestamp = segmentInfo.audio[0].dts;
37156      }
37157
37158      segmentInfo.audio.forEach(function (info) {
37159        info.dts = handleRollover$1(info.dts, audioBaseTimestamp);
37160        info.pts = handleRollover$1(info.pts, audioBaseTimestamp); // time in seconds
37161
37162        info.dtsTime = info.dts / ONE_SECOND_IN_TS$1;
37163        info.ptsTime = info.pts / ONE_SECOND_IN_TS$1;
37164      });
37165    }
37166
37167    if (segmentInfo.video && segmentInfo.video.length) {
37168      var videoBaseTimestamp = baseTimestamp;
37169
37170      if (typeof videoBaseTimestamp === 'undefined') {
37171        videoBaseTimestamp = segmentInfo.video[0].dts;
37172      }
37173
37174      segmentInfo.video.forEach(function (info) {
37175        info.dts = handleRollover$1(info.dts, videoBaseTimestamp);
37176        info.pts = handleRollover$1(info.pts, videoBaseTimestamp); // time in seconds
37177
37178        info.dtsTime = info.dts / ONE_SECOND_IN_TS$1;
37179        info.ptsTime = info.pts / ONE_SECOND_IN_TS$1;
37180      });
37181
37182      if (segmentInfo.firstKeyFrame) {
37183        var frame = segmentInfo.firstKeyFrame;
37184        frame.dts = handleRollover$1(frame.dts, videoBaseTimestamp);
37185        frame.pts = handleRollover$1(frame.pts, videoBaseTimestamp); // time in seconds
37186
37187        frame.dtsTime = frame.dts / ONE_SECOND_IN_TS$1;
37188        frame.ptsTime = frame.dts / ONE_SECOND_IN_TS$1;
37189      }
37190    }
37191  };
37192  /**
37193   * inspects the aac data stream for start and end time information
37194   */
37195
37196
37197  var inspectAac_ = function inspectAac_(bytes) {
37198    var endLoop = false,
37199        audioCount = 0,
37200        sampleRate = null,
37201        timestamp = null,
37202        frameSize = 0,
37203        byteIndex = 0,
37204        packet;
37205
37206    while (bytes.length - byteIndex >= 3) {
37207      var type = probe$2.aac.parseType(bytes, byteIndex);
37208
37209      switch (type) {
37210        case 'timed-metadata':
37211          // Exit early because we don't have enough to parse
37212          // the ID3 tag header
37213          if (bytes.length - byteIndex < 10) {
37214            endLoop = true;
37215            break;
37216          }
37217
37218          frameSize = probe$2.aac.parseId3TagSize(bytes, byteIndex); // Exit early if we don't have enough in the buffer
37219          // to emit a full packet
37220
37221          if (frameSize > bytes.length) {
37222            endLoop = true;
37223            break;
37224          }
37225
37226          if (timestamp === null) {
37227            packet = bytes.subarray(byteIndex, byteIndex + frameSize);
37228            timestamp = probe$2.aac.parseAacTimestamp(packet);
37229          }
37230
37231          byteIndex += frameSize;
37232          break;
37233
37234        case 'audio':
37235          // Exit early because we don't have enough to parse
37236          // the ADTS frame header
37237          if (bytes.length - byteIndex < 7) {
37238            endLoop = true;
37239            break;
37240          }
37241
37242          frameSize = probe$2.aac.parseAdtsSize(bytes, byteIndex); // Exit early if we don't have enough in the buffer
37243          // to emit a full packet
37244
37245          if (frameSize > bytes.length) {
37246            endLoop = true;
37247            break;
37248          }
37249
37250          if (sampleRate === null) {
37251            packet = bytes.subarray(byteIndex, byteIndex + frameSize);
37252            sampleRate = probe$2.aac.parseSampleRate(packet);
37253          }
37254
37255          audioCount++;
37256          byteIndex += frameSize;
37257          break;
37258
37259        default:
37260          byteIndex++;
37261          break;
37262      }
37263
37264      if (endLoop) {
37265        return null;
37266      }
37267    }
37268
37269    if (sampleRate === null || timestamp === null) {
37270      return null;
37271    }
37272
37273    var audioTimescale = ONE_SECOND_IN_TS$1 / sampleRate;
37274    var result = {
37275      audio: [{
37276        type: 'audio',
37277        dts: timestamp,
37278        pts: timestamp
37279      }, {
37280        type: 'audio',
37281        dts: timestamp + audioCount * 1024 * audioTimescale,
37282        pts: timestamp + audioCount * 1024 * audioTimescale
37283      }]
37284    };
37285    return result;
37286  };
37287  /**
37288   * inspects the transport stream segment data for start and end time information
37289   * of the audio and video tracks (when present) as well as the first key frame's
37290   * start time.
37291   */
37292
37293
37294  var inspectTs_ = function inspectTs_(bytes) {
37295    var pmt = {
37296      pid: null,
37297      table: null
37298    };
37299    var result = {};
37300    parsePsi_(bytes, pmt);
37301
37302    for (var pid in pmt.table) {
37303      if (pmt.table.hasOwnProperty(pid)) {
37304        var type = pmt.table[pid];
37305
37306        switch (type) {
37307          case streamTypes.H264_STREAM_TYPE:
37308            result.video = [];
37309            parseVideoPes_(bytes, pmt, result);
37310
37311            if (result.video.length === 0) {
37312              delete result.video;
37313            }
37314
37315            break;
37316
37317          case streamTypes.ADTS_STREAM_TYPE:
37318            result.audio = [];
37319            parseAudioPes_(bytes, pmt, result);
37320
37321            if (result.audio.length === 0) {
37322              delete result.audio;
37323            }
37324
37325            break;
37326
37327          default:
37328            break;
37329        }
37330      }
37331    }
37332
37333    return result;
37334  };
37335  /**
37336   * Inspects segment byte data and returns an object with start and end timing information
37337   *
37338   * @param {Uint8Array} bytes The segment byte data
37339   * @param {Number} baseTimestamp Relative reference timestamp used when adjusting frame
37340   *  timestamps for rollover. This value must be in 90khz clock.
37341   * @return {Object} Object containing start and end frame timing info of segment.
37342   */
37343
37344
37345  var inspect = function inspect(bytes, baseTimestamp) {
37346    var isAacData = probe$2.aac.isLikelyAacData(bytes);
37347    var result;
37348
37349    if (isAacData) {
37350      result = inspectAac_(bytes);
37351    } else {
37352      result = inspectTs_(bytes);
37353    }
37354
37355    if (!result || !result.audio && !result.video) {
37356      return null;
37357    }
37358
37359    adjustTimestamp_(result, baseTimestamp);
37360    return result;
37361  };
37362
37363  var tsInspector = {
37364    inspect: inspect,
37365    parseAudioPes_: parseAudioPes_
37366  };
37367
37368  /*
37369   * pkcs7.pad
37370   * https://github.com/brightcove/pkcs7
37371   *
37372   * Copyright (c) 2014 Brightcove
37373   * Licensed under the apache2 license.
37374   */
37375  /**
37376   * Returns the subarray of a Uint8Array without PKCS#7 padding.
37377   * @param padded {Uint8Array} unencrypted bytes that have been padded
37378   * @return {Uint8Array} the unpadded bytes
37379   * @see http://tools.ietf.org/html/rfc5652
37380   */
37381
37382  function unpad(padded) {
37383    return padded.subarray(0, padded.byteLength - padded[padded.byteLength - 1]);
37384  }
37385
37386  var classCallCheck = function classCallCheck(instance, Constructor) {
37387    if (!(instance instanceof Constructor)) {
37388      throw new TypeError("Cannot call a class as a function");
37389    }
37390  };
37391
37392  var createClass = function () {
37393    function defineProperties(target, props) {
37394      for (var i = 0; i < props.length; i++) {
37395        var descriptor = props[i];
37396        descriptor.enumerable = descriptor.enumerable || false;
37397        descriptor.configurable = true;
37398        if ("value" in descriptor) descriptor.writable = true;
37399        Object.defineProperty(target, descriptor.key, descriptor);
37400      }
37401    }
37402
37403    return function (Constructor, protoProps, staticProps) {
37404      if (protoProps) defineProperties(Constructor.prototype, protoProps);
37405      if (staticProps) defineProperties(Constructor, staticProps);
37406      return Constructor;
37407    };
37408  }();
37409
37410  var inherits = function inherits(subClass, superClass) {
37411    if (typeof superClass !== "function" && superClass !== null) {
37412      throw new TypeError("Super expression must either be null or a function, not " + typeof superClass);
37413    }
37414
37415    subClass.prototype = Object.create(superClass && superClass.prototype, {
37416      constructor: {
37417        value: subClass,
37418        enumerable: false,
37419        writable: true,
37420        configurable: true
37421      }
37422    });
37423    if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass;
37424  };
37425
37426  var possibleConstructorReturn = function possibleConstructorReturn(self, call) {
37427    if (!self) {
37428      throw new ReferenceError("this hasn't been initialised - super() hasn't been called");
37429    }
37430
37431    return call && (typeof call === "object" || typeof call === "function") ? call : self;
37432  };
37433  /**
37434   * @file aes.js
37435   *
37436   * This file contains an adaptation of the AES decryption algorithm
37437   * from the Standford Javascript Cryptography Library. That work is
37438   * covered by the following copyright and permissions notice:
37439   *
37440   * Copyright 2009-2010 Emily Stark, Mike Hamburg, Dan Boneh.
37441   * All rights reserved.
37442   *
37443   * Redistribution and use in source and binary forms, with or without
37444   * modification, are permitted provided that the following conditions are
37445   * met:
37446   *
37447   * 1. Redistributions of source code must retain the above copyright
37448   *    notice, this list of conditions and the following disclaimer.
37449   *
37450   * 2. Redistributions in binary form must reproduce the above
37451   *    copyright notice, this list of conditions and the following
37452   *    disclaimer in the documentation and/or other materials provided
37453   *    with the distribution.
37454   *
37455   * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR
37456   * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
37457   * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
37458   * DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> OR CONTRIBUTORS BE
37459   * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
37460   * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
37461   * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
37462   * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
37463   * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
37464   * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
37465   * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
37466   *
37467   * The views and conclusions contained in the software and documentation
37468   * are those of the authors and should not be interpreted as representing
37469   * official policies, either expressed or implied, of the authors.
37470   */
37471
37472  /**
37473   * Expand the S-box tables.
37474   *
37475   * @private
37476   */
37477
37478
37479  var precompute = function precompute() {
37480    var tables = [[[], [], [], [], []], [[], [], [], [], []]];
37481    var encTable = tables[0];
37482    var decTable = tables[1];
37483    var sbox = encTable[4];
37484    var sboxInv = decTable[4];
37485    var i = void 0;
37486    var x = void 0;
37487    var xInv = void 0;
37488    var d = [];
37489    var th = [];
37490    var x2 = void 0;
37491    var x4 = void 0;
37492    var x8 = void 0;
37493    var s = void 0;
37494    var tEnc = void 0;
37495    var tDec = void 0; // Compute double and third tables
37496
37497    for (i = 0; i < 256; i++) {
37498      th[(d[i] = i << 1 ^ (i >> 7) * 283) ^ i] = i;
37499    }
37500
37501    for (x = xInv = 0; !sbox[x]; x ^= x2 || 1, xInv = th[xInv] || 1) {
37502      // Compute sbox
37503      s = xInv ^ xInv << 1 ^ xInv << 2 ^ xInv << 3 ^ xInv << 4;
37504      s = s >> 8 ^ s & 255 ^ 99;
37505      sbox[x] = s;
37506      sboxInv[s] = x; // Compute MixColumns
37507
37508      x8 = d[x4 = d[x2 = d[x]]];
37509      tDec = x8 * 0x1010101 ^ x4 * 0x10001 ^ x2 * 0x101 ^ x * 0x1010100;
37510      tEnc = d[s] * 0x101 ^ s * 0x1010100;
37511
37512      for (i = 0; i < 4; i++) {
37513        encTable[i][x] = tEnc = tEnc << 24 ^ tEnc >>> 8;
37514        decTable[i][s] = tDec = tDec << 24 ^ tDec >>> 8;
37515      }
37516    } // Compactify. Considerable speedup on Firefox.
37517
37518
37519    for (i = 0; i < 5; i++) {
37520      encTable[i] = encTable[i].slice(0);
37521      decTable[i] = decTable[i].slice(0);
37522    }
37523
37524    return tables;
37525  };
37526
37527  var aesTables = null;
37528  /**
37529   * Schedule out an AES key for both encryption and decryption. This
37530   * is a low-level class. Use a cipher mode to do bulk encryption.
37531   *
37532   * @class AES
37533   * @param key {Array} The key as an array of 4, 6 or 8 words.
37534   */
37535
37536  var AES = function () {
37537    function AES(key) {
37538      classCallCheck(this, AES);
37539      /**
37540       * The expanded S-box and inverse S-box tables. These will be computed
37541       * on the client so that we don't have to send them down the wire.
37542       *
37543       * There are two tables, _tables[0] is for encryption and
37544       * _tables[1] is for decryption.
37545       *
37546       * The first 4 sub-tables are the expanded S-box with MixColumns. The
37547       * last (_tables[01][4]) is the S-box itself.
37548       *
37549       * @private
37550       */
37551      // if we have yet to precompute the S-box tables
37552      // do so now
37553
37554      if (!aesTables) {
37555        aesTables = precompute();
37556      } // then make a copy of that object for use
37557
37558
37559      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()]];
37560      var i = void 0;
37561      var j = void 0;
37562      var tmp = void 0;
37563      var encKey = void 0;
37564      var decKey = void 0;
37565      var sbox = this._tables[0][4];
37566      var decTable = this._tables[1];
37567      var keyLen = key.length;
37568      var rcon = 1;
37569
37570      if (keyLen !== 4 && keyLen !== 6 && keyLen !== 8) {
37571        throw new Error('Invalid aes key size');
37572      }
37573
37574      encKey = key.slice(0);
37575      decKey = [];
37576      this._key = [encKey, decKey]; // schedule encryption keys
37577
37578      for (i = keyLen; i < 4 * keyLen + 28; i++) {
37579        tmp = encKey[i - 1]; // apply sbox
37580
37581        if (i % keyLen === 0 || keyLen === 8 && i % keyLen === 4) {
37582          tmp = sbox[tmp >>> 24] << 24 ^ sbox[tmp >> 16 & 255] << 16 ^ sbox[tmp >> 8 & 255] << 8 ^ sbox[tmp & 255]; // shift rows and add rcon
37583
37584          if (i % keyLen === 0) {
37585            tmp = tmp << 8 ^ tmp >>> 24 ^ rcon << 24;
37586            rcon = rcon << 1 ^ (rcon >> 7) * 283;
37587          }
37588        }
37589
37590        encKey[i] = encKey[i - keyLen] ^ tmp;
37591      } // schedule decryption keys
37592
37593
37594      for (j = 0; i; j++, i--) {
37595        tmp = encKey[j & 3 ? i : i - 4];
37596
37597        if (i <= 4 || j < 4) {
37598          decKey[j] = tmp;
37599        } else {
37600          decKey[j] = decTable[0][sbox[tmp >>> 24]] ^ decTable[1][sbox[tmp >> 16 & 255]] ^ decTable[2][sbox[tmp >> 8 & 255]] ^ decTable[3][sbox[tmp & 255]];
37601        }
37602      }
37603    }
37604    /**
37605     * Decrypt 16 bytes, specified as four 32-bit words.
37606     *
37607     * @param {Number} encrypted0 the first word to decrypt
37608     * @param {Number} encrypted1 the second word to decrypt
37609     * @param {Number} encrypted2 the third word to decrypt
37610     * @param {Number} encrypted3 the fourth word to decrypt
37611     * @param {Int32Array} out the array to write the decrypted words
37612     * into
37613     * @param {Number} offset the offset into the output array to start
37614     * writing results
37615     * @return {Array} The plaintext.
37616     */
37617
37618
37619    AES.prototype.decrypt = function decrypt(encrypted0, encrypted1, encrypted2, encrypted3, out, offset) {
37620      var key = this._key[1]; // state variables a,b,c,d are loaded with pre-whitened data
37621
37622      var a = encrypted0 ^ key[0];
37623      var b = encrypted3 ^ key[1];
37624      var c = encrypted2 ^ key[2];
37625      var d = encrypted1 ^ key[3];
37626      var a2 = void 0;
37627      var b2 = void 0;
37628      var c2 = void 0; // key.length === 2 ?
37629
37630      var nInnerRounds = key.length / 4 - 2;
37631      var i = void 0;
37632      var kIndex = 4;
37633      var table = this._tables[1]; // load up the tables
37634
37635      var table0 = table[0];
37636      var table1 = table[1];
37637      var table2 = table[2];
37638      var table3 = table[3];
37639      var sbox = table[4]; // Inner rounds. Cribbed from OpenSSL.
37640
37641      for (i = 0; i < nInnerRounds; i++) {
37642        a2 = table0[a >>> 24] ^ table1[b >> 16 & 255] ^ table2[c >> 8 & 255] ^ table3[d & 255] ^ key[kIndex];
37643        b2 = table0[b >>> 24] ^ table1[c >> 16 & 255] ^ table2[d >> 8 & 255] ^ table3[a & 255] ^ key[kIndex + 1];
37644        c2 = table0[c >>> 24] ^ table1[d >> 16 & 255] ^ table2[a >> 8 & 255] ^ table3[b & 255] ^ key[kIndex + 2];
37645        d = table0[d >>> 24] ^ table1[a >> 16 & 255] ^ table2[b >> 8 & 255] ^ table3[c & 255] ^ key[kIndex + 3];
37646        kIndex += 4;
37647        a = a2;
37648        b = b2;
37649        c = c2;
37650      } // Last round.
37651
37652
37653      for (i = 0; i < 4; i++) {
37654        out[(3 & -i) + offset] = sbox[a >>> 24] << 24 ^ sbox[b >> 16 & 255] << 16 ^ sbox[c >> 8 & 255] << 8 ^ sbox[d & 255] ^ key[kIndex++];
37655        a2 = a;
37656        a = b;
37657        b = c;
37658        c = d;
37659        d = a2;
37660      }
37661    };
37662
37663    return AES;
37664  }();
37665  /**
37666   * @file stream.js
37667   */
37668
37669  /**
37670   * A lightweight readable stream implemention that handles event dispatching.
37671   *
37672   * @class Stream
37673   */
37674
37675
37676  var Stream$2 = function () {
37677    function Stream() {
37678      classCallCheck(this, Stream);
37679      this.listeners = {};
37680    }
37681    /**
37682     * Add a listener for a specified event type.
37683     *
37684     * @param {String} type the event name
37685     * @param {Function} listener the callback to be invoked when an event of
37686     * the specified type occurs
37687     */
37688
37689
37690    Stream.prototype.on = function on(type, listener) {
37691      if (!this.listeners[type]) {
37692        this.listeners[type] = [];
37693      }
37694
37695      this.listeners[type].push(listener);
37696    };
37697    /**
37698     * Remove a listener for a specified event type.
37699     *
37700     * @param {String} type the event name
37701     * @param {Function} listener  a function previously registered for this
37702     * type of event through `on`
37703     * @return {Boolean} if we could turn it off or not
37704     */
37705
37706
37707    Stream.prototype.off = function off(type, listener) {
37708      if (!this.listeners[type]) {
37709        return false;
37710      }
37711
37712      var index = this.listeners[type].indexOf(listener);
37713      this.listeners[type].splice(index, 1);
37714      return index > -1;
37715    };
37716    /**
37717     * Trigger an event of the specified type on this stream. Any additional
37718     * arguments to this function are passed as parameters to event listeners.
37719     *
37720     * @param {String} type the event name
37721     */
37722
37723
37724    Stream.prototype.trigger = function trigger(type) {
37725      var callbacks = this.listeners[type];
37726
37727      if (!callbacks) {
37728        return;
37729      } // Slicing the arguments on every invocation of this method
37730      // can add a significant amount of overhead. Avoid the
37731      // intermediate object creation for the common case of a
37732      // single callback argument
37733
37734
37735      if (arguments.length === 2) {
37736        var length = callbacks.length;
37737
37738        for (var i = 0; i < length; ++i) {
37739          callbacks[i].call(this, arguments[1]);
37740        }
37741      } else {
37742        var args = Array.prototype.slice.call(arguments, 1);
37743        var _length = callbacks.length;
37744
37745        for (var _i = 0; _i < _length; ++_i) {
37746          callbacks[_i].apply(this, args);
37747        }
37748      }
37749    };
37750    /**
37751     * Destroys the stream and cleans up.
37752     */
37753
37754
37755    Stream.prototype.dispose = function dispose() {
37756      this.listeners = {};
37757    };
37758    /**
37759     * Forwards all `data` events on this stream to the destination stream. The
37760     * destination stream should provide a method `push` to receive the data
37761     * events as they arrive.
37762     *
37763     * @param {Stream} destination the stream that will receive all `data` events
37764     * @see http://nodejs.org/api/stream.html#stream_readable_pipe_destination_options
37765     */
37766
37767
37768    Stream.prototype.pipe = function pipe(destination) {
37769      this.on('data', function (data) {
37770        destination.push(data);
37771      });
37772    };
37773
37774    return Stream;
37775  }();
37776  /**
37777   * @file async-stream.js
37778   */
37779
37780  /**
37781   * A wrapper around the Stream class to use setTiemout
37782   * and run stream "jobs" Asynchronously
37783   *
37784   * @class AsyncStream
37785   * @extends Stream
37786   */
37787
37788
37789  var AsyncStream = function (_Stream) {
37790    inherits(AsyncStream, _Stream);
37791
37792    function AsyncStream() {
37793      classCallCheck(this, AsyncStream);
37794
37795      var _this = possibleConstructorReturn(this, _Stream.call(this, Stream$2));
37796
37797      _this.jobs = [];
37798      _this.delay = 1;
37799      _this.timeout_ = null;
37800      return _this;
37801    }
37802    /**
37803     * process an async job
37804     *
37805     * @private
37806     */
37807
37808
37809    AsyncStream.prototype.processJob_ = function processJob_() {
37810      this.jobs.shift()();
37811
37812      if (this.jobs.length) {
37813        this.timeout_ = setTimeout(this.processJob_.bind(this), this.delay);
37814      } else {
37815        this.timeout_ = null;
37816      }
37817    };
37818    /**
37819     * push a job into the stream
37820     *
37821     * @param {Function} job the job to push into the stream
37822     */
37823
37824
37825    AsyncStream.prototype.push = function push(job) {
37826      this.jobs.push(job);
37827
37828      if (!this.timeout_) {
37829        this.timeout_ = setTimeout(this.processJob_.bind(this), this.delay);
37830      }
37831    };
37832
37833    return AsyncStream;
37834  }(Stream$2);
37835  /**
37836   * @file decrypter.js
37837   *
37838   * An asynchronous implementation of AES-128 CBC decryption with
37839   * PKCS#7 padding.
37840   */
37841
37842  /**
37843   * Convert network-order (big-endian) bytes into their little-endian
37844   * representation.
37845   */
37846
37847
37848  var ntoh = function ntoh(word) {
37849    return word << 24 | (word & 0xff00) << 8 | (word & 0xff0000) >> 8 | word >>> 24;
37850  };
37851  /**
37852   * Decrypt bytes using AES-128 with CBC and PKCS#7 padding.
37853   *
37854   * @param {Uint8Array} encrypted the encrypted bytes
37855   * @param {Uint32Array} key the bytes of the decryption key
37856   * @param {Uint32Array} initVector the initialization vector (IV) to
37857   * use for the first round of CBC.
37858   * @return {Uint8Array} the decrypted bytes
37859   *
37860   * @see http://en.wikipedia.org/wiki/Advanced_Encryption_Standard
37861   * @see http://en.wikipedia.org/wiki/Block_cipher_mode_of_operation#Cipher_Block_Chaining_.28CBC.29
37862   * @see https://tools.ietf.org/html/rfc2315
37863   */
37864
37865
37866  var decrypt = function decrypt(encrypted, key, initVector) {
37867    // word-level access to the encrypted bytes
37868    var encrypted32 = new Int32Array(encrypted.buffer, encrypted.byteOffset, encrypted.byteLength >> 2);
37869    var decipher = new AES(Array.prototype.slice.call(key)); // byte and word-level access for the decrypted output
37870
37871    var decrypted = new Uint8Array(encrypted.byteLength);
37872    var decrypted32 = new Int32Array(decrypted.buffer); // temporary variables for working with the IV, encrypted, and
37873    // decrypted data
37874
37875    var init0 = void 0;
37876    var init1 = void 0;
37877    var init2 = void 0;
37878    var init3 = void 0;
37879    var encrypted0 = void 0;
37880    var encrypted1 = void 0;
37881    var encrypted2 = void 0;
37882    var encrypted3 = void 0; // iteration variable
37883
37884    var wordIx = void 0; // pull out the words of the IV to ensure we don't modify the
37885    // passed-in reference and easier access
37886
37887    init0 = initVector[0];
37888    init1 = initVector[1];
37889    init2 = initVector[2];
37890    init3 = initVector[3]; // decrypt four word sequences, applying cipher-block chaining (CBC)
37891    // to each decrypted block
37892
37893    for (wordIx = 0; wordIx < encrypted32.length; wordIx += 4) {
37894      // convert big-endian (network order) words into little-endian
37895      // (javascript order)
37896      encrypted0 = ntoh(encrypted32[wordIx]);
37897      encrypted1 = ntoh(encrypted32[wordIx + 1]);
37898      encrypted2 = ntoh(encrypted32[wordIx + 2]);
37899      encrypted3 = ntoh(encrypted32[wordIx + 3]); // decrypt the block
37900
37901      decipher.decrypt(encrypted0, encrypted1, encrypted2, encrypted3, decrypted32, wordIx); // XOR with the IV, and restore network byte-order to obtain the
37902      // plaintext
37903
37904      decrypted32[wordIx] = ntoh(decrypted32[wordIx] ^ init0);
37905      decrypted32[wordIx + 1] = ntoh(decrypted32[wordIx + 1] ^ init1);
37906      decrypted32[wordIx + 2] = ntoh(decrypted32[wordIx + 2] ^ init2);
37907      decrypted32[wordIx + 3] = ntoh(decrypted32[wordIx + 3] ^ init3); // setup the IV for the next round
37908
37909      init0 = encrypted0;
37910      init1 = encrypted1;
37911      init2 = encrypted2;
37912      init3 = encrypted3;
37913    }
37914
37915    return decrypted;
37916  };
37917  /**
37918   * The `Decrypter` class that manages decryption of AES
37919   * data through `AsyncStream` objects and the `decrypt`
37920   * function
37921   *
37922   * @param {Uint8Array} encrypted the encrypted bytes
37923   * @param {Uint32Array} key the bytes of the decryption key
37924   * @param {Uint32Array} initVector the initialization vector (IV) to
37925   * @param {Function} done the function to run when done
37926   * @class Decrypter
37927   */
37928
37929
37930  var Decrypter = function () {
37931    function Decrypter(encrypted, key, initVector, done) {
37932      classCallCheck(this, Decrypter);
37933      var step = Decrypter.STEP;
37934      var encrypted32 = new Int32Array(encrypted.buffer);
37935      var decrypted = new Uint8Array(encrypted.byteLength);
37936      var i = 0;
37937      this.asyncStream_ = new AsyncStream(); // split up the encryption job and do the individual chunks asynchronously
37938
37939      this.asyncStream_.push(this.decryptChunk_(encrypted32.subarray(i, i + step), key, initVector, decrypted));
37940
37941      for (i = step; i < encrypted32.length; i += step) {
37942        initVector = new Uint32Array([ntoh(encrypted32[i - 4]), ntoh(encrypted32[i - 3]), ntoh(encrypted32[i - 2]), ntoh(encrypted32[i - 1])]);
37943        this.asyncStream_.push(this.decryptChunk_(encrypted32.subarray(i, i + step), key, initVector, decrypted));
37944      } // invoke the done() callback when everything is finished
37945
37946
37947      this.asyncStream_.push(function () {
37948        // remove pkcs#7 padding from the decrypted bytes
37949        done(null, unpad(decrypted));
37950      });
37951    }
37952    /**
37953     * a getter for step the maximum number of bytes to process at one time
37954     *
37955     * @return {Number} the value of step 32000
37956     */
37957
37958    /**
37959     * @private
37960     */
37961
37962
37963    Decrypter.prototype.decryptChunk_ = function decryptChunk_(encrypted, key, initVector, decrypted) {
37964      return function () {
37965        var bytes = decrypt(encrypted, key, initVector);
37966        decrypted.set(bytes, encrypted.byteOffset);
37967      };
37968    };
37969
37970    createClass(Decrypter, null, [{
37971      key: 'STEP',
37972      get: function get$$1() {
37973        // 4 * 8000;
37974        return 32000;
37975      }
37976    }]);
37977    return Decrypter;
37978  }();
37979
37980  /**
37981   * @videojs/http-streaming
37982   * @version 1.10.6
37983   * @copyright 2019 Brightcove, Inc
37984   * @license Apache-2.0
37985   */
37986  /**
37987   * @file resolve-url.js - Handling how URLs are resolved and manipulated
37988   */
37989
37990  var resolveUrl$1 = function resolveUrl(baseURL, relativeURL) {
37991    // return early if we don't need to resolve
37992    if (/^[a-z]+:/i.test(relativeURL)) {
37993      return relativeURL;
37994    } // if the base URL is relative then combine with the current location
37995
37996
37997    if (!/\/\//i.test(baseURL)) {
37998      baseURL = urlToolkit.buildAbsoluteURL(window$1.location.href, baseURL);
37999    }
38000
38001    return urlToolkit.buildAbsoluteURL(baseURL, relativeURL);
38002  };
38003  /**
38004   * Checks whether xhr request was redirected and returns correct url depending
38005   * on `handleManifestRedirects` option
38006   *
38007   * @api private
38008   *
38009   * @param  {String} url - an url being requested
38010   * @param  {XMLHttpRequest} req - xhr request result
38011   *
38012   * @return {String}
38013   */
38014
38015
38016  var resolveManifestRedirect = function resolveManifestRedirect(handleManifestRedirect, url, req) {
38017    // To understand how the responseURL below is set and generated:
38018    // - https://fetch.spec.whatwg.org/#concept-response-url
38019    // - https://fetch.spec.whatwg.org/#atomic-http-redirect-handling
38020    if (handleManifestRedirect && req.responseURL && url !== req.responseURL) {
38021      return req.responseURL;
38022    }
38023
38024    return url;
38025  };
38026
38027  var classCallCheck$1 = function classCallCheck(instance, Constructor) {
38028    if (!(instance instanceof Constructor)) {
38029      throw new TypeError("Cannot call a class as a function");
38030    }
38031  };
38032
38033  var createClass$1 = function () {
38034    function defineProperties(target, props) {
38035      for (var i = 0; i < props.length; i++) {
38036        var descriptor = props[i];
38037        descriptor.enumerable = descriptor.enumerable || false;
38038        descriptor.configurable = true;
38039        if ("value" in descriptor) descriptor.writable = true;
38040        Object.defineProperty(target, descriptor.key, descriptor);
38041      }
38042    }
38043
38044    return function (Constructor, protoProps, staticProps) {
38045      if (protoProps) defineProperties(Constructor.prototype, protoProps);
38046      if (staticProps) defineProperties(Constructor, staticProps);
38047      return Constructor;
38048    };
38049  }();
38050
38051  var get$1 = function get(object, property, receiver) {
38052    if (object === null) object = Function.prototype;
38053    var desc = Object.getOwnPropertyDescriptor(object, property);
38054
38055    if (desc === undefined) {
38056      var parent = Object.getPrototypeOf(object);
38057
38058      if (parent === null) {
38059        return undefined;
38060      } else {
38061        return get(parent, property, receiver);
38062      }
38063    } else if ("value" in desc) {
38064      return desc.value;
38065    } else {
38066      var getter = desc.get;
38067
38068      if (getter === undefined) {
38069        return undefined;
38070      }
38071
38072      return getter.call(receiver);
38073    }
38074  };
38075
38076  var inherits$1 = function inherits(subClass, superClass) {
38077    if (typeof superClass !== "function" && superClass !== null) {
38078      throw new TypeError("Super expression must either be null or a function, not " + typeof superClass);
38079    }
38080
38081    subClass.prototype = Object.create(superClass && superClass.prototype, {
38082      constructor: {
38083        value: subClass,
38084        enumerable: false,
38085        writable: true,
38086        configurable: true
38087      }
38088    });
38089    if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass;
38090  };
38091
38092  var possibleConstructorReturn$1 = function possibleConstructorReturn(self, call) {
38093    if (!self) {
38094      throw new ReferenceError("this hasn't been initialised - super() hasn't been called");
38095    }
38096
38097    return call && (typeof call === "object" || typeof call === "function") ? call : self;
38098  };
38099
38100  var slicedToArray = function () {
38101    function sliceIterator(arr, i) {
38102      var _arr = [];
38103      var _n = true;
38104      var _d = false;
38105      var _e = undefined;
38106
38107      try {
38108        for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) {
38109          _arr.push(_s.value);
38110
38111          if (i && _arr.length === i) break;
38112        }
38113      } catch (err) {
38114        _d = true;
38115        _e = err;
38116      } finally {
38117        try {
38118          if (!_n && _i["return"]) _i["return"]();
38119        } finally {
38120          if (_d) throw _e;
38121        }
38122      }
38123
38124      return _arr;
38125    }
38126
38127    return function (arr, i) {
38128      if (Array.isArray(arr)) {
38129        return arr;
38130      } else if (Symbol.iterator in Object(arr)) {
38131        return sliceIterator(arr, i);
38132      } else {
38133        throw new TypeError("Invalid attempt to destructure non-iterable instance");
38134      }
38135    };
38136  }();
38137  /**
38138   * @file playlist-loader.js
38139   *
38140   * A state machine that manages the loading, caching, and updating of
38141   * M3U8 playlists.
38142   *
38143   */
38144
38145
38146  var mergeOptions$1 = videojs$1.mergeOptions,
38147      EventTarget$1 = videojs$1.EventTarget,
38148      log$1 = videojs$1.log;
38149  /**
38150   * Loops through all supported media groups in master and calls the provided
38151   * callback for each group
38152   *
38153   * @param {Object} master
38154   *        The parsed master manifest object
38155   * @param {Function} callback
38156   *        Callback to call for each media group
38157   */
38158
38159  var forEachMediaGroup = function forEachMediaGroup(master, callback) {
38160    ['AUDIO', 'SUBTITLES'].forEach(function (mediaType) {
38161      for (var groupKey in master.mediaGroups[mediaType]) {
38162        for (var labelKey in master.mediaGroups[mediaType][groupKey]) {
38163          var mediaProperties = master.mediaGroups[mediaType][groupKey][labelKey];
38164          callback(mediaProperties, mediaType, groupKey, labelKey);
38165        }
38166      }
38167    });
38168  };
38169  /**
38170    * Returns a new array of segments that is the result of merging
38171    * properties from an older list of segments onto an updated
38172    * list. No properties on the updated playlist will be overridden.
38173    *
38174    * @param {Array} original the outdated list of segments
38175    * @param {Array} update the updated list of segments
38176    * @param {Number=} offset the index of the first update
38177    * segment in the original segment list. For non-live playlists,
38178    * this should always be zero and does not need to be
38179    * specified. For live playlists, it should be the difference
38180    * between the media sequence numbers in the original and updated
38181    * playlists.
38182    * @return a list of merged segment objects
38183    */
38184
38185
38186  var updateSegments = function updateSegments(original, update, offset) {
38187    var result = update.slice();
38188    offset = offset || 0;
38189    var length = Math.min(original.length, update.length + offset);
38190
38191    for (var i = offset; i < length; i++) {
38192      result[i - offset] = mergeOptions$1(original[i], result[i - offset]);
38193    }
38194
38195    return result;
38196  };
38197
38198  var resolveSegmentUris = function resolveSegmentUris(segment, baseUri) {
38199    if (!segment.resolvedUri) {
38200      segment.resolvedUri = resolveUrl$1(baseUri, segment.uri);
38201    }
38202
38203    if (segment.key && !segment.key.resolvedUri) {
38204      segment.key.resolvedUri = resolveUrl$1(baseUri, segment.key.uri);
38205    }
38206
38207    if (segment.map && !segment.map.resolvedUri) {
38208      segment.map.resolvedUri = resolveUrl$1(baseUri, segment.map.uri);
38209    }
38210  };
38211  /**
38212    * Returns a new master playlist that is the result of merging an
38213    * updated media playlist into the original version. If the
38214    * updated media playlist does not match any of the playlist
38215    * entries in the original master playlist, null is returned.
38216    *
38217    * @param {Object} master a parsed master M3U8 object
38218    * @param {Object} media a parsed media M3U8 object
38219    * @return {Object} a new object that represents the original
38220    * master playlist with the updated media playlist merged in, or
38221    * null if the merge produced no change.
38222    */
38223
38224
38225  var updateMaster = function updateMaster(master, media) {
38226    var result = mergeOptions$1(master, {});
38227    var playlist = result.playlists[media.uri];
38228
38229    if (!playlist) {
38230      return null;
38231    } // consider the playlist unchanged if the number of segments is equal, the media
38232    // sequence number is unchanged, and this playlist hasn't become the end of the playlist
38233
38234
38235    if (playlist.segments && media.segments && playlist.segments.length === media.segments.length && playlist.endList === media.endList && playlist.mediaSequence === media.mediaSequence) {
38236      return null;
38237    }
38238
38239    var mergedPlaylist = mergeOptions$1(playlist, media); // if the update could overlap existing segment information, merge the two segment lists
38240
38241    if (playlist.segments) {
38242      mergedPlaylist.segments = updateSegments(playlist.segments, media.segments, media.mediaSequence - playlist.mediaSequence);
38243    } // resolve any segment URIs to prevent us from having to do it later
38244
38245
38246    mergedPlaylist.segments.forEach(function (segment) {
38247      resolveSegmentUris(segment, mergedPlaylist.resolvedUri);
38248    }); // TODO Right now in the playlists array there are two references to each playlist, one
38249    // that is referenced by index, and one by URI. The index reference may no longer be
38250    // necessary.
38251
38252    for (var i = 0; i < result.playlists.length; i++) {
38253      if (result.playlists[i].uri === media.uri) {
38254        result.playlists[i] = mergedPlaylist;
38255      }
38256    }
38257
38258    result.playlists[media.uri] = mergedPlaylist;
38259    return result;
38260  };
38261
38262  var setupMediaPlaylists = function setupMediaPlaylists(master) {
38263    // setup by-URI lookups and resolve media playlist URIs
38264    var i = master.playlists.length;
38265
38266    while (i--) {
38267      var playlist = master.playlists[i];
38268      master.playlists[playlist.uri] = playlist;
38269      playlist.resolvedUri = resolveUrl$1(master.uri, playlist.uri);
38270      playlist.id = i;
38271
38272      if (!playlist.attributes) {
38273        // Although the spec states an #EXT-X-STREAM-INF tag MUST have a
38274        // BANDWIDTH attribute, we can play the stream without it. This means a poorly
38275        // formatted master playlist may not have an attribute list. An attributes
38276        // property is added here to prevent undefined references when we encounter
38277        // this scenario.
38278        playlist.attributes = {};
38279        log$1.warn('Invalid playlist STREAM-INF detected. Missing BANDWIDTH attribute.');
38280      }
38281    }
38282  };
38283
38284  var resolveMediaGroupUris = function resolveMediaGroupUris(master) {
38285    forEachMediaGroup(master, function (properties) {
38286      if (properties.uri) {
38287        properties.resolvedUri = resolveUrl$1(master.uri, properties.uri);
38288      }
38289    });
38290  };
38291  /**
38292   * Calculates the time to wait before refreshing a live playlist
38293   *
38294   * @param {Object} media
38295   *        The current media
38296   * @param {Boolean} update
38297   *        True if there were any updates from the last refresh, false otherwise
38298   * @return {Number}
38299   *         The time in ms to wait before refreshing the live playlist
38300   */
38301
38302
38303  var refreshDelay = function refreshDelay(media, update) {
38304    var lastSegment = media.segments[media.segments.length - 1];
38305    var delay = void 0;
38306
38307    if (update && lastSegment && lastSegment.duration) {
38308      delay = lastSegment.duration * 1000;
38309    } else {
38310      // if the playlist is unchanged since the last reload or last segment duration
38311      // cannot be determined, try again after half the target duration
38312      delay = (media.targetDuration || 10) * 500;
38313    }
38314
38315    return delay;
38316  };
38317  /**
38318   * Load a playlist from a remote location
38319   *
38320   * @class PlaylistLoader
38321   * @extends Stream
38322   * @param {String} srcUrl the url to start with
38323   * @param {Boolean} withCredentials the withCredentials xhr option
38324   * @constructor
38325   */
38326
38327
38328  var PlaylistLoader = function (_EventTarget) {
38329    inherits$1(PlaylistLoader, _EventTarget);
38330
38331    function PlaylistLoader(srcUrl, hls) {
38332      var options = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
38333      classCallCheck$1(this, PlaylistLoader);
38334
38335      var _this = possibleConstructorReturn$1(this, (PlaylistLoader.__proto__ || Object.getPrototypeOf(PlaylistLoader)).call(this));
38336
38337      var _options$withCredenti = options.withCredentials,
38338          withCredentials = _options$withCredenti === undefined ? false : _options$withCredenti,
38339          _options$handleManife = options.handleManifestRedirects,
38340          handleManifestRedirects = _options$handleManife === undefined ? false : _options$handleManife;
38341      _this.srcUrl = srcUrl;
38342      _this.hls_ = hls;
38343      _this.withCredentials = withCredentials;
38344      _this.handleManifestRedirects = handleManifestRedirects;
38345      var hlsOptions = hls.options_;
38346      _this.customTagParsers = hlsOptions && hlsOptions.customTagParsers || [];
38347      _this.customTagMappers = hlsOptions && hlsOptions.customTagMappers || [];
38348
38349      if (!_this.srcUrl) {
38350        throw new Error('A non-empty playlist URL is required');
38351      } // initialize the loader state
38352
38353
38354      _this.state = 'HAVE_NOTHING'; // live playlist staleness timeout
38355
38356      _this.on('mediaupdatetimeout', function () {
38357        if (_this.state !== 'HAVE_METADATA') {
38358          // only refresh the media playlist if no other activity is going on
38359          return;
38360        }
38361
38362        _this.state = 'HAVE_CURRENT_METADATA';
38363        _this.request = _this.hls_.xhr({
38364          uri: resolveUrl$1(_this.master.uri, _this.media().uri),
38365          withCredentials: _this.withCredentials
38366        }, function (error, req) {
38367          // disposed
38368          if (!_this.request) {
38369            return;
38370          }
38371
38372          if (error) {
38373            return _this.playlistRequestError(_this.request, _this.media().uri, 'HAVE_METADATA');
38374          }
38375
38376          _this.haveMetadata(_this.request, _this.media().uri);
38377        });
38378      });
38379
38380      return _this;
38381    }
38382
38383    createClass$1(PlaylistLoader, [{
38384      key: 'playlistRequestError',
38385      value: function playlistRequestError(xhr, url, startingState) {
38386        // any in-flight request is now finished
38387        this.request = null;
38388
38389        if (startingState) {
38390          this.state = startingState;
38391        }
38392
38393        this.error = {
38394          playlist: this.master.playlists[url],
38395          status: xhr.status,
38396          message: 'HLS playlist request error at URL: ' + url + '.',
38397          responseText: xhr.responseText,
38398          code: xhr.status >= 500 ? 4 : 2
38399        };
38400        this.trigger('error');
38401      } // update the playlist loader's state in response to a new or
38402      // updated playlist.
38403
38404    }, {
38405      key: 'haveMetadata',
38406      value: function haveMetadata(xhr, url) {
38407        var _this2 = this; // any in-flight request is now finished
38408
38409
38410        this.request = null;
38411        this.state = 'HAVE_METADATA';
38412        var parser = new Parser(); // adding custom tag parsers
38413
vendor: 5,588 bytes, lines 38414-38578
38414        this.customTagParsers.forEach(function (customParser) {
38415          return parser.addParser(customParser);
38416        }); // adding custom tag mappers
38417
38418        this.customTagMappers.forEach(function (mapper) {
38419          return parser.addTagMapper(mapper);
38420        });
38421        parser.push(xhr.responseText);
38422        parser.end();
38423        parser.manifest.uri = url; // m3u8-parser does not attach an attributes property to media playlists so make
38424        // sure that the property is attached to avoid undefined reference errors
38425
38426        parser.manifest.attributes = parser.manifest.attributes || {}; // merge this playlist into the master
38427
38428        var update = updateMaster(this.master, parser.manifest);
38429        this.targetDuration = parser.manifest.targetDuration;
38430
38431        if (update) {
38432          this.master = update;
38433          this.media_ = this.master.playlists[parser.manifest.uri];
38434        } else {
38435          this.trigger('playlistunchanged');
38436        } // refresh live playlists after a target duration passes
38437
38438
38439        if (!this.media().endList) {
38440          window$1.clearTimeout(this.mediaUpdateTimeout);
38441          this.mediaUpdateTimeout = window$1.setTimeout(function () {
38442            _this2.trigger('mediaupdatetimeout');
38443          }, refreshDelay(this.media(), !!update));
38444        }
38445
38446        this.trigger('loadedplaylist');
38447      }
38448      /**
38449       * Abort any outstanding work and clean up.
38450       */
38451
38452    }, {
38453      key: 'dispose',
38454      value: function dispose() {
38455        this.stopRequest();
38456        window$1.clearTimeout(this.mediaUpdateTimeout);
38457        window$1.clearTimeout(this.finalRenditionTimeout);
38458      }
38459    }, {
38460      key: 'stopRequest',
38461      value: function stopRequest() {
38462        if (this.request) {
38463          var oldRequest = this.request;
38464          this.request = null;
38465          oldRequest.onreadystatechange = null;
38466          oldRequest.abort();
38467        }
38468      }
38469      /**
38470       * When called without any arguments, returns the currently
38471       * active media playlist. When called with a single argument,
38472       * triggers the playlist loader to asynchronously switch to the
38473       * specified media playlist. Calling this method while the
38474       * loader is in the HAVE_NOTHING causes an error to be emitted
38475       * but otherwise has no effect.
38476       *
38477       * @param {Object=} playlist the parsed media playlist
38478       * object to switch to
38479       * @param {Boolean=} is this the last available playlist
38480       *
38481       * @return {Playlist} the current loaded media
38482       */
38483
38484    }, {
38485      key: 'media',
38486      value: function media(playlist, isFinalRendition) {
38487        var _this3 = this; // getter
38488
38489
38490        if (!playlist) {
38491          return this.media_;
38492        } // setter
38493
38494
38495        if (this.state === 'HAVE_NOTHING') {
38496          throw new Error('Cannot switch media playlist from ' + this.state);
38497        } // find the playlist object if the target playlist has been
38498        // specified by URI
38499
38500
38501        if (typeof playlist === 'string') {
38502          if (!this.master.playlists[playlist]) {
38503            throw new Error('Unknown playlist URI: ' + playlist);
38504          }
38505
38506          playlist = this.master.playlists[playlist];
38507        }
38508
38509        window$1.clearTimeout(this.finalRenditionTimeout);
38510
38511        if (isFinalRendition) {
38512          var delay = playlist.targetDuration / 2 * 1000 || 5 * 1000;
38513          this.finalRenditionTimeout = window$1.setTimeout(this.media.bind(this, playlist, false), delay);
38514          return;
38515        }
38516
38517        var startingState = this.state;
38518        var mediaChange = !this.media_ || playlist.uri !== this.media_.uri; // switch to fully loaded playlists immediately
38519
38520        if (this.master.playlists[playlist.uri].endList) {
38521          // abort outstanding playlist requests
38522          if (this.request) {
38523            this.request.onreadystatechange = null;
38524            this.request.abort();
38525            this.request = null;
38526          }
38527
38528          this.state = 'HAVE_METADATA';
38529          this.media_ = playlist; // trigger media change if the active media has been updated
38530
38531          if (mediaChange) {
38532            this.trigger('mediachanging');
38533            this.trigger('mediachange');
38534          }
38535
38536          return;
38537        } // switching to the active playlist is a no-op
38538
38539
38540        if (!mediaChange) {
38541          return;
38542        }
38543
38544        this.state = 'SWITCHING_MEDIA'; // there is already an outstanding playlist request
38545
38546        if (this.request) {
38547          if (playlist.resolvedUri === this.request.url) {
38548            // requesting to switch to the same playlist multiple times
38549            // has no effect after the first
38550            return;
38551          }
38552
38553          this.request.onreadystatechange = null;
38554          this.request.abort();
38555          this.request = null;
38556        } // request the new playlist
38557
38558
38559        if (this.media_) {
38560          this.trigger('mediachanging');
38561        }
38562
38563        this.request = this.hls_.xhr({
38564          uri: playlist.resolvedUri,
38565          withCredentials: this.withCredentials
38566        }, function (error, req) {
38567          // disposed
38568          if (!_this3.request) {
38569            return;
38570          }
38571
38572          playlist.resolvedUri = resolveManifestRedirect(_this3.handleManifestRedirects, playlist.resolvedUri, req);
38573
38574          if (error) {
38575            return _this3.playlistRequestError(_this3.request, playlist.uri, startingState);
38576          }
38577
38578          _this3.haveMetadata(req, playlist.uri);
vendor: 29,113 bytes, lines 38578-39517
38578 // fire loadedmetadata the first time a media playlist is loaded
38579
38580
38581          if (startingState === 'HAVE_MASTER') {
38582            _this3.trigger('loadedmetadata');
38583          } else {
38584            _this3.trigger('mediachange');
38585          }
38586        });
38587      }
38588      /**
38589       * pause loading of the playlist
38590       */
38591
38592    }, {
38593      key: 'pause',
38594      value: function pause() {
38595        this.stopRequest();
38596        window$1.clearTimeout(this.mediaUpdateTimeout);
38597
38598        if (this.state === 'HAVE_NOTHING') {
38599          // If we pause the loader before any data has been retrieved, its as if we never
38600          // started, so reset to an unstarted state.
38601          this.started = false;
38602        } // Need to restore state now that no activity is happening
38603
38604
38605        if (this.state === 'SWITCHING_MEDIA') {
38606          // if the loader was in the process of switching media, it should either return to
38607          // HAVE_MASTER or HAVE_METADATA depending on if the loader has loaded a media
38608          // playlist yet. This is determined by the existence of loader.media_
38609          if (this.media_) {
38610            this.state = 'HAVE_METADATA';
38611          } else {
38612            this.state = 'HAVE_MASTER';
38613          }
38614        } else if (this.state === 'HAVE_CURRENT_METADATA') {
38615          this.state = 'HAVE_METADATA';
38616        }
38617      }
38618      /**
38619       * start loading of the playlist
38620       */
38621
38622    }, {
38623      key: 'load',
38624      value: function load(isFinalRendition) {
38625        var _this4 = this;
38626
38627        window$1.clearTimeout(this.mediaUpdateTimeout);
38628        var media = this.media();
38629
38630        if (isFinalRendition) {
38631          var delay = media ? media.targetDuration / 2 * 1000 : 5 * 1000;
38632          this.mediaUpdateTimeout = window$1.setTimeout(function () {
38633            return _this4.load();
38634          }, delay);
38635          return;
38636        }
38637
38638        if (!this.started) {
38639          this.start();
38640          return;
38641        }
38642
38643        if (media && !media.endList) {
38644          this.trigger('mediaupdatetimeout');
38645        } else {
38646          this.trigger('loadedplaylist');
38647        }
38648      }
38649      /**
38650       * start loading of the playlist
38651       */
38652
38653    }, {
38654      key: 'start',
38655      value: function start() {
38656        var _this5 = this;
38657
38658        this.started = true; // request the specified URL
38659
38660        this.request = this.hls_.xhr({
38661          uri: this.srcUrl,
38662          withCredentials: this.withCredentials
38663        }, function (error, req) {
38664          // disposed
38665          if (!_this5.request) {
38666            return;
38667          } // clear the loader's request reference
38668
38669
38670          _this5.request = null;
38671
38672          if (error) {
38673            _this5.error = {
38674              status: req.status,
38675              message: 'HLS playlist request error at URL: ' + _this5.srcUrl + '.',
38676              responseText: req.responseText,
38677              // MEDIA_ERR_NETWORK
38678              code: 2
38679            };
38680
38681            if (_this5.state === 'HAVE_NOTHING') {
38682              _this5.started = false;
38683            }
38684
38685            return _this5.trigger('error');
38686          }
38687
38688          var parser = new Parser(); // adding custom tag parsers
38689
38690          _this5.customTagParsers.forEach(function (customParser) {
38691            return parser.addParser(customParser);
38692          }); // adding custom tag mappers
38693
38694
38695          _this5.customTagMappers.forEach(function (mapper) {
38696            return parser.addTagMapper(mapper);
38697          });
38698
38699          parser.push(req.responseText);
38700          parser.end();
38701          _this5.state = 'HAVE_MASTER';
38702          _this5.srcUrl = resolveManifestRedirect(_this5.handleManifestRedirects, _this5.srcUrl, req);
38703          parser.manifest.uri = _this5.srcUrl; // loaded a master playlist
38704
38705          if (parser.manifest.playlists) {
38706            _this5.master = parser.manifest;
38707            setupMediaPlaylists(_this5.master);
38708            resolveMediaGroupUris(_this5.master);
38709
38710            _this5.trigger('loadedplaylist');
38711
38712            if (!_this5.request) {
38713              // no media playlist was specifically selected so start
38714              // from the first listed one
38715              _this5.media(parser.manifest.playlists[0]);
38716            }
38717
38718            return;
38719          } // loaded a media playlist
38720          // infer a master playlist if none was previously requested
38721
38722
38723          _this5.master = {
38724            mediaGroups: {
38725              'AUDIO': {},
38726              'VIDEO': {},
38727              'CLOSED-CAPTIONS': {},
38728              'SUBTITLES': {}
38729            },
38730            uri: window$1.location.href,
38731            playlists: [{
38732              uri: _this5.srcUrl,
38733              id: 0,
38734              resolvedUri: _this5.srcUrl,
38735              // m3u8-parser does not attach an attributes property to media playlists so make
38736              // sure that the property is attached to avoid undefined reference errors
38737              attributes: {}
38738            }]
38739          };
38740          _this5.master.playlists[_this5.srcUrl] = _this5.master.playlists[0];
38741
38742          _this5.haveMetadata(req, _this5.srcUrl);
38743
38744          return _this5.trigger('loadedmetadata');
38745        });
38746      }
38747    }]);
38748    return PlaylistLoader;
38749  }(EventTarget$1);
38750  /**
38751   * @file playlist.js
38752   *
38753   * Playlist related utilities.
38754   */
38755
38756
38757  var createTimeRange = videojs$1.createTimeRange;
38758  /**
38759   * walk backward until we find a duration we can use
38760   * or return a failure
38761   *
38762   * @param {Playlist} playlist the playlist to walk through
38763   * @param {Number} endSequence the mediaSequence to stop walking on
38764   */
38765
38766  var backwardDuration = function backwardDuration(playlist, endSequence) {
38767    var result = 0;
38768    var i = endSequence - playlist.mediaSequence; // if a start time is available for segment immediately following
38769    // the interval, use it
38770
38771    var segment = playlist.segments[i]; // Walk backward until we find the latest segment with timeline
38772    // information that is earlier than endSequence
38773
38774    if (segment) {
38775      if (typeof segment.start !== 'undefined') {
38776        return {
38777          result: segment.start,
38778          precise: true
38779        };
38780      }
38781
38782      if (typeof segment.end !== 'undefined') {
38783        return {
38784          result: segment.end - segment.duration,
38785          precise: true
38786        };
38787      }
38788    }
38789
38790    while (i--) {
38791      segment = playlist.segments[i];
38792
38793      if (typeof segment.end !== 'undefined') {
38794        return {
38795          result: result + segment.end,
38796          precise: true
38797        };
38798      }
38799
38800      result += segment.duration;
38801
38802      if (typeof segment.start !== 'undefined') {
38803        return {
38804          result: result + segment.start,
38805          precise: true
38806        };
38807      }
38808    }
38809
38810    return {
38811      result: result,
38812      precise: false
38813    };
38814  };
38815  /**
38816   * walk forward until we find a duration we can use
38817   * or return a failure
38818   *
38819   * @param {Playlist} playlist the playlist to walk through
38820   * @param {Number} endSequence the mediaSequence to stop walking on
38821   */
38822
38823
38824  var forwardDuration = function forwardDuration(playlist, endSequence) {
38825    var result = 0;
38826    var segment = void 0;
38827    var i = endSequence - playlist.mediaSequence; // Walk forward until we find the earliest segment with timeline
38828    // information
38829
38830    for (; i < playlist.segments.length; i++) {
38831      segment = playlist.segments[i];
38832
38833      if (typeof segment.start !== 'undefined') {
38834        return {
38835          result: segment.start - result,
38836          precise: true
38837        };
38838      }
38839
38840      result += segment.duration;
38841
38842      if (typeof segment.end !== 'undefined') {
38843        return {
38844          result: segment.end - result,
38845          precise: true
38846        };
38847      }
38848    } // indicate we didn't find a useful duration estimate
38849
38850
38851    return {
38852      result: -1,
38853      precise: false
38854    };
38855  };
38856  /**
38857    * Calculate the media duration from the segments associated with a
38858    * playlist. The duration of a subinterval of the available segments
38859    * may be calculated by specifying an end index.
38860    *
38861    * @param {Object} playlist a media playlist object
38862    * @param {Number=} endSequence an exclusive upper boundary
38863    * for the playlist.  Defaults to playlist length.
38864    * @param {Number} expired the amount of time that has dropped
38865    * off the front of the playlist in a live scenario
38866    * @return {Number} the duration between the first available segment
38867    * and end index.
38868    */
38869
38870
38871  var intervalDuration = function intervalDuration(playlist, endSequence, expired) {
38872    var backward = void 0;
38873    var forward = void 0;
38874
38875    if (typeof endSequence === 'undefined') {
38876      endSequence = playlist.mediaSequence + playlist.segments.length;
38877    }
38878
38879    if (endSequence < playlist.mediaSequence) {
38880      return 0;
38881    } // do a backward walk to estimate the duration
38882
38883
38884    backward = backwardDuration(playlist, endSequence);
38885
38886    if (backward.precise) {
38887      // if we were able to base our duration estimate on timing
38888      // information provided directly from the Media Source, return
38889      // it
38890      return backward.result;
38891    } // walk forward to see if a precise duration estimate can be made
38892    // that way
38893
38894
38895    forward = forwardDuration(playlist, endSequence);
38896
38897    if (forward.precise) {
38898      // we found a segment that has been buffered and so it's
38899      // position is known precisely
38900      return forward.result;
38901    } // return the less-precise, playlist-based duration estimate
38902
38903
38904    return backward.result + expired;
38905  };
38906  /**
38907    * Calculates the duration of a playlist. If a start and end index
38908    * are specified, the duration will be for the subset of the media
38909    * timeline between those two indices. The total duration for live
38910    * playlists is always Infinity.
38911    *
38912    * @param {Object} playlist a media playlist object
38913    * @param {Number=} endSequence an exclusive upper
38914    * boundary for the playlist. Defaults to the playlist media
38915    * sequence number plus its length.
38916    * @param {Number=} expired the amount of time that has
38917    * dropped off the front of the playlist in a live scenario
38918    * @return {Number} the duration between the start index and end
38919    * index.
38920    */
38921
38922
38923  var duration = function duration(playlist, endSequence, expired) {
38924    if (!playlist) {
38925      return 0;
38926    }
38927
38928    if (typeof expired !== 'number') {
38929      expired = 0;
38930    } // if a slice of the total duration is not requested, use
38931    // playlist-level duration indicators when they're present
38932
38933
38934    if (typeof endSequence === 'undefined') {
38935      // if present, use the duration specified in the playlist
38936      if (playlist.totalDuration) {
38937        return playlist.totalDuration;
38938      } // duration should be Infinity for live playlists
38939
38940
38941      if (!playlist.endList) {
38942        return window$1.Infinity;
38943      }
38944    } // calculate the total duration based on the segment durations
38945
38946
38947    return intervalDuration(playlist, endSequence, expired);
38948  };
38949  /**
38950    * Calculate the time between two indexes in the current playlist
38951    * neight the start- nor the end-index need to be within the current
38952    * playlist in which case, the targetDuration of the playlist is used
38953    * to approximate the durations of the segments
38954    *
38955    * @param {Object} playlist a media playlist object
38956    * @param {Number} startIndex
38957    * @param {Number} endIndex
38958    * @return {Number} the number of seconds between startIndex and endIndex
38959    */
38960
38961
38962  var sumDurations = function sumDurations(playlist, startIndex, endIndex) {
38963    var durations = 0;
38964
38965    if (startIndex > endIndex) {
38966      var _ref = [endIndex, startIndex];
38967      startIndex = _ref[0];
38968      endIndex = _ref[1];
38969    }
38970
38971    if (startIndex < 0) {
38972      for (var i = startIndex; i < Math.min(0, endIndex); i++) {
38973        durations += playlist.targetDuration;
38974      }
38975
38976      startIndex = 0;
38977    }
38978
38979    for (var _i = startIndex; _i < endIndex; _i++) {
38980      durations += playlist.segments[_i].duration;
38981    }
38982
38983    return durations;
38984  };
38985  /**
38986   * Determines the media index of the segment corresponding to the safe edge of the live
38987   * window which is the duration of the last segment plus 2 target durations from the end
38988   * of the playlist.
38989   *
38990   * @param {Object} playlist
38991   *        a media playlist object
38992   * @return {Number}
38993   *         The media index of the segment at the safe live point. 0 if there is no "safe"
38994   *         point.
38995   * @function safeLiveIndex
38996   */
38997
38998
38999  var safeLiveIndex = function safeLiveIndex(playlist) {
39000    if (!playlist.segments.length) {
39001      return 0;
39002    }
39003
39004    var i = playlist.segments.length - 1;
39005    var distanceFromEnd = playlist.segments[i].duration || playlist.targetDuration;
39006    var safeDistance = distanceFromEnd + playlist.targetDuration * 2;
39007
39008    while (i--) {
39009      distanceFromEnd += playlist.segments[i].duration;
39010
39011      if (distanceFromEnd >= safeDistance) {
39012        break;
39013      }
39014    }
39015
39016    return Math.max(0, i);
39017  };
39018  /**
39019   * Calculates the playlist end time
39020   *
39021   * @param {Object} playlist a media playlist object
39022   * @param {Number=} expired the amount of time that has
39023   *                  dropped off the front of the playlist in a live scenario
39024   * @param {Boolean|false} useSafeLiveEnd a boolean value indicating whether or not the
39025   *                        playlist end calculation should consider the safe live end
39026   *                        (truncate the playlist end by three segments). This is normally
39027   *                        used for calculating the end of the playlist's seekable range.
39028   * @returns {Number} the end time of playlist
39029   * @function playlistEnd
39030   */
39031
39032
39033  var playlistEnd = function playlistEnd(playlist, expired, useSafeLiveEnd) {
39034    if (!playlist || !playlist.segments) {
39035      return null;
39036    }
39037
39038    if (playlist.endList) {
39039      return duration(playlist);
39040    }
39041
39042    if (expired === null) {
39043      return null;
39044    }
39045
39046    expired = expired || 0;
39047    var endSequence = useSafeLiveEnd ? safeLiveIndex(playlist) : playlist.segments.length;
39048    return intervalDuration(playlist, playlist.mediaSequence + endSequence, expired);
39049  };
39050  /**
39051    * Calculates the interval of time that is currently seekable in a
39052    * playlist. The returned time ranges are relative to the earliest
39053    * moment in the specified playlist that is still available. A full
39054    * seekable implementation for live streams would need to offset
39055    * these values by the duration of content that has expired from the
39056    * stream.
39057    *
39058    * @param {Object} playlist a media playlist object
39059    * dropped off the front of the playlist in a live scenario
39060    * @param {Number=} expired the amount of time that has
39061    * dropped off the front of the playlist in a live scenario
39062    * @return {TimeRanges} the periods of time that are valid targets
39063    * for seeking
39064    */
39065
39066
39067  var seekable = function seekable(playlist, expired) {
39068    var useSafeLiveEnd = true;
39069    var seekableStart = expired || 0;
39070    var seekableEnd = playlistEnd(playlist, expired, useSafeLiveEnd);
39071
39072    if (seekableEnd === null) {
39073      return createTimeRange();
39074    }
39075
39076    return createTimeRange(seekableStart, seekableEnd);
39077  };
39078
39079  var isWholeNumber = function isWholeNumber(num) {
39080    return num - Math.floor(num) === 0;
39081  };
39082
39083  var roundSignificantDigit = function roundSignificantDigit(increment, num) {
39084    // If we have a whole number, just add 1 to it
39085    if (isWholeNumber(num)) {
39086      return num + increment * 0.1;
39087    }
39088
39089    var numDecimalDigits = num.toString().split('.')[1].length;
39090
39091    for (var i = 1; i <= numDecimalDigits; i++) {
39092      var scale = Math.pow(10, i);
39093      var temp = num * scale;
39094
39095      if (isWholeNumber(temp) || i === numDecimalDigits) {
39096        return (temp + increment) / scale;
39097      }
39098    }
39099  };
39100
39101  var ceilLeastSignificantDigit = roundSignificantDigit.bind(null, 1);
39102  var floorLeastSignificantDigit = roundSignificantDigit.bind(null, -1);
39103  /**
39104   * Determine the index and estimated starting time of the segment that
39105   * contains a specified playback position in a media playlist.
39106   *
39107   * @param {Object} playlist the media playlist to query
39108   * @param {Number} currentTime The number of seconds since the earliest
39109   * possible position to determine the containing segment for
39110   * @param {Number} startIndex
39111   * @param {Number} startTime
39112   * @return {Object}
39113   */
39114
39115  var getMediaInfoForTime = function getMediaInfoForTime(playlist, currentTime, startIndex, startTime) {
39116    var i = void 0;
39117    var segment = void 0;
39118    var numSegments = playlist.segments.length;
39119    var time = currentTime - startTime;
39120
39121    if (time < 0) {
39122      // Walk backward from startIndex in the playlist, adding durations
39123      // until we find a segment that contains `time` and return it
39124      if (startIndex > 0) {
39125        for (i = startIndex - 1; i >= 0; i--) {
39126          segment = playlist.segments[i];
39127          time += floorLeastSignificantDigit(segment.duration);
39128
39129          if (time > 0) {
39130            return {
39131              mediaIndex: i,
39132              startTime: startTime - sumDurations(playlist, startIndex, i)
39133            };
39134          }
39135        }
39136      } // We were unable to find a good segment within the playlist
39137      // so select the first segment
39138
39139
39140      return {
39141        mediaIndex: 0,
39142        startTime: currentTime
39143      };
39144    } // When startIndex is negative, we first walk forward to first segment
39145    // adding target durations. If we "run out of time" before getting to
39146    // the first segment, return the first segment
39147
39148
39149    if (startIndex < 0) {
39150      for (i = startIndex; i < 0; i++) {
39151        time -= playlist.targetDuration;
39152
39153        if (time < 0) {
39154          return {
39155            mediaIndex: 0,
39156            startTime: currentTime
39157          };
39158        }
39159      }
39160
39161      startIndex = 0;
39162    } // Walk forward from startIndex in the playlist, subtracting durations
39163    // until we find a segment that contains `time` and return it
39164
39165
39166    for (i = startIndex; i < numSegments; i++) {
39167      segment = playlist.segments[i];
39168      time -= ceilLeastSignificantDigit(segment.duration);
39169
39170      if (time < 0) {
39171        return {
39172          mediaIndex: i,
39173          startTime: startTime + sumDurations(playlist, startIndex, i)
39174        };
39175      }
39176    } // We are out of possible candidates so load the last one...
39177
39178
39179    return {
39180      mediaIndex: numSegments - 1,
39181      startTime: currentTime
39182    };
39183  };
39184  /**
39185   * Check whether the playlist is blacklisted or not.
39186   *
39187   * @param {Object} playlist the media playlist object
39188   * @return {boolean} whether the playlist is blacklisted or not
39189   * @function isBlacklisted
39190   */
39191
39192
39193  var isBlacklisted = function isBlacklisted(playlist) {
39194    return playlist.excludeUntil && playlist.excludeUntil > Date.now();
39195  };
39196  /**
39197   * Check whether the playlist is compatible with current playback configuration or has
39198   * been blacklisted permanently for being incompatible.
39199   *
39200   * @param {Object} playlist the media playlist object
39201   * @return {boolean} whether the playlist is incompatible or not
39202   * @function isIncompatible
39203   */
39204
39205
39206  var isIncompatible = function isIncompatible(playlist) {
39207    return playlist.excludeUntil && playlist.excludeUntil === Infinity;
39208  };
39209  /**
39210   * Check whether the playlist is enabled or not.
39211   *
39212   * @param {Object} playlist the media playlist object
39213   * @return {boolean} whether the playlist is enabled or not
39214   * @function isEnabled
39215   */
39216
39217
39218  var isEnabled = function isEnabled(playlist) {
39219    var blacklisted = isBlacklisted(playlist);
39220    return !playlist.disabled && !blacklisted;
39221  };
39222  /**
39223   * Check whether the playlist has been manually disabled through the representations api.
39224   *
39225   * @param {Object} playlist the media playlist object
39226   * @return {boolean} whether the playlist is disabled manually or not
39227   * @function isDisabled
39228   */
39229
39230
39231  var isDisabled = function isDisabled(playlist) {
39232    return playlist.disabled;
39233  };
39234  /**
39235   * Returns whether the current playlist is an AES encrypted HLS stream
39236   *
39237   * @return {Boolean} true if it's an AES encrypted HLS stream
39238   */
39239
39240
39241  var isAes = function isAes(media) {
39242    for (var i = 0; i < media.segments.length; i++) {
39243      if (media.segments[i].key) {
39244        return true;
39245      }
39246    }
39247
39248    return false;
39249  };
39250  /**
39251   * Returns whether the current playlist contains fMP4
39252   *
39253   * @return {Boolean} true if the playlist contains fMP4
39254   */
39255
39256
39257  var isFmp4 = function isFmp4(media) {
39258    for (var i = 0; i < media.segments.length; i++) {
39259      if (media.segments[i].map) {
39260        return true;
39261      }
39262    }
39263
39264    return false;
39265  };
39266  /**
39267   * Checks if the playlist has a value for the specified attribute
39268   *
39269   * @param {String} attr
39270   *        Attribute to check for
39271   * @param {Object} playlist
39272   *        The media playlist object
39273   * @return {Boolean}
39274   *         Whether the playlist contains a value for the attribute or not
39275   * @function hasAttribute
39276   */
39277
39278
39279  var hasAttribute = function hasAttribute(attr, playlist) {
39280    return playlist.attributes && playlist.attributes[attr];
39281  };
39282  /**
39283   * Estimates the time required to complete a segment download from the specified playlist
39284   *
39285   * @param {Number} segmentDuration
39286   *        Duration of requested segment
39287   * @param {Number} bandwidth
39288   *        Current measured bandwidth of the player
39289   * @param {Object} playlist
39290   *        The media playlist object
39291   * @param {Number=} bytesReceived
39292   *        Number of bytes already received for the request. Defaults to 0
39293   * @return {Number|NaN}
39294   *         The estimated time to request the segment. NaN if bandwidth information for
39295   *         the given playlist is unavailable
39296   * @function estimateSegmentRequestTime
39297   */
39298
39299
39300  var estimateSegmentRequestTime = function estimateSegmentRequestTime(segmentDuration, bandwidth, playlist) {
39301    var bytesReceived = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : 0;
39302
39303    if (!hasAttribute('BANDWIDTH', playlist)) {
39304      return NaN;
39305    }
39306
39307    var size = segmentDuration * playlist.attributes.BANDWIDTH;
39308    return (size - bytesReceived * 8) / bandwidth;
39309  };
39310  /*
39311   * Returns whether the current playlist is the lowest rendition
39312   *
39313   * @return {Boolean} true if on lowest rendition
39314   */
39315
39316
39317  var isLowestEnabledRendition = function isLowestEnabledRendition(master, media) {
39318    if (master.playlists.length === 1) {
39319      return true;
39320    }
39321
39322    var currentBandwidth = media.attributes.BANDWIDTH || Number.MAX_VALUE;
39323    return master.playlists.filter(function (playlist) {
39324      if (!isEnabled(playlist)) {
39325        return false;
39326      }
39327
39328      return (playlist.attributes.BANDWIDTH || 0) < currentBandwidth;
39329    }).length === 0;
39330  }; // exports
39331
39332
39333  var Playlist = {
39334    duration: duration,
39335    seekable: seekable,
39336    safeLiveIndex: safeLiveIndex,
39337    getMediaInfoForTime: getMediaInfoForTime,
39338    isEnabled: isEnabled,
39339    isDisabled: isDisabled,
39340    isBlacklisted: isBlacklisted,
39341    isIncompatible: isIncompatible,
39342    playlistEnd: playlistEnd,
39343    isAes: isAes,
39344    isFmp4: isFmp4,
39345    hasAttribute: hasAttribute,
39346    estimateSegmentRequestTime: estimateSegmentRequestTime,
39347    isLowestEnabledRendition: isLowestEnabledRendition
39348  };
39349  /**
39350   * @file xhr.js
39351   */
39352
39353  var videojsXHR = videojs$1.xhr,
39354      mergeOptions$1$1 = videojs$1.mergeOptions;
39355
39356  var xhrFactory = function xhrFactory() {
39357    var xhr = function XhrFunction(options, callback) {
39358      // Add a default timeout for all hls requests
39359      options = mergeOptions$1$1({
39360        timeout: 45e3
39361      }, options); // Allow an optional user-specified function to modify the option
39362      // object before we construct the xhr request
39363
39364      var beforeRequest = XhrFunction.beforeRequest || videojs$1.Hls.xhr.beforeRequest;
39365
39366      if (beforeRequest && typeof beforeRequest === 'function') {
39367        var newOptions = beforeRequest(options);
39368
39369        if (newOptions) {
39370          options = newOptions;
39371        }
39372      }
39373
39374      var request = videojsXHR(options, function (error, response) {
39375        var reqResponse = request.response;
39376
39377        if (!error && reqResponse) {
39378          request.responseTime = Date.now();
39379          request.roundTripTime = request.responseTime - request.requestTime;
39380          request.bytesReceived = reqResponse.byteLength || reqResponse.length;
39381
39382          if (!request.bandwidth) {
39383            request.bandwidth = Math.floor(request.bytesReceived / request.roundTripTime * 8 * 1000);
39384          }
39385        }
39386
39387        if (response.headers) {
39388          request.responseHeaders = response.headers;
39389        } // videojs.xhr now uses a specific code on the error
39390        // object to signal that a request has timed out instead
39391        // of setting a boolean on the request object
39392
39393
39394        if (error && error.code === 'ETIMEDOUT') {
39395          request.timedout = true;
39396        } // videojs.xhr no longer considers status codes outside of 200 and 0
39397        // (for file uris) to be errors, but the old XHR did, so emulate that
39398        // behavior. Status 206 may be used in response to byterange requests.
39399
39400
39401        if (!error && !request.aborted && response.statusCode !== 200 && response.statusCode !== 206 && response.statusCode !== 0) {
39402          error = new Error('XHR Failed with a response of: ' + (request && (reqResponse || request.responseText)));
39403        }
39404
39405        callback(error, request);
39406      });
39407      var originalAbort = request.abort;
39408
39409      request.abort = function () {
39410        request.aborted = true;
39411        return originalAbort.apply(request, arguments);
39412      };
39413
39414      request.uri = options.uri;
39415      request.requestTime = Date.now();
39416      return request;
39417    };
39418
39419    return xhr;
39420  };
39421  /**
39422   * Turns segment byterange into a string suitable for use in
39423   * HTTP Range requests
39424   *
39425   * @param {Object} byterange - an object with two values defining the start and end
39426   *                             of a byte-range
39427   */
39428
39429
39430  var byterangeStr = function byterangeStr(byterange) {
39431    var byterangeStart = void 0;
39432    var byterangeEnd = void 0; // `byterangeEnd` is one less than `offset + length` because the HTTP range
39433    // header uses inclusive ranges
39434
39435    byterangeEnd = byterange.offset + byterange.length - 1;
39436    byterangeStart = byterange.offset;
39437    return 'bytes=' + byterangeStart + '-' + byterangeEnd;
39438  };
39439  /**
39440   * Defines headers for use in the xhr request for a particular segment.
39441   *
39442   * @param {Object} segment - a simplified copy of the segmentInfo object
39443   *                           from SegmentLoader
39444   */
39445
39446
39447  var segmentXhrHeaders = function segmentXhrHeaders(segment) {
39448    var headers = {};
39449
39450    if (segment.byterange) {
39451      headers.Range = byterangeStr(segment.byterange);
39452    }
39453
39454    return headers;
39455  };
39456  /**
39457   * @file bin-utils.js
39458   */
39459
39460  /**
39461   * convert a TimeRange to text
39462   *
39463   * @param {TimeRange} range the timerange to use for conversion
39464   * @param {Number} i the iterator on the range to convert
39465   */
39466
39467
39468  var textRange = function textRange(range, i) {
39469    return range.start(i) + '-' + range.end(i);
39470  };
39471  /**
39472   * format a number as hex string
39473   *
39474   * @param {Number} e The number
39475   * @param {Number} i the iterator
39476   */
39477
39478
39479  var formatHexString = function formatHexString(e, i) {
39480    var value = e.toString(16);
39481    return '00'.substring(0, 2 - value.length) + value + (i % 2 ? ' ' : '');
39482  };
39483
39484  var formatAsciiString = function formatAsciiString(e) {
39485    if (e >= 0x20 && e < 0x7e) {
39486      return String.fromCharCode(e);
39487    }
39488
39489    return '.';
39490  };
39491  /**
39492   * Creates an object for sending to a web worker modifying properties that are TypedArrays
39493   * into a new object with seperated properties for the buffer, byteOffset, and byteLength.
39494   *
39495   * @param {Object} message
39496   *        Object of properties and values to send to the web worker
39497   * @return {Object}
39498   *         Modified message with TypedArray values expanded
39499   * @function createTransferableMessage
39500   */
39501
39502
39503  var createTransferableMessage = function createTransferableMessage(message) {
39504    var transferable = {};
39505    Object.keys(message).forEach(function (key) {
39506      var value = message[key];
39507
39508      if (ArrayBuffer.isView(value)) {
39509        transferable[key] = {
39510          bytes: value.buffer,
39511          byteOffset: value.byteOffset,
39512          byteLength: value.byteLength
39513        };
39514      } else {
39515        transferable[key] = value;
39516      }
39517    });
vendor: 17,592 bytes, lines 39518-40007
39518    return transferable;
39519  };
39520  /**
39521   * Returns a unique string identifier for a media initialization
39522   * segment.
39523   */
39524
39525
39526  var initSegmentId = function initSegmentId(initSegment) {
39527    var byterange = initSegment.byterange || {
39528      length: Infinity,
39529      offset: 0
39530    };
39531    return [byterange.length, byterange.offset, initSegment.resolvedUri].join(',');
39532  };
39533  /**
39534   * Returns a unique string identifier for a media segment key.
39535   */
39536
39537
39538  var segmentKeyId = function segmentKeyId(key) {
39539    return key.resolvedUri;
39540  };
39541  /**
39542   * utils to help dump binary data to the console
39543   */
39544
39545
39546  var hexDump = function hexDump(data) {
39547    var bytes = Array.prototype.slice.call(data);
39548    var step = 16;
39549    var result = '';
39550    var hex = void 0;
39551    var ascii = void 0;
39552
39553    for (var j = 0; j < bytes.length / step; j++) {
39554      hex = bytes.slice(j * step, j * step + step).map(formatHexString).join('');
39555      ascii = bytes.slice(j * step, j * step + step).map(formatAsciiString).join('');
39556      result += hex + ' ' + ascii + '\n';
39557    }
39558
39559    return result;
39560  };
39561
39562  var tagDump = function tagDump(_ref) {
39563    var bytes = _ref.bytes;
39564    return hexDump(bytes);
39565  };
39566
39567  var textRanges = function textRanges(ranges) {
39568    var result = '';
39569    var i = void 0;
39570
39571    for (i = 0; i < ranges.length; i++) {
39572      result += textRange(ranges, i) + ' ';
39573    }
39574
39575    return result;
39576  };
39577
39578  var utils$1 =
39579  /*#__PURE__*/
39580  Object.freeze({
39581    createTransferableMessage: createTransferableMessage,
39582    initSegmentId: initSegmentId,
39583    segmentKeyId: segmentKeyId,
39584    hexDump: hexDump,
39585    tagDump: tagDump,
39586    textRanges: textRanges
39587  }); // TODO handle fmp4 case where the timing info is accurate and doesn't involve transmux
39588  // Add 25% to the segment duration to account for small discrepencies in segment timing.
39589  // 25% was arbitrarily chosen, and may need to be refined over time.
39590
39591  var SEGMENT_END_FUDGE_PERCENT = 0.25;
39592  /**
39593   * Converts a player time (any time that can be gotten/set from player.currentTime(),
39594   * e.g., any time within player.seekable().start(0) to player.seekable().end(0)) to a
39595   * program time (any time referencing the real world (e.g., EXT-X-PROGRAM-DATE-TIME)).
39596   *
39597   * The containing segment is required as the EXT-X-PROGRAM-DATE-TIME serves as an "anchor
39598   * point" (a point where we have a mapping from program time to player time, with player
39599   * time being the post transmux start of the segment).
39600   *
39601   * For more details, see [this doc](../../docs/program-time-from-player-time.md).
39602   *
39603   * @param {Number} playerTime the player time
39604   * @param {Object} segment the segment which contains the player time
39605   * @return {Date} program time
39606   */
39607
39608  var playerTimeToProgramTime = function playerTimeToProgramTime(playerTime, segment) {
39609    if (!segment.dateTimeObject) {
39610      // Can't convert without an "anchor point" for the program time (i.e., a time that can
39611      // be used to map the start of a segment with a real world time).
39612      return null;
39613    }
39614
39615    var transmuxerPrependedSeconds = segment.videoTimingInfo.transmuxerPrependedSeconds;
39616    var transmuxedStart = segment.videoTimingInfo.transmuxedPresentationStart; // get the start of the content from before old content is prepended
39617
39618    var startOfSegment = transmuxedStart + transmuxerPrependedSeconds;
39619    var offsetFromSegmentStart = playerTime - startOfSegment;
39620    return new Date(segment.dateTimeObject.getTime() + offsetFromSegmentStart * 1000);
39621  };
39622
39623  var originalSegmentVideoDuration = function originalSegmentVideoDuration(videoTimingInfo) {
39624    return videoTimingInfo.transmuxedPresentationEnd - videoTimingInfo.transmuxedPresentationStart - videoTimingInfo.transmuxerPrependedSeconds;
39625  };
39626  /**
39627   * Finds a segment that contains the time requested given as an ISO-8601 string. The
39628   * returned segment might be an estimate or an accurate match.
39629   *
39630   * @param {String} programTime The ISO-8601 programTime to find a match for
39631   * @param {Object} playlist A playlist object to search within
39632   */
39633
39634
39635  var findSegmentForProgramTime = function findSegmentForProgramTime(programTime, playlist) {
39636    // Assumptions:
39637    //  - verifyProgramDateTimeTags has already been run
39638    //  - live streams have been started
39639    var dateTimeObject = void 0;
39640
39641    try {
39642      dateTimeObject = new Date(programTime);
39643    } catch (e) {
39644      return null;
39645    }
39646
39647    if (!playlist || !playlist.segments || playlist.segments.length === 0) {
39648      return null;
39649    }
39650
39651    var segment = playlist.segments[0];
39652
39653    if (dateTimeObject < segment.dateTimeObject) {
39654      // Requested time is before stream start.
39655      return null;
39656    }
39657
39658    for (var i = 0; i < playlist.segments.length - 1; i++) {
39659      segment = playlist.segments[i];
39660      var nextSegmentStart = playlist.segments[i + 1].dateTimeObject;
39661
39662      if (dateTimeObject < nextSegmentStart) {
39663        break;
39664      }
39665    }
39666
39667    var lastSegment = playlist.segments[playlist.segments.length - 1];
39668    var lastSegmentStart = lastSegment.dateTimeObject;
39669    var lastSegmentDuration = lastSegment.videoTimingInfo ? originalSegmentVideoDuration(lastSegment.videoTimingInfo) : lastSegment.duration + lastSegment.duration * SEGMENT_END_FUDGE_PERCENT;
39670    var lastSegmentEnd = new Date(lastSegmentStart.getTime() + lastSegmentDuration * 1000);
39671
39672    if (dateTimeObject > lastSegmentEnd) {
39673      // Beyond the end of the stream, or our best guess of the end of the stream.
39674      return null;
39675    }
39676
39677    if (dateTimeObject > lastSegmentStart) {
39678      segment = lastSegment;
39679    }
39680
39681    return {
39682      segment: segment,
39683      estimatedStart: segment.videoTimingInfo ? segment.videoTimingInfo.transmuxedPresentationStart : Playlist.duration(playlist, playlist.mediaSequence + playlist.segments.indexOf(segment)),
39684      // Although, given that all segments have accurate date time objects, the segment
39685      // selected should be accurate, unless the video has been transmuxed at some point
39686      // (determined by the presence of the videoTimingInfo object), the segment's "player
39687      // time" (the start time in the player) can't be considered accurate.
39688      type: segment.videoTimingInfo ? 'accurate' : 'estimate'
39689    };
39690  };
39691  /**
39692   * Finds a segment that contains the given player time(in seconds).
39693   *
39694   * @param {Number} time The player time to find a match for
39695   * @param {Object} playlist A playlist object to search within
39696   */
39697
39698
39699  var findSegmentForPlayerTime = function findSegmentForPlayerTime(time, playlist) {
39700    // Assumptions:
39701    // - there will always be a segment.duration
39702    // - we can start from zero
39703    // - segments are in time order
39704    if (!playlist || !playlist.segments || playlist.segments.length === 0) {
39705      return null;
39706    }
39707
39708    var segmentEnd = 0;
39709    var segment = void 0;
39710
39711    for (var i = 0; i < playlist.segments.length; i++) {
39712      segment = playlist.segments[i]; // videoTimingInfo is set after the segment is downloaded and transmuxed, and
39713      // should contain the most accurate values we have for the segment's player times.
39714      //
39715      // Use the accurate transmuxedPresentationEnd value if it is available, otherwise fall
39716      // back to an estimate based on the manifest derived (inaccurate) segment.duration, to
39717      // calculate an end value.
39718
39719      segmentEnd = segment.videoTimingInfo ? segment.videoTimingInfo.transmuxedPresentationEnd : segmentEnd + segment.duration;
39720
39721      if (time <= segmentEnd) {
39722        break;
39723      }
39724    }
39725
39726    var lastSegment = playlist.segments[playlist.segments.length - 1];
39727
39728    if (lastSegment.videoTimingInfo && lastSegment.videoTimingInfo.transmuxedPresentationEnd < time) {
39729      // The time requested is beyond the stream end.
39730      return null;
39731    }
39732
39733    if (time > segmentEnd) {
39734      // The time is within or beyond the last segment.
39735      //
39736      // Check to see if the time is beyond a reasonable guess of the end of the stream.
39737      if (time > segmentEnd + lastSegment.duration * SEGMENT_END_FUDGE_PERCENT) {
39738        // Technically, because the duration value is only an estimate, the time may still
39739        // exist in the last segment, however, there isn't enough information to make even
39740        // a reasonable estimate.
39741        return null;
39742      }
39743
39744      segment = lastSegment;
39745    }
39746
39747    return {
39748      segment: segment,
39749      estimatedStart: segment.videoTimingInfo ? segment.videoTimingInfo.transmuxedPresentationStart : segmentEnd - segment.duration,
39750      // Because videoTimingInfo is only set after transmux, it is the only way to get
39751      // accurate timing values.
39752      type: segment.videoTimingInfo ? 'accurate' : 'estimate'
39753    };
39754  };
39755  /**
39756   * Gives the offset of the comparisonTimestamp from the programTime timestamp in seconds.
39757   * If the offset returned is positive, the programTime occurs after the
39758   * comparisonTimestamp.
39759   * If the offset is negative, the programTime occurs before the comparisonTimestamp.
39760   *
39761   * @param {String} comparisonTimeStamp An ISO-8601 timestamp to compare against
39762   * @param {String} programTime The programTime as an ISO-8601 string
39763   * @return {Number} offset
39764   */
39765
39766
39767  var getOffsetFromTimestamp = function getOffsetFromTimestamp(comparisonTimeStamp, programTime) {
39768    var segmentDateTime = void 0;
39769    var programDateTime = void 0;
39770
39771    try {
39772      segmentDateTime = new Date(comparisonTimeStamp);
39773      programDateTime = new Date(programTime);
39774    } catch (e) {// TODO handle error
39775    }
39776
39777    var segmentTimeEpoch = segmentDateTime.getTime();
39778    var programTimeEpoch = programDateTime.getTime();
39779    return (programTimeEpoch - segmentTimeEpoch) / 1000;
39780  };
39781  /**
39782   * Checks that all segments in this playlist have programDateTime tags.
39783   *
39784   * @param {Object} playlist A playlist object
39785   */
39786
39787
39788  var verifyProgramDateTimeTags = function verifyProgramDateTimeTags(playlist) {
39789    if (!playlist.segments || playlist.segments.length === 0) {
39790      return false;
39791    }
39792
39793    for (var i = 0; i < playlist.segments.length; i++) {
39794      var segment = playlist.segments[i];
39795
39796      if (!segment.dateTimeObject) {
39797        return false;
39798      }
39799    }
39800
39801    return true;
39802  };
39803  /**
39804   * Returns the programTime of the media given a playlist and a playerTime.
39805   * The playlist must have programDateTime tags for a programDateTime tag to be returned.
39806   * If the segments containing the time requested have not been buffered yet, an estimate
39807   * may be returned to the callback.
39808   *
39809   * @param {Object} args
39810   * @param {Object} args.playlist A playlist object to search within
39811   * @param {Number} time A playerTime in seconds
39812   * @param {Function} callback(err, programTime)
39813   * @returns {String} err.message A detailed error message
39814   * @returns {Object} programTime
39815   * @returns {Number} programTime.mediaSeconds The streamTime in seconds
39816   * @returns {String} programTime.programDateTime The programTime as an ISO-8601 String
39817   */
39818
39819
39820  var getProgramTime = function getProgramTime(_ref) {
39821    var playlist = _ref.playlist,
39822        _ref$time = _ref.time,
39823        time = _ref$time === undefined ? undefined : _ref$time,
39824        callback = _ref.callback;
39825
39826    if (!callback) {
39827      throw new Error('getProgramTime: callback must be provided');
39828    }
39829
39830    if (!playlist || time === undefined) {
39831      return callback({
39832        message: 'getProgramTime: playlist and time must be provided'
39833      });
39834    }
39835
39836    var matchedSegment = findSegmentForPlayerTime(time, playlist);
39837
39838    if (!matchedSegment) {
39839      return callback({
39840        message: 'valid programTime was not found'
39841      });
39842    }
39843
39844    if (matchedSegment.type === 'estimate') {
39845      return callback({
39846        message: 'Accurate programTime could not be determined.' + ' Please seek to e.seekTime and try again',
39847        seekTime: matchedSegment.estimatedStart
39848      });
39849    }
39850
39851    var programTimeObject = {
39852      mediaSeconds: time
39853    };
39854    var programTime = playerTimeToProgramTime(time, matchedSegment.segment);
39855
39856    if (programTime) {
39857      programTimeObject.programDateTime = programTime.toISOString();
39858    }
39859
39860    return callback(null, programTimeObject);
39861  };
39862  /**
39863   * Seeks in the player to a time that matches the given programTime ISO-8601 string.
39864   *
39865   * @param {Object} args
39866   * @param {String} args.programTime A programTime to seek to as an ISO-8601 String
39867   * @param {Object} args.playlist A playlist to look within
39868   * @param {Number} args.retryCount The number of times to try for an accurate seek. Default is 2.
39869   * @param {Function} args.seekTo A method to perform a seek
39870   * @param {Boolean} args.pauseAfterSeek Whether to end in a paused state after seeking. Default is true.
39871   * @param {Object} args.tech The tech to seek on
39872   * @param {Function} args.callback(err, newTime) A callback to return the new time to
39873   * @returns {String} err.message A detailed error message
39874   * @returns {Number} newTime The exact time that was seeked to in seconds
39875   */
39876
39877
39878  var seekToProgramTime = function seekToProgramTime(_ref2) {
39879    var programTime = _ref2.programTime,
39880        playlist = _ref2.playlist,
39881        _ref2$retryCount = _ref2.retryCount,
39882        retryCount = _ref2$retryCount === undefined ? 2 : _ref2$retryCount,
39883        seekTo = _ref2.seekTo,
39884        _ref2$pauseAfterSeek = _ref2.pauseAfterSeek,
39885        pauseAfterSeek = _ref2$pauseAfterSeek === undefined ? true : _ref2$pauseAfterSeek,
39886        tech = _ref2.tech,
39887        callback = _ref2.callback;
39888
39889    if (!callback) {
39890      throw new Error('seekToProgramTime: callback must be provided');
39891    }
39892
39893    if (typeof programTime === 'undefined' || !playlist || !seekTo) {
39894      return callback({
39895        message: 'seekToProgramTime: programTime, seekTo and playlist must be provided'
39896      });
39897    }
39898
39899    if (!playlist.endList && !tech.hasStarted_) {
39900      return callback({
39901        message: 'player must be playing a live stream to start buffering'
39902      });
39903    }
39904
39905    if (!verifyProgramDateTimeTags(playlist)) {
39906      return callback({
39907        message: 'programDateTime tags must be provided in the manifest ' + playlist.resolvedUri
39908      });
39909    }
39910
39911    var matchedSegment = findSegmentForProgramTime(programTime, playlist); // no match
39912
39913    if (!matchedSegment) {
39914      return callback({
39915        message: programTime + ' was not found in the stream'
39916      });
39917    }
39918
39919    var segment = matchedSegment.segment;
39920    var mediaOffset = getOffsetFromTimestamp(segment.dateTimeObject, programTime);
39921
39922    if (matchedSegment.type === 'estimate') {
39923      // we've run out of retries
39924      if (retryCount === 0) {
39925        return callback({
39926          message: programTime + ' is not buffered yet. Try again'
39927        });
39928      }
39929
39930      seekTo(matchedSegment.estimatedStart + mediaOffset);
39931      tech.one('seeked', function () {
39932        seekToProgramTime({
39933          programTime: programTime,
39934          playlist: playlist,
39935          retryCount: retryCount - 1,
39936          seekTo: seekTo,
39937          pauseAfterSeek: pauseAfterSeek,
39938          tech: tech,
39939          callback: callback
39940        });
39941      });
39942      return;
39943    } // Since the segment.start value is determined from the buffered end or ending time
39944    // of the prior segment, the seekToTime doesn't need to account for any transmuxer
39945    // modifications.
39946
39947
39948    var seekToTime = segment.start + mediaOffset;
39949
39950    var seekedCallback = function seekedCallback() {
39951      return callback(null, tech.currentTime());
39952    }; // listen for seeked event
39953
39954
39955    tech.one('seeked', seekedCallback); // pause before seeking as video.js will restore this state
39956
39957    if (pauseAfterSeek) {
39958      tech.pause();
39959    }
39960
39961    seekTo(seekToTime);
39962  };
39963  /**
39964   * ranges
39965   *
39966   * Utilities for working with TimeRanges.
39967   *
39968   */
39969  // Fudge factor to account for TimeRanges rounding
39970
39971
39972  var TIME_FUDGE_FACTOR = 1 / 30; // Comparisons between time values such as current time and the end of the buffered range
39973  // can be misleading because of precision differences or when the current media has poorly
39974  // aligned audio and video, which can cause values to be slightly off from what you would
39975  // expect. This value is what we consider to be safe to use in such comparisons to account
39976  // for these scenarios.
39977
39978  var SAFE_TIME_DELTA = TIME_FUDGE_FACTOR * 3;
39979
39980  var filterRanges = function filterRanges(timeRanges, predicate) {
39981    var results = [];
39982    var i = void 0;
39983
39984    if (timeRanges && timeRanges.length) {
39985      // Search for ranges that match the predicate
39986      for (i = 0; i < timeRanges.length; i++) {
39987        if (predicate(timeRanges.start(i), timeRanges.end(i))) {
39988          results.push([timeRanges.start(i), timeRanges.end(i)]);
39989        }
39990      }
39991    }
39992
39993    return videojs$1.createTimeRanges(results);
39994  };
39995  /**
39996   * Attempts to find the buffered TimeRange that contains the specified
39997   * time.
39998   * @param {TimeRanges} buffered - the TimeRanges object to query
39999   * @param {number} time  - the time to filter on.
40000   * @returns {TimeRanges} a new TimeRanges object
40001   */
40002
40003
40004  var findRange = function findRange(buffered, time) {
40005    return filterRanges(buffered, function (start, end) {
40006      return start - SAFE_TIME_DELTA <= time && end + SAFE_TIME_DELTA >= time;
40007    });
vendor: 20,100 bytes, lines 40008-40642
40008  };
40009  /**
40010   * Returns the TimeRanges that begin later than the specified time.
40011   * @param {TimeRanges} timeRanges - the TimeRanges object to query
40012   * @param {number} time - the time to filter on.
40013   * @returns {TimeRanges} a new TimeRanges object.
40014   */
40015
40016
40017  var findNextRange = function findNextRange(timeRanges, time) {
40018    return filterRanges(timeRanges, function (start) {
40019      return start - TIME_FUDGE_FACTOR >= time;
40020    });
40021  };
40022  /**
40023   * Returns gaps within a list of TimeRanges
40024   * @param {TimeRanges} buffered - the TimeRanges object
40025   * @return {TimeRanges} a TimeRanges object of gaps
40026   */
40027
40028
40029  var findGaps = function findGaps(buffered) {
40030    if (buffered.length < 2) {
40031      return videojs$1.createTimeRanges();
40032    }
40033
40034    var ranges = [];
40035
40036    for (var i = 1; i < buffered.length; i++) {
40037      var start = buffered.end(i - 1);
40038      var end = buffered.start(i);
40039      ranges.push([start, end]);
40040    }
40041
40042    return videojs$1.createTimeRanges(ranges);
40043  };
40044  /**
40045   * Gets a human readable string for a TimeRange
40046   *
40047   * @param {TimeRange} range
40048   * @returns {String} a human readable string
40049   */
40050
40051
40052  var printableRange = function printableRange(range) {
40053    var strArr = [];
40054
40055    if (!range || !range.length) {
40056      return '';
40057    }
40058
40059    for (var i = 0; i < range.length; i++) {
40060      strArr.push(range.start(i) + ' => ' + range.end(i));
40061    }
40062
40063    return strArr.join(', ');
40064  };
40065  /**
40066   * Calculates the amount of time left in seconds until the player hits the end of the
40067   * buffer and causes a rebuffer
40068   *
40069   * @param {TimeRange} buffered
40070   *        The state of the buffer
40071   * @param {Numnber} currentTime
40072   *        The current time of the player
40073   * @param {Number} playbackRate
40074   *        The current playback rate of the player. Defaults to 1.
40075   * @return {Number}
40076   *         Time until the player has to start rebuffering in seconds.
40077   * @function timeUntilRebuffer
40078   */
40079
40080
40081  var timeUntilRebuffer = function timeUntilRebuffer(buffered, currentTime) {
40082    var playbackRate = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 1;
40083    var bufferedEnd = buffered.length ? buffered.end(buffered.length - 1) : 0;
40084    return (bufferedEnd - currentTime) / playbackRate;
40085  };
40086  /**
40087   * Converts a TimeRanges object into an array representation
40088   * @param {TimeRanges} timeRanges
40089   * @returns {Array}
40090   */
40091
40092
40093  var timeRangesToArray = function timeRangesToArray(timeRanges) {
40094    var timeRangesList = [];
40095
40096    for (var i = 0; i < timeRanges.length; i++) {
40097      timeRangesList.push({
40098        start: timeRanges.start(i),
40099        end: timeRanges.end(i)
40100      });
40101    }
40102
40103    return timeRangesList;
40104  };
40105  /**
40106   * @file create-text-tracks-if-necessary.js
40107   */
40108
40109  /**
40110   * Create text tracks on video.js if they exist on a segment.
40111   *
40112   * @param {Object} sourceBuffer the VSB or FSB
40113   * @param {Object} mediaSource the HTML media source
40114   * @param {Object} segment the segment that may contain the text track
40115   * @private
40116   */
40117
40118
40119  var createTextTracksIfNecessary = function createTextTracksIfNecessary(sourceBuffer, mediaSource, segment) {
40120    var player = mediaSource.player_; // create an in-band caption track if one is present in the segment
40121
40122    if (segment.captions && segment.captions.length) {
40123      if (!sourceBuffer.inbandTextTracks_) {
40124        sourceBuffer.inbandTextTracks_ = {};
40125      }
40126
40127      for (var trackId in segment.captionStreams) {
40128        if (!sourceBuffer.inbandTextTracks_[trackId]) {
40129          player.tech_.trigger({
40130            type: 'usage',
40131            name: 'hls-608'
40132          });
40133          var track = player.textTracks().getTrackById(trackId);
40134
40135          if (track) {
40136            // Resuse an existing track with a CC# id because this was
40137            // very likely created by videojs-contrib-hls from information
40138            // in the m3u8 for us to use
40139            sourceBuffer.inbandTextTracks_[trackId] = track;
40140          } else {
40141            // Otherwise, create a track with the default `CC#` label and
40142            // without a language
40143            sourceBuffer.inbandTextTracks_[trackId] = player.addRemoteTextTrack({
40144              kind: 'captions',
40145              id: trackId,
40146              label: trackId
40147            }, false).track;
40148          }
40149        }
40150      }
40151    }
40152
40153    if (segment.metadata && segment.metadata.length && !sourceBuffer.metadataTrack_) {
40154      sourceBuffer.metadataTrack_ = player.addRemoteTextTrack({
40155        kind: 'metadata',
40156        label: 'Timed Metadata'
40157      }, false).track;
40158      sourceBuffer.metadataTrack_.inBandMetadataTrackDispatchType = segment.metadata.dispatchType;
40159    }
40160  };
40161  /**
40162   * @file remove-cues-from-track.js
40163   */
40164
40165  /**
40166   * Remove cues from a track on video.js.
40167   *
40168   * @param {Double} start start of where we should remove the cue
40169   * @param {Double} end end of where the we should remove the cue
40170   * @param {Object} track the text track to remove the cues from
40171   * @private
40172   */
40173
40174
40175  var removeCuesFromTrack = function removeCuesFromTrack(start, end, track) {
40176    var i = void 0;
40177    var cue = void 0;
40178
40179    if (!track) {
40180      return;
40181    }
40182
40183    if (!track.cues) {
40184      return;
40185    }
40186
40187    i = track.cues.length;
40188
40189    while (i--) {
40190      cue = track.cues[i]; // Remove any overlapping cue
40191
40192      if (cue.startTime <= end && cue.endTime >= start) {
40193        track.removeCue(cue);
40194      }
40195    }
40196  };
40197  /**
40198   * @file add-text-track-data.js
40199   */
40200
40201  /**
40202   * Define properties on a cue for backwards compatability,
40203   * but warn the user that the way that they are using it
40204   * is depricated and will be removed at a later date.
40205   *
40206   * @param {Cue} cue the cue to add the properties on
40207   * @private
40208   */
40209
40210
40211  var deprecateOldCue = function deprecateOldCue(cue) {
40212    Object.defineProperties(cue.frame, {
40213      id: {
40214        get: function get() {
40215          videojs$1.log.warn('cue.frame.id is deprecated. Use cue.value.key instead.');
40216          return cue.value.key;
40217        }
40218      },
40219      value: {
40220        get: function get() {
40221          videojs$1.log.warn('cue.frame.value is deprecated. Use cue.value.data instead.');
40222          return cue.value.data;
40223        }
40224      },
40225      privateData: {
40226        get: function get() {
40227          videojs$1.log.warn('cue.frame.privateData is deprecated. Use cue.value.data instead.');
40228          return cue.value.data;
40229        }
40230      }
40231    });
40232  };
40233
40234  var durationOfVideo = function durationOfVideo(duration) {
40235    var dur = void 0;
40236
40237    if (isNaN(duration) || Math.abs(duration) === Infinity) {
40238      dur = Number.MAX_VALUE;
40239    } else {
40240      dur = duration;
40241    }
40242
40243    return dur;
40244  };
40245  /**
40246   * Add text track data to a source handler given the captions and
40247   * metadata from the buffer.
40248   *
40249   * @param {Object} sourceHandler the virtual source buffer
40250   * @param {Array} captionArray an array of caption data
40251   * @param {Array} metadataArray an array of meta data
40252   * @private
40253   */
40254
40255
40256  var addTextTrackData = function addTextTrackData(sourceHandler, captionArray, metadataArray) {
40257    var Cue = window$1.WebKitDataCue || window$1.VTTCue;
40258
40259    if (captionArray) {
40260      captionArray.forEach(function (caption) {
40261        var track = caption.stream;
40262        this.inbandTextTracks_[track].addCue(new Cue(caption.startTime + this.timestampOffset, caption.endTime + this.timestampOffset, caption.text));
40263      }, sourceHandler);
40264    }
40265
40266    if (metadataArray) {
40267      var videoDuration = durationOfVideo(sourceHandler.mediaSource_.duration);
40268      metadataArray.forEach(function (metadata) {
40269        var time = metadata.cueTime + this.timestampOffset; // if time isn't a finite number between 0 and Infinity, like NaN,
40270        // ignore this bit of metadata.
40271        // This likely occurs when you have an non-timed ID3 tag like TIT2,
40272        // which is the "Title/Songname/Content description" frame
40273
40274        if (typeof time !== 'number' || window$1.isNaN(time) || time < 0 || !(time < Infinity)) {
40275          return;
40276        }
40277
40278        metadata.frames.forEach(function (frame) {
40279          var cue = new Cue(time, time, frame.value || frame.url || frame.data || '');
40280          cue.frame = frame;
40281          cue.value = frame;
40282          deprecateOldCue(cue);
40283          this.metadataTrack_.addCue(cue);
40284        }, this);
40285      }, sourceHandler); // Updating the metadeta cues so that
40286      // the endTime of each cue is the startTime of the next cue
40287      // the endTime of last cue is the duration of the video
40288
40289      if (sourceHandler.metadataTrack_ && sourceHandler.metadataTrack_.cues && sourceHandler.metadataTrack_.cues.length) {
40290        var cues = sourceHandler.metadataTrack_.cues;
40291        var cuesArray = []; // Create a copy of the TextTrackCueList...
40292        // ...disregarding cues with a falsey value
40293
40294        for (var i = 0; i < cues.length; i++) {
40295          if (cues[i]) {
40296            cuesArray.push(cues[i]);
40297          }
40298        } // Group cues by their startTime value
40299
40300
40301        var cuesGroupedByStartTime = cuesArray.reduce(function (obj, cue) {
40302          var timeSlot = obj[cue.startTime] || [];
40303          timeSlot.push(cue);
40304          obj[cue.startTime] = timeSlot;
40305          return obj;
40306        }, {}); // Sort startTimes by ascending order
40307
40308        var sortedStartTimes = Object.keys(cuesGroupedByStartTime).sort(function (a, b) {
40309          return Number(a) - Number(b);
40310        }); // Map each cue group's endTime to the next group's startTime
40311
40312        sortedStartTimes.forEach(function (startTime, idx) {
40313          var cueGroup = cuesGroupedByStartTime[startTime];
40314          var nextTime = Number(sortedStartTimes[idx + 1]) || videoDuration; // Map each cue's endTime the next group's startTime
40315
40316          cueGroup.forEach(function (cue) {
40317            cue.endTime = nextTime;
40318          });
40319        });
40320      }
40321    }
40322  };
40323
40324  var win = typeof window !== 'undefined' ? window : {},
40325      TARGET = typeof Symbol === 'undefined' ? '__target' : Symbol(),
40326      SCRIPT_TYPE = 'application/javascript',
40327      BlobBuilder = win.BlobBuilder || win.WebKitBlobBuilder || win.MozBlobBuilder || win.MSBlobBuilder,
40328      URL = win.URL || win.webkitURL || URL && URL.msURL,
40329      Worker = win.Worker;
40330  /**
40331   * Returns a wrapper around Web Worker code that is constructible.
40332   *
40333   * @function shimWorker
40334   *
40335   * @param { String }    filename    The name of the file
40336   * @param { Function }  fn          Function wrapping the code of the worker
40337   */
40338
40339  function shimWorker(filename, fn) {
40340    return function ShimWorker(forceFallback) {
40341      var o = this;
40342
40343      if (!fn) {
40344        return new Worker(filename);
40345      } else if (Worker && !forceFallback) {
40346        // Convert the function's inner code to a string to construct the worker
40347        var source = fn.toString().replace(/^function.+?{/, '').slice(0, -1),
40348            objURL = createSourceObject(source);
40349        this[TARGET] = new Worker(objURL);
40350        wrapTerminate(this[TARGET], objURL);
40351        return this[TARGET];
40352      } else {
40353        var selfShim = {
40354          postMessage: function postMessage(m) {
40355            if (o.onmessage) {
40356              setTimeout(function () {
40357                o.onmessage({
40358                  data: m,
40359                  target: selfShim
40360                });
40361              });
40362            }
40363          }
40364        };
40365        fn.call(selfShim);
40366
40367        this.postMessage = function (m) {
40368          setTimeout(function () {
40369            selfShim.onmessage({
40370              data: m,
40371              target: o
40372            });
40373          });
40374        };
40375
40376        this.isThisThread = true;
40377      }
40378    };
40379  } // Test Worker capabilities
40380
40381
40382  if (Worker) {
40383    var testWorker,
40384        objURL = createSourceObject('self.onmessage = function () {}'),
40385        testArray = new Uint8Array(1);
40386
40387    try {
40388      testWorker = new Worker(objURL); // Native browser on some Samsung devices throws for transferables, let's detect it
40389
40390      testWorker.postMessage(testArray, [testArray.buffer]);
40391    } catch (e) {
40392      Worker = null;
40393    } finally {
40394      URL.revokeObjectURL(objURL);
40395
40396      if (testWorker) {
40397        testWorker.terminate();
40398      }
40399    }
40400  }
40401
40402  function createSourceObject(str) {
40403    try {
40404      return URL.createObjectURL(new Blob([str], {
40405        type: SCRIPT_TYPE
40406      }));
40407    } catch (e) {
40408      var blob = new BlobBuilder();
40409      blob.append(str);
40410      return URL.createObjectURL(blob.getBlob(type));
40411    }
40412  }
40413
40414  function wrapTerminate(worker, objURL) {
40415    if (!worker || !objURL) return;
40416    var term = worker.terminate;
40417    worker.objURL = objURL;
40418
40419    worker.terminate = function () {
40420      if (worker.objURL) URL.revokeObjectURL(worker.objURL);
40421      term.call(worker);
40422    };
40423  }
40424
40425  var TransmuxWorker = new shimWorker("./transmuxer-worker.worker.js", function (window, document$$1) {
40426    var self = this;
40427
40428    var transmuxerWorker = function () {
40429      /**
40430       * mux.js
40431       *
40432       * Copyright (c) Brightcove
40433       * Licensed Apache-2.0 https://github.com/videojs/mux.js/blob/master/LICENSE
40434       *
40435       * A lightweight readable stream implemention that handles event dispatching.
40436       * Objects that inherit from streams should call init in their constructors.
40437       */
40438      var Stream = function Stream() {
40439        this.init = function () {
40440          var listeners = {};
40441          /**
40442           * Add a listener for a specified event type.
40443           * @param type {string} the event name
40444           * @param listener {function} the callback to be invoked when an event of
40445           * the specified type occurs
40446           */
40447
40448          this.on = function (type, listener) {
40449            if (!listeners[type]) {
40450              listeners[type] = [];
40451            }
40452
40453            listeners[type] = listeners[type].concat(listener);
40454          };
40455          /**
40456           * Remove a listener for a specified event type.
40457           * @param type {string} the event name
40458           * @param listener {function} a function previously registered for this
40459           * type of event through `on`
40460           */
40461
40462
40463          this.off = function (type, listener) {
40464            var index;
40465
40466            if (!listeners[type]) {
40467              return false;
40468            }
40469
40470            index = listeners[type].indexOf(listener);
40471            listeners[type] = listeners[type].slice();
40472            listeners[type].splice(index, 1);
40473            return index > -1;
40474          };
40475          /**
40476           * Trigger an event of the specified type on this stream. Any additional
40477           * arguments to this function are passed as parameters to event listeners.
40478           * @param type {string} the event name
40479           */
40480
40481
40482          this.trigger = function (type) {
40483            var callbacks, i, length, args;
40484            callbacks = listeners[type];
40485
40486            if (!callbacks) {
40487              return;
40488            } // Slicing the arguments on every invocation of this method
40489            // can add a significant amount of overhead. Avoid the
40490            // intermediate object creation for the common case of a
40491            // single callback argument
40492
40493
40494            if (arguments.length === 2) {
40495              length = callbacks.length;
40496
40497              for (i = 0; i < length; ++i) {
40498                callbacks[i].call(this, arguments[1]);
40499              }
40500            } else {
40501              args = [];
40502              i = arguments.length;
40503
40504              for (i = 1; i < arguments.length; ++i) {
40505                args.push(arguments[i]);
40506              }
40507
40508              length = callbacks.length;
40509
40510              for (i = 0; i < length; ++i) {
40511                callbacks[i].apply(this, args);
40512              }
40513            }
40514          };
40515          /**
40516           * Destroys the stream and cleans up.
40517           */
40518
40519
40520          this.dispose = function () {
40521            listeners = {};
40522          };
40523        };
40524      };
40525      /**
40526       * Forwards all `data` events on this stream to the destination stream. The
40527       * destination stream should provide a method `push` to receive the data
40528       * events as they arrive.
40529       * @param destination {stream} the stream that will receive all `data` events
40530       * @param autoFlush {boolean} if false, we will not call `flush` on the destination
40531       *                            when the current stream emits a 'done' event
40532       * @see http://nodejs.org/api/stream.html#stream_readable_pipe_destination_options
40533       */
40534
40535
40536      Stream.prototype.pipe = function (destination) {
40537        this.on('data', function (data) {
40538          destination.push(data);
40539        });
40540        this.on('done', function (flushSource) {
40541          destination.flush(flushSource);
40542        });
40543        this.on('partialdone', function (flushSource) {
40544          destination.partialFlush(flushSource);
40545        });
40546        this.on('endedtimeline', function (flushSource) {
40547          destination.endTimeline(flushSource);
40548        });
40549        this.on('reset', function (flushSource) {
40550          destination.reset(flushSource);
40551        });
40552        return destination;
40553      }; // Default stream functions that are expected to be overridden to perform
40554      // actual work. These are provided by the prototype as a sort of no-op
40555      // implementation so that we don't have to check for their existence in the
40556      // `pipe` function above.
40557
40558
40559      Stream.prototype.push = function (data) {
40560        this.trigger('data', data);
40561      };
40562
40563      Stream.prototype.flush = function (flushSource) {
40564        this.trigger('done', flushSource);
40565      };
40566
40567      Stream.prototype.partialFlush = function (flushSource) {
40568        this.trigger('partialdone', flushSource);
40569      };
40570
40571      Stream.prototype.endTimeline = function (flushSource) {
40572        this.trigger('endedtimeline', flushSource);
40573      };
40574
40575      Stream.prototype.reset = function (flushSource) {
40576        this.trigger('reset', flushSource);
40577      };
40578
40579      var stream = Stream;
40580      /**
40581       * mux.js
40582       *
40583       * Copyright (c) Brightcove
40584       * Licensed Apache-2.0 https://github.com/videojs/mux.js/blob/master/LICENSE
40585       *
40586       * Functions that generate fragmented MP4s suitable for use with Media
40587       * Source Extensions.
40588       */
40589
40590      var UINT32_MAX = Math.pow(2, 32) - 1;
40591      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
40592
40593      (function () {
40594        var i;
40595        types = {
40596          avc1: [],
40597          // codingname
40598          avcC: [],
40599          btrt: [],
40600          dinf: [],
40601          dref: [],
40602          esds: [],
40603          ftyp: [],
40604          hdlr: [],
40605          mdat: [],
40606          mdhd: [],
40607          mdia: [],
40608          mfhd: [],
40609          minf: [],
40610          moof: [],
40611          moov: [],
40612          mp4a: [],
40613          // codingname
40614          mvex: [],
40615          mvhd: [],
40616          pasp: [],
40617          sdtp: [],
40618          smhd: [],
40619          stbl: [],
40620          stco: [],
40621          stsc: [],
40622          stsd: [],
40623          stsz: [],
40624          stts: [],
40625          styp: [],
40626          tfdt: [],
40627          tfhd: [],
40628          traf: [],
40629          trak: [],
40630          trun: [],
40631          trex: [],
40632          tkhd: [],
40633          vmhd: []
40634        }; // In environments where Uint8Array is undefined (e.g., IE8), skip set up so that we
40635        // don't throw an error
40636
40637        if (typeof Uint8Array === 'undefined') {
40638          return;
40639        }
40640
40641        for (i in types) {
40642          if (types.hasOwnProperty(i)) {
vendor: 16,384 bytes, lines 40643-41012
40643            types[i] = [i.charCodeAt(0), i.charCodeAt(1), i.charCodeAt(2), i.charCodeAt(3)];
40644          }
40645        }
40646
40647        MAJOR_BRAND = new Uint8Array(['i'.charCodeAt(0), 's'.charCodeAt(0), 'o'.charCodeAt(0), 'm'.charCodeAt(0)]);
40648        AVC1_BRAND = new Uint8Array(['a'.charCodeAt(0), 'v'.charCodeAt(0), 'c'.charCodeAt(0), '1'.charCodeAt(0)]);
40649        MINOR_VERSION = new Uint8Array([0, 0, 0, 1]);
40650        VIDEO_HDLR = new Uint8Array([0x00, // version 0
40651        0x00, 0x00, 0x00, // flags
40652        0x00, 0x00, 0x00, 0x00, // pre_defined
40653        0x76, 0x69, 0x64, 0x65, // handler_type: 'vide'
40654        0x00, 0x00, 0x00, 0x00, // reserved
40655        0x00, 0x00, 0x00, 0x00, // reserved
40656        0x00, 0x00, 0x00, 0x00, // reserved
40657        0x56, 0x69, 0x64, 0x65, 0x6f, 0x48, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x72, 0x00 // name: 'VideoHandler'
40658        ]);
40659        AUDIO_HDLR = new Uint8Array([0x00, // version 0
40660        0x00, 0x00, 0x00, // flags
40661        0x00, 0x00, 0x00, 0x00, // pre_defined
40662        0x73, 0x6f, 0x75, 0x6e, // handler_type: 'soun'
40663        0x00, 0x00, 0x00, 0x00, // reserved
40664        0x00, 0x00, 0x00, 0x00, // reserved
40665        0x00, 0x00, 0x00, 0x00, // reserved
40666        0x53, 0x6f, 0x75, 0x6e, 0x64, 0x48, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x72, 0x00 // name: 'SoundHandler'
40667        ]);
40668        HDLR_TYPES = {
40669          video: VIDEO_HDLR,
40670          audio: AUDIO_HDLR
40671        };
40672        DREF = new Uint8Array([0x00, // version 0
40673        0x00, 0x00, 0x00, // flags
40674        0x00, 0x00, 0x00, 0x01, // entry_count
40675        0x00, 0x00, 0x00, 0x0c, // entry_size
40676        0x75, 0x72, 0x6c, 0x20, // 'url' type
40677        0x00, // version 0
40678        0x00, 0x00, 0x01 // entry_flags
40679        ]);
40680        SMHD = new Uint8Array([0x00, // version
40681        0x00, 0x00, 0x00, // flags
40682        0x00, 0x00, // balance, 0 means centered
40683        0x00, 0x00 // reserved
40684        ]);
40685        STCO = new Uint8Array([0x00, // version
40686        0x00, 0x00, 0x00, // flags
40687        0x00, 0x00, 0x00, 0x00 // entry_count
40688        ]);
40689        STSC = STCO;
40690        STSZ = new Uint8Array([0x00, // version
40691        0x00, 0x00, 0x00, // flags
40692        0x00, 0x00, 0x00, 0x00, // sample_size
40693        0x00, 0x00, 0x00, 0x00 // sample_count
40694        ]);
40695        STTS = STCO;
40696        VMHD = new Uint8Array([0x00, // version
40697        0x00, 0x00, 0x01, // flags
40698        0x00, 0x00, // graphicsmode
40699        0x00, 0x00, 0x00, 0x00, 0x00, 0x00 // opcolor
40700        ]);
40701      })();
40702
40703      box = function box(type) {
40704        var payload = [],
40705            size = 0,
40706            i,
40707            result,
40708            view;
40709
40710        for (i = 1; i < arguments.length; i++) {
40711          payload.push(arguments[i]);
40712        }
40713
40714        i = payload.length; // calculate the total size we need to allocate
40715
40716        while (i--) {
40717          size += payload[i].byteLength;
40718        }
40719
40720        result = new Uint8Array(size + 8);
40721        view = new DataView(result.buffer, result.byteOffset, result.byteLength);
40722        view.setUint32(0, result.byteLength);
40723        result.set(type, 4); // copy the payload into the result
40724
40725        for (i = 0, size = 8; i < payload.length; i++) {
40726          result.set(payload[i], size);
40727          size += payload[i].byteLength;
40728        }
40729
40730        return result;
40731      };
40732
40733      dinf = function dinf() {
40734        return box(types.dinf, box(types.dref, DREF));
40735      };
40736
40737      esds = function esds(track) {
40738        return box(types.esds, new Uint8Array([0x00, // version
40739        0x00, 0x00, 0x00, // flags
40740        // ES_Descriptor
40741        0x03, // tag, ES_DescrTag
40742        0x19, // length
40743        0x00, 0x00, // ES_ID
40744        0x00, // streamDependenceFlag, URL_flag, reserved, streamPriority
40745        // DecoderConfigDescriptor
40746        0x04, // tag, DecoderConfigDescrTag
40747        0x11, // length
40748        0x40, // object type
40749        0x15, // streamType
40750        0x00, 0x06, 0x00, // bufferSizeDB
40751        0x00, 0x00, 0xda, 0xc0, // maxBitrate
40752        0x00, 0x00, 0xda, 0xc0, // avgBitrate
40753        // DecoderSpecificInfo
40754        0x05, // tag, DecoderSpecificInfoTag
40755        0x02, // length
40756        // ISO/IEC 14496-3, AudioSpecificConfig
40757        // for samplingFrequencyIndex see ISO/IEC 13818-7:2006, 8.1.3.2.2, Table 35
40758        track.audioobjecttype << 3 | track.samplingfrequencyindex >>> 1, track.samplingfrequencyindex << 7 | track.channelcount << 3, 0x06, 0x01, 0x02 // GASpecificConfig
40759        ]));
40760      };
40761
40762      ftyp = function ftyp() {
40763        return box(types.ftyp, MAJOR_BRAND, MINOR_VERSION, MAJOR_BRAND, AVC1_BRAND);
40764      };
40765
40766      hdlr = function hdlr(type) {
40767        return box(types.hdlr, HDLR_TYPES[type]);
40768      };
40769
40770      mdat = function mdat(data) {
40771        return box(types.mdat, data);
40772      };
40773
40774      mdhd = function mdhd(track) {
40775        var result = new Uint8Array([0x00, // version 0
40776        0x00, 0x00, 0x00, // flags
40777        0x00, 0x00, 0x00, 0x02, // creation_time
40778        0x00, 0x00, 0x00, 0x03, // modification_time
40779        0x00, 0x01, 0x5f, 0x90, // timescale, 90,000 "ticks" per second
40780        track.duration >>> 24 & 0xFF, track.duration >>> 16 & 0xFF, track.duration >>> 8 & 0xFF, track.duration & 0xFF, // duration
40781        0x55, 0xc4, // 'und' language (undetermined)
40782        0x00, 0x00]); // Use the sample rate from the track metadata, when it is
40783        // defined. The sample rate can be parsed out of an ADTS header, for
40784        // instance.
40785
40786        if (track.samplerate) {
40787          result[12] = track.samplerate >>> 24 & 0xFF;
40788          result[13] = track.samplerate >>> 16 & 0xFF;
40789          result[14] = track.samplerate >>> 8 & 0xFF;
40790          result[15] = track.samplerate & 0xFF;
40791        }
40792
40793        return box(types.mdhd, result);
40794      };
40795
40796      mdia = function mdia(track) {
40797        return box(types.mdia, mdhd(track), hdlr(track.type), minf(track));
40798      };
40799
40800      mfhd = function mfhd(sequenceNumber) {
40801        return box(types.mfhd, new Uint8Array([0x00, 0x00, 0x00, 0x00, // flags
40802        (sequenceNumber & 0xFF000000) >> 24, (sequenceNumber & 0xFF0000) >> 16, (sequenceNumber & 0xFF00) >> 8, sequenceNumber & 0xFF // sequence_number
40803        ]));
40804      };
40805
40806      minf = function minf(track) {
40807        return box(types.minf, track.type === 'video' ? box(types.vmhd, VMHD) : box(types.smhd, SMHD), dinf(), stbl(track));
40808      };
40809
40810      moof = function moof(sequenceNumber, tracks) {
40811        var trackFragments = [],
40812            i = tracks.length; // build traf boxes for each track fragment
40813
40814        while (i--) {
40815          trackFragments[i] = traf(tracks[i]);
40816        }
40817
40818        return box.apply(null, [types.moof, mfhd(sequenceNumber)].concat(trackFragments));
40819      };
40820      /**
40821       * Returns a movie box.
40822       * @param tracks {array} the tracks associated with this movie
40823       * @see ISO/IEC 14496-12:2012(E), section 8.2.1
40824       */
40825
40826
40827      moov = function moov(tracks) {
40828        var i = tracks.length,
40829            boxes = [];
40830
40831        while (i--) {
40832          boxes[i] = trak(tracks[i]);
40833        }
40834
40835        return box.apply(null, [types.moov, mvhd(0xffffffff)].concat(boxes).concat(mvex(tracks)));
40836      };
40837
40838      mvex = function mvex(tracks) {
40839        var i = tracks.length,
40840            boxes = [];
40841
40842        while (i--) {
40843          boxes[i] = trex(tracks[i]);
40844        }
40845
40846        return box.apply(null, [types.mvex].concat(boxes));
40847      };
40848
40849      mvhd = function mvhd(duration) {
40850        var bytes = new Uint8Array([0x00, // version 0
40851        0x00, 0x00, 0x00, // flags
40852        0x00, 0x00, 0x00, 0x01, // creation_time
40853        0x00, 0x00, 0x00, 0x02, // modification_time
40854        0x00, 0x01, 0x5f, 0x90, // timescale, 90,000 "ticks" per second
40855        (duration & 0xFF000000) >> 24, (duration & 0xFF0000) >> 16, (duration & 0xFF00) >> 8, duration & 0xFF, // duration
40856        0x00, 0x01, 0x00, 0x00, // 1.0 rate
40857        0x01, 0x00, // 1.0 volume
40858        0x00, 0x00, // reserved
40859        0x00, 0x00, 0x00, 0x00, // reserved
40860        0x00, 0x00, 0x00, 0x00, // reserved
40861        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
40862        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
40863        0xff, 0xff, 0xff, 0xff // next_track_ID
40864        ]);
40865        return box(types.mvhd, bytes);
40866      };
40867
40868      sdtp = function sdtp(track) {
40869        var samples = track.samples || [],
40870            bytes = new Uint8Array(4 + samples.length),
40871            flags,
40872            i; // leave the full box header (4 bytes) all zero
40873        // write the sample table
40874
40875        for (i = 0; i < samples.length; i++) {
40876          flags = samples[i].flags;
40877          bytes[i + 4] = flags.dependsOn << 4 | flags.isDependedOn << 2 | flags.hasRedundancy;
40878        }
40879
40880        return box(types.sdtp, bytes);
40881      };
40882
40883      stbl = function stbl(track) {
40884        return box(types.stbl, stsd(track), box(types.stts, STTS), box(types.stsc, STSC), box(types.stsz, STSZ), box(types.stco, STCO));
40885      };
40886
40887      (function () {
40888        var videoSample, audioSample;
40889
40890        stsd = function stsd(track) {
40891          return box(types.stsd, new Uint8Array([0x00, // version 0
40892          0x00, 0x00, 0x00, // flags
40893          0x00, 0x00, 0x00, 0x01]), track.type === 'video' ? videoSample(track) : audioSample(track));
40894        };
40895
40896        videoSample = function videoSample(track) {
40897          var sps = track.sps || [],
40898              pps = track.pps || [],
40899              sequenceParameterSets = [],
40900              pictureParameterSets = [],
40901              i,
40902              avc1Box; // assemble the SPSs
40903
40904          for (i = 0; i < sps.length; i++) {
40905            sequenceParameterSets.push((sps[i].byteLength & 0xFF00) >>> 8);
40906            sequenceParameterSets.push(sps[i].byteLength & 0xFF); // sequenceParameterSetLength
40907
40908            sequenceParameterSets = sequenceParameterSets.concat(Array.prototype.slice.call(sps[i])); // SPS
40909          } // assemble the PPSs
40910
40911
40912          for (i = 0; i < pps.length; i++) {
40913            pictureParameterSets.push((pps[i].byteLength & 0xFF00) >>> 8);
40914            pictureParameterSets.push(pps[i].byteLength & 0xFF);
40915            pictureParameterSets = pictureParameterSets.concat(Array.prototype.slice.call(pps[i]));
40916          }
40917
40918          avc1Box = [types.avc1, new Uint8Array([0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // reserved
40919          0x00, 0x01, // data_reference_index
40920          0x00, 0x00, // pre_defined
40921          0x00, 0x00, // reserved
40922          0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // pre_defined
40923          (track.width & 0xff00) >> 8, track.width & 0xff, // width
40924          (track.height & 0xff00) >> 8, track.height & 0xff, // height
40925          0x00, 0x48, 0x00, 0x00, // horizresolution
40926          0x00, 0x48, 0x00, 0x00, // vertresolution
40927          0x00, 0x00, 0x00, 0x00, // reserved
40928          0x00, 0x01, // frame_count
40929          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
40930          0x00, 0x18, // depth = 24
40931          0x11, 0x11 // pre_defined = -1
40932          ]), box(types.avcC, new Uint8Array([0x01, // configurationVersion
40933          track.profileIdc, // AVCProfileIndication
40934          track.profileCompatibility, // profile_compatibility
40935          track.levelIdc, // AVCLevelIndication
40936          0xff // lengthSizeMinusOne, hard-coded to 4 bytes
40937          ].concat([sps.length], // numOfSequenceParameterSets
40938          sequenceParameterSets, // "SPS"
40939          [pps.length], // numOfPictureParameterSets
40940          pictureParameterSets // "PPS"
40941          ))), box(types.btrt, new Uint8Array([0x00, 0x1c, 0x9c, 0x80, // bufferSizeDB
40942          0x00, 0x2d, 0xc6, 0xc0, // maxBitrate
40943          0x00, 0x2d, 0xc6, 0xc0 // avgBitrate
40944          ]))];
40945
40946          if (track.sarRatio) {
40947            var hSpacing = track.sarRatio[0],
40948                vSpacing = track.sarRatio[1];
40949            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])));
40950          }
40951
40952          return box.apply(null, avc1Box);
40953        };
40954
40955        audioSample = function audioSample(track) {
40956          return box(types.mp4a, new Uint8Array([// SampleEntry, ISO/IEC 14496-12
40957          0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // reserved
40958          0x00, 0x01, // data_reference_index
40959          // AudioSampleEntry, ISO/IEC 14496-12
40960          0x00, 0x00, 0x00, 0x00, // reserved
40961          0x00, 0x00, 0x00, 0x00, // reserved
40962          (track.channelcount & 0xff00) >> 8, track.channelcount & 0xff, // channelcount
40963          (track.samplesize & 0xff00) >> 8, track.samplesize & 0xff, // samplesize
40964          0x00, 0x00, // pre_defined
40965          0x00, 0x00, // reserved
40966          (track.samplerate & 0xff00) >> 8, track.samplerate & 0xff, 0x00, 0x00 // samplerate, 16.16
40967          // MP4AudioSampleEntry, ISO/IEC 14496-14
40968          ]), esds(track));
40969        };
40970      })();
40971
40972      tkhd = function tkhd(track) {
40973        var result = new Uint8Array([0x00, // version 0
40974        0x00, 0x00, 0x07, // flags
40975        0x00, 0x00, 0x00, 0x00, // creation_time
40976        0x00, 0x00, 0x00, 0x00, // modification_time
40977        (track.id & 0xFF000000) >> 24, (track.id & 0xFF0000) >> 16, (track.id & 0xFF00) >> 8, track.id & 0xFF, // track_ID
40978        0x00, 0x00, 0x00, 0x00, // reserved
40979        (track.duration & 0xFF000000) >> 24, (track.duration & 0xFF0000) >> 16, (track.duration & 0xFF00) >> 8, track.duration & 0xFF, // duration
40980        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // reserved
40981        0x00, 0x00, // layer
40982        0x00, 0x00, // alternate_group
40983        0x01, 0x00, // non-audio track volume
40984        0x00, 0x00, // reserved
40985        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
40986        (track.width & 0xFF00) >> 8, track.width & 0xFF, 0x00, 0x00, // width
40987        (track.height & 0xFF00) >> 8, track.height & 0xFF, 0x00, 0x00 // height
40988        ]);
40989        return box(types.tkhd, result);
40990      };
40991      /**
40992       * Generate a track fragment (traf) box. A traf box collects metadata
40993       * about tracks in a movie fragment (moof) box.
40994       */
40995
40996
40997      traf = function traf(track) {
40998        var trackFragmentHeader, trackFragmentDecodeTime, trackFragmentRun, sampleDependencyTable, dataOffset, upperWordBaseMediaDecodeTime, lowerWordBaseMediaDecodeTime;
40999        trackFragmentHeader = box(types.tfhd, new Uint8Array([0x00, // version 0
41000        0x00, 0x00, 0x3a, // flags
41001        (track.id & 0xFF000000) >> 24, (track.id & 0xFF0000) >> 16, (track.id & 0xFF00) >> 8, track.id & 0xFF, // track_ID
41002        0x00, 0x00, 0x00, 0x01, // sample_description_index
41003        0x00, 0x00, 0x00, 0x00, // default_sample_duration
41004        0x00, 0x00, 0x00, 0x00, // default_sample_size
41005        0x00, 0x00, 0x00, 0x00 // default_sample_flags
41006        ]));
41007        upperWordBaseMediaDecodeTime = Math.floor(track.baseMediaDecodeTime / (UINT32_MAX + 1));
41008        lowerWordBaseMediaDecodeTime = Math.floor(track.baseMediaDecodeTime % (UINT32_MAX + 1));
41009        trackFragmentDecodeTime = box(types.tfdt, new Uint8Array([0x01, // version 1
41010        0x00, 0x00, 0x00, // flags
41011        // baseMediaDecodeTime
41012        upperWordBaseMediaDecodeTime >>> 24 & 0xFF, upperWordBaseMediaDecodeTime >>> 16 & 0xFF, upperWordBaseMediaDecodeTime >>> 8 & 0xFF, upperWordBaseMediaDecodeTime & 0xFF, lowerWordBaseMediaDecodeTime >>> 24 & 0xFF, lowerWordBaseMediaDecodeTime >>> 16 & 0xFF, lowerWordBaseMediaDecodeTime >>> 8 & 0xFF, lowerWordBaseMediaDecodeTime & 
vendor: 10,330 bytes, lines 41012-41257
410120xFF])); // the data offset specifies the number of bytes from the start of
41013        // the containing moof to the first payload byte of the associated
41014        // mdat
41015
41016        dataOffset = 32 + // tfhd
41017        20 + // tfdt
41018        8 + // traf header
41019        16 + // mfhd
41020        8 + // moof header
41021        8; // mdat header
41022        // audio tracks require less metadata
41023
41024        if (track.type === 'audio') {
41025          trackFragmentRun = trun(track, dataOffset);
41026          return box(types.traf, trackFragmentHeader, trackFragmentDecodeTime, trackFragmentRun);
41027        } // video tracks should contain an independent and disposable samples
41028        // box (sdtp)
41029        // generate one and adjust offsets to match
41030
41031
41032        sampleDependencyTable = sdtp(track);
41033        trackFragmentRun = trun(track, sampleDependencyTable.length + dataOffset);
41034        return box(types.traf, trackFragmentHeader, trackFragmentDecodeTime, trackFragmentRun, sampleDependencyTable);
41035      };
41036      /**
41037       * Generate a track box.
41038       * @param track {object} a track definition
41039       * @return {Uint8Array} the track box
41040       */
41041
41042
41043      trak = function trak(track) {
41044        track.duration = track.duration || 0xffffffff;
41045        return box(types.trak, tkhd(track), mdia(track));
41046      };
41047
41048      trex = function trex(track) {
41049        var result = new Uint8Array([0x00, // version 0
41050        0x00, 0x00, 0x00, // flags
41051        (track.id & 0xFF000000) >> 24, (track.id & 0xFF0000) >> 16, (track.id & 0xFF00) >> 8, track.id & 0xFF, // track_ID
41052        0x00, 0x00, 0x00, 0x01, // default_sample_description_index
41053        0x00, 0x00, 0x00, 0x00, // default_sample_duration
41054        0x00, 0x00, 0x00, 0x00, // default_sample_size
41055        0x00, 0x01, 0x00, 0x01 // default_sample_flags
41056        ]); // the last two bytes of default_sample_flags is the sample
41057        // degradation priority, a hint about the importance of this sample
41058        // relative to others. Lower the degradation priority for all sample
41059        // types other than video.
41060
41061        if (track.type !== 'video') {
41062          result[result.length - 1] = 0x00;
41063        }
41064
41065        return box(types.trex, result);
41066      };
41067
41068      (function () {
41069        var audioTrun, videoTrun, trunHeader; // This method assumes all samples are uniform. That is, if a
41070        // duration is present for the first sample, it will be present for
41071        // all subsequent samples.
41072        // see ISO/IEC 14496-12:2012, Section 8.8.8.1
41073
41074        trunHeader = function trunHeader(samples, offset) {
41075          var durationPresent = 0,
41076              sizePresent = 0,
41077              flagsPresent = 0,
41078              compositionTimeOffset = 0; // trun flag constants
41079
41080          if (samples.length) {
41081            if (samples[0].duration !== undefined) {
41082              durationPresent = 0x1;
41083            }
41084
41085            if (samples[0].size !== undefined) {
41086              sizePresent = 0x2;
41087            }
41088
41089            if (samples[0].flags !== undefined) {
41090              flagsPresent = 0x4;
41091            }
41092
41093            if (samples[0].compositionTimeOffset !== undefined) {
41094              compositionTimeOffset = 0x8;
41095            }
41096          }
41097
41098          return [0x00, // version 0
41099          0x00, durationPresent | sizePresent | flagsPresent | compositionTimeOffset, 0x01, // flags
41100          (samples.length & 0xFF000000) >>> 24, (samples.length & 0xFF0000) >>> 16, (samples.length & 0xFF00) >>> 8, samples.length & 0xFF, // sample_count
41101          (offset & 0xFF000000) >>> 24, (offset & 0xFF0000) >>> 16, (offset & 0xFF00) >>> 8, offset & 0xFF // data_offset
41102          ];
41103        };
41104
41105        videoTrun = function videoTrun(track, offset) {
41106          var bytes, samples, sample, i;
41107          samples = track.samples || [];
41108          offset += 8 + 12 + 16 * samples.length;
41109          bytes = trunHeader(samples, offset);
41110
41111          for (i = 0; i < samples.length; i++) {
41112            sample = samples[i];
41113            bytes = bytes.concat([(sample.duration & 0xFF000000) >>> 24, (sample.duration & 0xFF0000) >>> 16, (sample.duration & 0xFF00) >>> 8, sample.duration & 0xFF, // sample_duration
41114            (sample.size & 0xFF000000) >>> 24, (sample.size & 0xFF0000) >>> 16, (sample.size & 0xFF00) >>> 8, sample.size & 0xFF, // sample_size
41115            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
41116            (sample.compositionTimeOffset & 0xFF000000) >>> 24, (sample.compositionTimeOffset & 0xFF0000) >>> 16, (sample.compositionTimeOffset & 0xFF00) >>> 8, sample.compositionTimeOffset & 0xFF // sample_composition_time_offset
41117            ]);
41118          }
41119
41120          return box(types.trun, new Uint8Array(bytes));
41121        };
41122
41123        audioTrun = function audioTrun(track, offset) {
41124          var bytes, samples, sample, i;
41125          samples = track.samples || [];
41126          offset += 8 + 12 + 8 * samples.length;
41127          bytes = trunHeader(samples, offset);
41128
41129          for (i = 0; i < samples.length; i++) {
41130            sample = samples[i];
41131            bytes = bytes.concat([(sample.duration & 0xFF000000) >>> 24, (sample.duration & 0xFF0000) >>> 16, (sample.duration & 0xFF00) >>> 8, sample.duration & 0xFF, // sample_duration
41132            (sample.size & 0xFF000000) >>> 24, (sample.size & 0xFF0000) >>> 16, (sample.size & 0xFF00) >>> 8, sample.size & 0xFF]); // sample_size
41133          }
41134
41135          return box(types.trun, new Uint8Array(bytes));
41136        };
41137
41138        trun = function trun(track, offset) {
41139          if (track.type === 'audio') {
41140            return audioTrun(track, offset);
41141          }
41142
41143          return videoTrun(track, offset);
41144        };
41145      })();
41146
41147      var mp4Generator = {
41148        ftyp: ftyp,
41149        mdat: mdat,
41150        moof: moof,
41151        moov: moov,
41152        initSegment: function initSegment(tracks) {
41153          var fileType = ftyp(),
41154              movie = moov(tracks),
41155              result;
41156          result = new Uint8Array(fileType.byteLength + movie.byteLength);
41157          result.set(fileType);
41158          result.set(movie, fileType.byteLength);
41159          return result;
41160        }
41161      };
41162      /**
41163       * mux.js
41164       *
41165       * Copyright (c) Brightcove
41166       * Licensed Apache-2.0 https://github.com/videojs/mux.js/blob/master/LICENSE
41167       */
41168      // Convert an array of nal units into an array of frames with each frame being
41169      // composed of the nal units that make up that frame
41170      // Also keep track of cummulative data about the frame from the nal units such
41171      // as the frame duration, starting pts, etc.
41172
41173      var groupNalsIntoFrames = function groupNalsIntoFrames(nalUnits) {
41174        var i,
41175            currentNal,
41176            currentFrame = [],
41177            frames = []; // TODO added for LHLS, make sure this is OK
41178
41179        frames.byteLength = 0;
41180        frames.nalCount = 0;
41181        frames.duration = 0;
41182        currentFrame.byteLength = 0;
41183
41184        for (i = 0; i < nalUnits.length; i++) {
41185          currentNal = nalUnits[i]; // Split on 'aud'-type nal units
41186
41187          if (currentNal.nalUnitType === 'access_unit_delimiter_rbsp') {
41188            // Since the very first nal unit is expected to be an AUD
41189            // only push to the frames array when currentFrame is not empty
41190            if (currentFrame.length) {
41191              currentFrame.duration = currentNal.dts - currentFrame.dts; // TODO added for LHLS, make sure this is OK
41192
41193              frames.byteLength += currentFrame.byteLength;
41194              frames.nalCount += currentFrame.length;
41195              frames.duration += currentFrame.duration;
41196              frames.push(currentFrame);
41197            }
41198
41199            currentFrame = [currentNal];
41200            currentFrame.byteLength = currentNal.data.byteLength;
41201            currentFrame.pts = currentNal.pts;
41202            currentFrame.dts = currentNal.dts;
41203          } else {
41204            // Specifically flag key frames for ease of use later
41205            if (currentNal.nalUnitType === 'slice_layer_without_partitioning_rbsp_idr') {
41206              currentFrame.keyFrame = true;
41207            }
41208
41209            currentFrame.duration = currentNal.dts - currentFrame.dts;
41210            currentFrame.byteLength += currentNal.data.byteLength;
41211            currentFrame.push(currentNal);
41212          }
41213        } // For the last frame, use the duration of the previous frame if we
41214        // have nothing better to go on
41215
41216
41217        if (frames.length && (!currentFrame.duration || currentFrame.duration <= 0)) {
41218          currentFrame.duration = frames[frames.length - 1].duration;
41219        } // Push the final frame
41220        // TODO added for LHLS, make sure this is OK
41221
41222
41223        frames.byteLength += currentFrame.byteLength;
41224        frames.nalCount += currentFrame.length;
41225        frames.duration += currentFrame.duration;
41226        frames.push(currentFrame);
41227        return frames;
41228      }; // Convert an array of frames into an array of Gop with each Gop being composed
41229      // of the frames that make up that Gop
41230      // Also keep track of cummulative data about the Gop from the frames such as the
41231      // Gop duration, starting pts, etc.
41232
41233
41234      var groupFramesIntoGops = function groupFramesIntoGops(frames) {
41235        var i,
41236            currentFrame,
41237            currentGop = [],
41238            gops = []; // We must pre-set some of the values on the Gop since we
41239        // keep running totals of these values
41240
41241        currentGop.byteLength = 0;
41242        currentGop.nalCount = 0;
41243        currentGop.duration = 0;
41244        currentGop.pts = frames[0].pts;
41245        currentGop.dts = frames[0].dts; // store some metadata about all the Gops
41246
41247        gops.byteLength = 0;
41248        gops.nalCount = 0;
41249        gops.duration = 0;
41250        gops.pts = frames[0].pts;
41251        gops.dts = frames[0].dts;
41252
41253        for (i = 0; i < frames.length; i++) {
41254          currentFrame = frames[i];
41255
41256          if (currentFrame.keyFrame) {
41257            // Since the very first frame is expected to be an keyframe
vendor: 7,922 bytes, lines 41258-41490
41258            // only push to the gops array when currentGop is not empty
41259            if (currentGop.length) {
41260              gops.push(currentGop);
41261              gops.byteLength += currentGop.byteLength;
41262              gops.nalCount += currentGop.nalCount;
41263              gops.duration += currentGop.duration;
41264            }
41265
41266            currentGop = [currentFrame];
41267            currentGop.nalCount = currentFrame.length;
41268            currentGop.byteLength = currentFrame.byteLength;
41269            currentGop.pts = currentFrame.pts;
41270            currentGop.dts = currentFrame.dts;
41271            currentGop.duration = currentFrame.duration;
41272          } else {
41273            currentGop.duration += currentFrame.duration;
41274            currentGop.nalCount += currentFrame.length;
41275            currentGop.byteLength += currentFrame.byteLength;
41276            currentGop.push(currentFrame);
41277          }
41278        }
41279
41280        if (gops.length && currentGop.duration <= 0) {
41281          currentGop.duration = gops[gops.length - 1].duration;
41282        }
41283
41284        gops.byteLength += currentGop.byteLength;
41285        gops.nalCount += currentGop.nalCount;
41286        gops.duration += currentGop.duration; // push the final Gop
41287
41288        gops.push(currentGop);
41289        return gops;
41290      };
41291      /*
41292       * Search for the first keyframe in the GOPs and throw away all frames
41293       * until that keyframe. Then extend the duration of the pulled keyframe
41294       * and pull the PTS and DTS of the keyframe so that it covers the time
41295       * range of the frames that were disposed.
41296       *
41297       * @param {Array} gops video GOPs
41298       * @returns {Array} modified video GOPs
41299       */
41300
41301
41302      var extendFirstKeyFrame = function extendFirstKeyFrame(gops) {
41303        var currentGop;
41304
41305        if (!gops[0][0].keyFrame && gops.length > 1) {
41306          // Remove the first GOP
41307          currentGop = gops.shift();
41308          gops.byteLength -= currentGop.byteLength;
41309          gops.nalCount -= currentGop.nalCount; // Extend the first frame of what is now the
41310          // first gop to cover the time period of the
41311          // frames we just removed
41312
41313          gops[0][0].dts = currentGop.dts;
41314          gops[0][0].pts = currentGop.pts;
41315          gops[0][0].duration += currentGop.duration;
41316        }
41317
41318        return gops;
41319      };
41320      /**
41321       * Default sample object
41322       * see ISO/IEC 14496-12:2012, section 8.6.4.3
41323       */
41324
41325
41326      var createDefaultSample = function createDefaultSample() {
41327        return {
41328          size: 0,
41329          flags: {
41330            isLeading: 0,
41331            dependsOn: 1,
41332            isDependedOn: 0,
41333            hasRedundancy: 0,
41334            degradationPriority: 0,
41335            isNonSyncSample: 1
41336          }
41337        };
41338      };
41339      /*
41340       * Collates information from a video frame into an object for eventual
41341       * entry into an MP4 sample table.
41342       *
41343       * @param {Object} frame the video frame
41344       * @param {Number} dataOffset the byte offset to position the sample
41345       * @return {Object} object containing sample table info for a frame
41346       */
41347
41348
41349      var sampleForFrame = function sampleForFrame(frame, dataOffset) {
41350        var sample = createDefaultSample();
41351        sample.dataOffset = dataOffset;
41352        sample.compositionTimeOffset = frame.pts - frame.dts;
41353        sample.duration = frame.duration;
41354        sample.size = 4 * frame.length; // Space for nal unit size
41355
41356        sample.size += frame.byteLength;
41357
41358        if (frame.keyFrame) {
41359          sample.flags.dependsOn = 2;
41360          sample.flags.isNonSyncSample = 0;
41361        }
41362
41363        return sample;
41364      }; // generate the track's sample table from an array of gops
41365
41366
41367      var generateSampleTable = function generateSampleTable(gops, baseDataOffset) {
41368        var h,
41369            i,
41370            sample,
41371            currentGop,
41372            currentFrame,
41373            dataOffset = baseDataOffset || 0,
41374            samples = [];
41375
41376        for (h = 0; h < gops.length; h++) {
41377          currentGop = gops[h];
41378
41379          for (i = 0; i < currentGop.length; i++) {
41380            currentFrame = currentGop[i];
41381            sample = sampleForFrame(currentFrame, dataOffset);
41382            dataOffset += sample.size;
41383            samples.push(sample);
41384          }
41385        }
41386
41387        return samples;
41388      }; // generate the track's raw mdat data from an array of gops
41389
41390
41391      var concatenateNalData = function concatenateNalData(gops) {
41392        var h,
41393            i,
41394            j,
41395            currentGop,
41396            currentFrame,
41397            currentNal,
41398            dataOffset = 0,
41399            nalsByteLength = gops.byteLength,
41400            numberOfNals = gops.nalCount,
41401            totalByteLength = nalsByteLength + 4 * numberOfNals,
41402            data = new Uint8Array(totalByteLength),
41403            view = new DataView(data.buffer); // For each Gop..
41404
41405        for (h = 0; h < gops.length; h++) {
41406          currentGop = gops[h]; // For each Frame..
41407
41408          for (i = 0; i < currentGop.length; i++) {
41409            currentFrame = currentGop[i]; // For each NAL..
41410
41411            for (j = 0; j < currentFrame.length; j++) {
41412              currentNal = currentFrame[j];
41413              view.setUint32(dataOffset, currentNal.data.byteLength);
41414              dataOffset += 4;
41415              data.set(currentNal.data, dataOffset);
41416              dataOffset += currentNal.data.byteLength;
41417            }
41418          }
41419        }
41420
41421        return data;
41422      }; // generate the track's sample table from a frame
41423
41424
41425      var generateSampleTableForFrame = function generateSampleTableForFrame(frame, baseDataOffset) {
41426        var sample,
41427            dataOffset = baseDataOffset || 0,
41428            samples = [];
41429        sample = sampleForFrame(frame, dataOffset);
41430        samples.push(sample);
41431        return samples;
41432      }; // generate the track's raw mdat data from a frame
41433
41434
41435      var concatenateNalDataForFrame = function concatenateNalDataForFrame(frame) {
41436        var i,
41437            currentNal,
41438            dataOffset = 0,
41439            nalsByteLength = frame.byteLength,
41440            numberOfNals = frame.length,
41441            totalByteLength = nalsByteLength + 4 * numberOfNals,
41442            data = new Uint8Array(totalByteLength),
41443            view = new DataView(data.buffer); // For each NAL..
41444
41445        for (i = 0; i < frame.length; i++) {
41446          currentNal = frame[i];
41447          view.setUint32(dataOffset, currentNal.data.byteLength);
41448          dataOffset += 4;
41449          data.set(currentNal.data, dataOffset);
41450          dataOffset += currentNal.data.byteLength;
41451        }
41452
41453        return data;
41454      };
41455
41456      var frameUtils = {
41457        groupNalsIntoFrames: groupNalsIntoFrames,
41458        groupFramesIntoGops: groupFramesIntoGops,
41459        extendFirstKeyFrame: extendFirstKeyFrame,
41460        generateSampleTable: generateSampleTable,
41461        concatenateNalData: concatenateNalData,
41462        generateSampleTableForFrame: generateSampleTableForFrame,
41463        concatenateNalDataForFrame: concatenateNalDataForFrame
41464      };
41465      /**
41466       * mux.js
41467       *
41468       * Copyright (c) Brightcove
41469       * Licensed Apache-2.0 https://github.com/videojs/mux.js/blob/master/LICENSE
41470       */
41471
41472      var highPrefix = [33, 16, 5, 32, 164, 27];
41473      var lowPrefix = [33, 65, 108, 84, 1, 2, 4, 8, 168, 2, 4, 8, 17, 191, 252];
41474
41475      var zeroFill = function zeroFill(count) {
41476        var a = [];
41477
41478        while (count--) {
41479          a.push(0);
41480        }
41481
41482        return a;
41483      };
41484
41485      var makeTable = function makeTable(metaTable) {
41486        return Object.keys(metaTable).reduce(function (obj, key) {
41487          obj[key] = new Uint8Array(metaTable[key].reduce(function (arr, part) {
41488            return arr.concat(part);
41489          }, []));
41490          return obj;
vendor: 8,797 bytes, lines 41491-41718
41491        }, {});
41492      }; // Frames-of-silence to use for filling in missing AAC frames
41493
41494
41495      var coneOfSilence = {
41496        96000: [highPrefix, [227, 64], zeroFill(154), [56]],
41497        88200: [highPrefix, [231], zeroFill(170), [56]],
41498        64000: [highPrefix, [248, 192], zeroFill(240), [56]],
41499        48000: [highPrefix, [255, 192], zeroFill(268), [55, 148, 128], zeroFill(54), [112]],
41500        44100: [highPrefix, [255, 192], zeroFill(268), [55, 163, 128], zeroFill(84), [112]],
41501        32000: [highPrefix, [255, 192], zeroFill(268), [55, 234], zeroFill(226), [112]],
41502        24000: [highPrefix, [255, 192], zeroFill(268), [55, 255, 128], zeroFill(268), [111, 112], zeroFill(126), [224]],
41503        16000: [highPrefix, [255, 192], zeroFill(268), [55, 255, 128], zeroFill(268), [111, 255], zeroFill(269), [223, 108], zeroFill(195), [1, 192]],
41504        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]],
41505        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]],
41506        8000: [lowPrefix, zeroFill(268), [3, 121, 16], zeroFill(47), [7]]
41507      };
41508      var silence = makeTable(coneOfSilence);
41509      /**
41510       * mux.js
41511       *
41512       * Copyright (c) Brightcove
41513       * Licensed Apache-2.0 https://github.com/videojs/mux.js/blob/master/LICENSE
41514       */
41515
41516      var ONE_SECOND_IN_TS = 90000,
41517          // 90kHz clock
41518      secondsToVideoTs,
41519          secondsToAudioTs,
41520          videoTsToSeconds,
41521          audioTsToSeconds,
41522          audioTsToVideoTs,
41523          videoTsToAudioTs,
41524          metadataTsToSeconds;
41525
41526      secondsToVideoTs = function secondsToVideoTs(seconds) {
41527        return seconds * ONE_SECOND_IN_TS;
41528      };
41529
41530      secondsToAudioTs = function secondsToAudioTs(seconds, sampleRate) {
41531        return seconds * sampleRate;
41532      };
41533
41534      videoTsToSeconds = function videoTsToSeconds(timestamp) {
41535        return timestamp / ONE_SECOND_IN_TS;
41536      };
41537
41538      audioTsToSeconds = function audioTsToSeconds(timestamp, sampleRate) {
41539        return timestamp / sampleRate;
41540      };
41541
41542      audioTsToVideoTs = function audioTsToVideoTs(timestamp, sampleRate) {
41543        return secondsToVideoTs(audioTsToSeconds(timestamp, sampleRate));
41544      };
41545
41546      videoTsToAudioTs = function videoTsToAudioTs(timestamp, sampleRate) {
41547        return secondsToAudioTs(videoTsToSeconds(timestamp), sampleRate);
41548      };
41549      /**
41550       * Adjust ID3 tag or caption timing information by the timeline pts values
41551       * (if keepOriginalTimestamps is false) and convert to seconds
41552       */
41553
41554
41555      metadataTsToSeconds = function metadataTsToSeconds(timestamp, timelineStartPts, keepOriginalTimestamps) {
41556        return videoTsToSeconds(keepOriginalTimestamps ? timestamp : timestamp - timelineStartPts);
41557      };
41558
41559      var clock = {
41560        ONE_SECOND_IN_TS: ONE_SECOND_IN_TS,
41561        secondsToVideoTs: secondsToVideoTs,
41562        secondsToAudioTs: secondsToAudioTs,
41563        videoTsToSeconds: videoTsToSeconds,
41564        audioTsToSeconds: audioTsToSeconds,
41565        audioTsToVideoTs: audioTsToVideoTs,
41566        videoTsToAudioTs: videoTsToAudioTs,
41567        metadataTsToSeconds: metadataTsToSeconds
41568      };
41569      /**
41570       * mux.js
41571       *
41572       * Copyright (c) Brightcove
41573       * Licensed Apache-2.0 https://github.com/videojs/mux.js/blob/master/LICENSE
41574       */
41575
41576      /**
41577       * Sum the `byteLength` properties of the data in each AAC frame
41578       */
41579
41580      var sumFrameByteLengths = function sumFrameByteLengths(array) {
41581        var i,
41582            currentObj,
41583            sum = 0; // sum the byteLength's all each nal unit in the frame
41584
41585        for (i = 0; i < array.length; i++) {
41586          currentObj = array[i];
41587          sum += currentObj.data.byteLength;
41588        }
41589
41590        return sum;
41591      }; // Possibly pad (prefix) the audio track with silence if appending this track
41592      // would lead to the introduction of a gap in the audio buffer
41593
41594
41595      var prefixWithSilence = function prefixWithSilence(track, frames, audioAppendStartTs, videoBaseMediaDecodeTime) {
41596        var baseMediaDecodeTimeTs,
41597            frameDuration = 0,
41598            audioGapDuration = 0,
41599            audioFillFrameCount = 0,
41600            audioFillDuration = 0,
41601            silentFrame,
41602            i,
41603            firstFrame;
41604
41605        if (!frames.length) {
41606          return;
41607        }
41608
41609        baseMediaDecodeTimeTs = clock.audioTsToVideoTs(track.baseMediaDecodeTime, track.samplerate); // determine frame clock duration based on sample rate, round up to avoid overfills
41610
41611        frameDuration = Math.ceil(clock.ONE_SECOND_IN_TS / (track.samplerate / 1024));
41612
41613        if (audioAppendStartTs && videoBaseMediaDecodeTime) {
41614          // insert the shortest possible amount (audio gap or audio to video gap)
41615          audioGapDuration = baseMediaDecodeTimeTs - Math.max(audioAppendStartTs, videoBaseMediaDecodeTime); // number of full frames in the audio gap
41616
41617          audioFillFrameCount = Math.floor(audioGapDuration / frameDuration);
41618          audioFillDuration = audioFillFrameCount * frameDuration;
41619        } // don't attempt to fill gaps smaller than a single frame or larger
41620        // than a half second
41621
41622
41623        if (audioFillFrameCount < 1 || audioFillDuration > clock.ONE_SECOND_IN_TS / 2) {
41624          return;
41625        }
41626
41627        silentFrame = silence[track.samplerate];
41628
41629        if (!silentFrame) {
41630          // we don't have a silent frame pregenerated for the sample rate, so use a frame
41631          // from the content instead
41632          silentFrame = frames[0].data;
41633        }
41634
41635        for (i = 0; i < audioFillFrameCount; i++) {
41636          firstFrame = frames[0];
41637          frames.splice(0, 0, {
41638            data: silentFrame,
41639            dts: firstFrame.dts - frameDuration,
41640            pts: firstFrame.pts - frameDuration
41641          });
41642        }
41643
41644        track.baseMediaDecodeTime -= Math.floor(clock.videoTsToAudioTs(audioFillDuration, track.samplerate));
41645      }; // If the audio segment extends before the earliest allowed dts
41646      // value, remove AAC frames until starts at or after the earliest
41647      // allowed DTS so that we don't end up with a negative baseMedia-
41648      // DecodeTime for the audio track
41649
41650
41651      var trimAdtsFramesByEarliestDts = function trimAdtsFramesByEarliestDts(adtsFrames, track, earliestAllowedDts) {
41652        if (track.minSegmentDts >= earliestAllowedDts) {
41653          return adtsFrames;
41654        } // We will need to recalculate the earliest segment Dts
41655
41656
41657        track.minSegmentDts = Infinity;
41658        return adtsFrames.filter(function (currentFrame) {
41659          // If this is an allowed frame, keep it and record it's Dts
41660          if (currentFrame.dts >= earliestAllowedDts) {
41661            track.minSegmentDts = Math.min(track.minSegmentDts, currentFrame.dts);
41662            track.minSegmentPts = track.minSegmentDts;
41663            return true;
41664          } // Otherwise, discard it
41665
41666
41667          return false;
41668        });
41669      }; // generate the track's raw mdat data from an array of frames
41670
41671
41672      var generateSampleTable$1 = function generateSampleTable(frames) {
41673        var i,
41674            currentFrame,
41675            samples = [];
41676
41677        for (i = 0; i < frames.length; i++) {
41678          currentFrame = frames[i];
41679          samples.push({
41680            size: currentFrame.data.byteLength,
41681            duration: 1024 // For AAC audio, all samples contain 1024 samples
41682
41683          });
41684        }
41685
41686        return samples;
41687      }; // generate the track's sample table from an array of frames
41688
41689
41690      var concatenateFrameData = function concatenateFrameData(frames) {
41691        var i,
41692            currentFrame,
41693            dataOffset = 0,
41694            data = new Uint8Array(sumFrameByteLengths(frames));
41695
41696        for (i = 0; i < frames.length; i++) {
41697          currentFrame = frames[i];
41698          data.set(currentFrame.data, dataOffset);
41699          dataOffset += currentFrame.data.byteLength;
41700        }
41701
41702        return data;
41703      };
41704
41705      var audioFrameUtils = {
41706        prefixWithSilence: prefixWithSilence,
41707        trimAdtsFramesByEarliestDts: trimAdtsFramesByEarliestDts,
41708        generateSampleTable: generateSampleTable$1,
41709        concatenateFrameData: concatenateFrameData
41710      };
41711      /**
41712       * mux.js
41713       *
41714       * Copyright (c) Brightcove
41715       * Licensed Apache-2.0 https://github.com/videojs/mux.js/blob/master/LICENSE
41716       */
41717
41718      var ONE_SECOND_IN_TS$1 = clock.ONE_SECOND_IN_TS;
vendor: 12,931 bytes, lines 41719-42077
41719      /**
41720       * Store information about the start and end of the track and the
41721       * duration for each frame/sample we process in order to calculate
41722       * the baseMediaDecodeTime
41723       */
41724
41725      var collectDtsInfo = function collectDtsInfo(track, data) {
41726        if (typeof data.pts === 'number') {
41727          if (track.timelineStartInfo.pts === undefined) {
41728            track.timelineStartInfo.pts = data.pts;
41729          }
41730
41731          if (track.minSegmentPts === undefined) {
41732            track.minSegmentPts = data.pts;
41733          } else {
41734            track.minSegmentPts = Math.min(track.minSegmentPts, data.pts);
41735          }
41736
41737          if (track.maxSegmentPts === undefined) {
41738            track.maxSegmentPts = data.pts;
41739          } else {
41740            track.maxSegmentPts = Math.max(track.maxSegmentPts, data.pts);
41741          }
41742        }
41743
41744        if (typeof data.dts === 'number') {
41745          if (track.timelineStartInfo.dts === undefined) {
41746            track.timelineStartInfo.dts = data.dts;
41747          }
41748
41749          if (track.minSegmentDts === undefined) {
41750            track.minSegmentDts = data.dts;
41751          } else {
41752            track.minSegmentDts = Math.min(track.minSegmentDts, data.dts);
41753          }
41754
41755          if (track.maxSegmentDts === undefined) {
41756            track.maxSegmentDts = data.dts;
41757          } else {
41758            track.maxSegmentDts = Math.max(track.maxSegmentDts, data.dts);
41759          }
41760        }
41761      };
41762      /**
41763       * Clear values used to calculate the baseMediaDecodeTime between
41764       * tracks
41765       */
41766
41767
41768      var clearDtsInfo = function clearDtsInfo(track) {
41769        delete track.minSegmentDts;
41770        delete track.maxSegmentDts;
41771        delete track.minSegmentPts;
41772        delete track.maxSegmentPts;
41773      };
41774      /**
41775       * Calculate the track's baseMediaDecodeTime based on the earliest
41776       * DTS the transmuxer has ever seen and the minimum DTS for the
41777       * current track
41778       * @param track {object} track metadata configuration
41779       * @param keepOriginalTimestamps {boolean} If true, keep the timestamps
41780       *        in the source; false to adjust the first segment to start at 0.
41781       */
41782
41783
41784      var calculateTrackBaseMediaDecodeTime = function calculateTrackBaseMediaDecodeTime(track, keepOriginalTimestamps) {
41785        var baseMediaDecodeTime,
41786            scale,
41787            minSegmentDts = track.minSegmentDts; // Optionally adjust the time so the first segment starts at zero.
41788
41789        if (!keepOriginalTimestamps) {
41790          minSegmentDts -= track.timelineStartInfo.dts;
41791        } // track.timelineStartInfo.baseMediaDecodeTime is the location, in time, where
41792        // we want the start of the first segment to be placed
41793
41794
41795        baseMediaDecodeTime = track.timelineStartInfo.baseMediaDecodeTime; // Add to that the distance this segment is from the very first
41796
41797        baseMediaDecodeTime += minSegmentDts; // baseMediaDecodeTime must not become negative
41798
41799        baseMediaDecodeTime = Math.max(0, baseMediaDecodeTime);
41800
41801        if (track.type === 'audio') {
41802          // Audio has a different clock equal to the sampling_rate so we need to
41803          // scale the PTS values into the clock rate of the track
41804          scale = track.samplerate / ONE_SECOND_IN_TS$1;
41805          baseMediaDecodeTime *= scale;
41806          baseMediaDecodeTime = Math.floor(baseMediaDecodeTime);
41807        }
41808
41809        return baseMediaDecodeTime;
41810      };
41811
41812      var trackDecodeInfo = {
41813        clearDtsInfo: clearDtsInfo,
41814        calculateTrackBaseMediaDecodeTime: calculateTrackBaseMediaDecodeTime,
41815        collectDtsInfo: collectDtsInfo
41816      };
41817      /**
41818       * mux.js
41819       *
41820       * Copyright (c) Brightcove
41821       * Licensed Apache-2.0 https://github.com/videojs/mux.js/blob/master/LICENSE
41822       *
41823       * Reads in-band caption information from a video elementary
41824       * stream. Captions must follow the CEA-708 standard for injection
41825       * into an MPEG-2 transport streams.
41826       * @see https://en.wikipedia.org/wiki/CEA-708
41827       * @see https://www.gpo.gov/fdsys/pkg/CFR-2007-title47-vol1/pdf/CFR-2007-title47-vol1-sec15-119.pdf
41828       */
41829      // Supplemental enhancement information (SEI) NAL units have a
41830      // payload type field to indicate how they are to be
41831      // interpreted. CEAS-708 caption content is always transmitted with
41832      // payload type 0x04.
41833
41834      var USER_DATA_REGISTERED_ITU_T_T35 = 4,
41835          RBSP_TRAILING_BITS = 128;
41836      /**
41837        * Parse a supplemental enhancement information (SEI) NAL unit.
41838        * Stops parsing once a message of type ITU T T35 has been found.
41839        *
41840        * @param bytes {Uint8Array} the bytes of a SEI NAL unit
41841        * @return {object} the parsed SEI payload
41842        * @see Rec. ITU-T H.264, 7.3.2.3.1
41843        */
41844
41845      var parseSei = function parseSei(bytes) {
41846        var i = 0,
41847            result = {
41848          payloadType: -1,
41849          payloadSize: 0
41850        },
41851            payloadType = 0,
41852            payloadSize = 0; // go through the sei_rbsp parsing each each individual sei_message
41853
41854        while (i < bytes.byteLength) {
41855          // stop once we have hit the end of the sei_rbsp
41856          if (bytes[i] === RBSP_TRAILING_BITS) {
41857            break;
41858          } // Parse payload type
41859
41860
41861          while (bytes[i] === 0xFF) {
41862            payloadType += 255;
41863            i++;
41864          }
41865
41866          payloadType += bytes[i++]; // Parse payload size
41867
41868          while (bytes[i] === 0xFF) {
41869            payloadSize += 255;
41870            i++;
41871          }
41872
41873          payloadSize += bytes[i++]; // this sei_message is a 608/708 caption so save it and break
41874          // there can only ever be one caption message in a frame's sei
41875
41876          if (!result.payload && payloadType === USER_DATA_REGISTERED_ITU_T_T35) {
41877            result.payloadType = payloadType;
41878            result.payloadSize = payloadSize;
41879            result.payload = bytes.subarray(i, i + payloadSize);
41880            break;
41881          } // skip the payload and parse the next message
41882
41883
41884          i += payloadSize;
41885          payloadType = 0;
41886          payloadSize = 0;
41887        }
41888
41889        return result;
41890      }; // see ANSI/SCTE 128-1 (2013), section 8.1
41891
41892
41893      var parseUserData = function parseUserData(sei) {
41894        // itu_t_t35_contry_code must be 181 (United States) for
41895        // captions
41896        if (sei.payload[0] !== 181) {
41897          return null;
41898        } // itu_t_t35_provider_code should be 49 (ATSC) for captions
41899
41900
41901        if ((sei.payload[1] << 8 | sei.payload[2]) !== 49) {
41902          return null;
41903        } // the user_identifier should be "GA94" to indicate ATSC1 data
41904
41905
41906        if (String.fromCharCode(sei.payload[3], sei.payload[4], sei.payload[5], sei.payload[6]) !== 'GA94') {
41907          return null;
41908        } // finally, user_data_type_code should be 0x03 for caption data
41909
41910
41911        if (sei.payload[7] !== 0x03) {
41912          return null;
41913        } // return the user_data_type_structure and strip the trailing
41914        // marker bits
41915
41916
41917        return sei.payload.subarray(8, sei.payload.length - 1);
41918      }; // see CEA-708-D, section 4.4
41919
41920
41921      var parseCaptionPackets = function parseCaptionPackets(pts, userData) {
41922        var results = [],
41923            i,
41924            count,
41925            offset,
41926            data; // if this is just filler, return immediately
41927
41928        if (!(userData[0] & 0x40)) {
41929          return results;
41930        } // parse out the cc_data_1 and cc_data_2 fields
41931
41932
41933        count = userData[0] & 0x1f;
41934
41935        for (i = 0; i < count; i++) {
41936          offset = i * 3;
41937          data = {
41938            type: userData[offset + 2] & 0x03,
41939            pts: pts
41940          }; // capture cc data when cc_valid is 1
41941
41942          if (userData[offset + 2] & 0x04) {
41943            data.ccData = userData[offset + 3] << 8 | userData[offset + 4];
41944            results.push(data);
41945          }
41946        }
41947
41948        return results;
41949      };
41950
41951      var discardEmulationPreventionBytes = function discardEmulationPreventionBytes(data) {
41952        var length = data.byteLength,
41953            emulationPreventionBytesPositions = [],
41954            i = 1,
41955            newLength,
41956            newData; // Find all `Emulation Prevention Bytes`
41957
41958        while (i < length - 2) {
41959          if (data[i] === 0 && data[i + 1] === 0 && data[i + 2] === 0x03) {
41960            emulationPreventionBytesPositions.push(i + 2);
41961            i += 2;
41962          } else {
41963            i++;
41964          }
41965        } // If no Emulation Prevention Bytes were found just return the original
41966        // array
41967
41968
41969        if (emulationPreventionBytesPositions.length === 0) {
41970          return data;
41971        } // Create a new array to hold the NAL unit data
41972
41973
41974        newLength = length - emulationPreventionBytesPositions.length;
41975        newData = new Uint8Array(newLength);
41976        var sourceIndex = 0;
41977
41978        for (i = 0; i < newLength; sourceIndex++, i++) {
41979          if (sourceIndex === emulationPreventionBytesPositions[0]) {
41980            // Skip this byte
41981            sourceIndex++; // Remove this position index
41982
41983            emulationPreventionBytesPositions.shift();
41984          }
41985
41986          newData[i] = data[sourceIndex];
41987        }
41988
41989        return newData;
41990      }; // exports
41991
41992
41993      var captionPacketParser = {
41994        parseSei: parseSei,
41995        parseUserData: parseUserData,
41996        parseCaptionPackets: parseCaptionPackets,
41997        discardEmulationPreventionBytes: discardEmulationPreventionBytes,
41998        USER_DATA_REGISTERED_ITU_T_T35: USER_DATA_REGISTERED_ITU_T_T35
41999      }; // -----------------
42000      // Link To Transport
42001      // -----------------
42002
42003      var CaptionStream = function CaptionStream() {
42004        CaptionStream.prototype.init.call(this);
42005        this.captionPackets_ = [];
42006        this.ccStreams_ = [new Cea608Stream(0, 0), // eslint-disable-line no-use-before-define
42007        new Cea608Stream(0, 1), // eslint-disable-line no-use-before-define
42008        new Cea608Stream(1, 0), // eslint-disable-line no-use-before-define
42009        new Cea608Stream(1, 1) // eslint-disable-line no-use-before-define
42010        ];
42011        this.reset(); // forward data and done events from CCs to this CaptionStream
42012
42013        this.ccStreams_.forEach(function (cc) {
42014          cc.on('data', this.trigger.bind(this, 'data'));
42015          cc.on('partialdone', this.trigger.bind(this, 'partialdone'));
42016          cc.on('done', this.trigger.bind(this, 'done'));
42017        }, this);
42018      };
42019
42020      CaptionStream.prototype = new stream();
42021
42022      CaptionStream.prototype.push = function (event) {
42023        var sei, userData, newCaptionPackets; // only examine SEI NALs
42024
42025        if (event.nalUnitType !== 'sei_rbsp') {
42026          return;
42027        } // parse the sei
42028
42029
42030        sei = captionPacketParser.parseSei(event.escapedRBSP); // ignore everything but user_data_registered_itu_t_t35
42031
42032        if (sei.payloadType !== captionPacketParser.USER_DATA_REGISTERED_ITU_T_T35) {
42033          return;
42034        } // parse out the user data payload
42035
42036
42037        userData = captionPacketParser.parseUserData(sei); // ignore unrecognized userData
42038
42039        if (!userData) {
42040          return;
42041        } // Sometimes, the same segment # will be downloaded twice. To stop the
42042        // caption data from being processed twice, we track the latest dts we've
42043        // received and ignore everything with a dts before that. However, since
42044        // data for a specific dts can be split across packets on either side of
42045        // a segment boundary, we need to make sure we *don't* ignore the packets
42046        // from the *next* segment that have dts === this.latestDts_. By constantly
42047        // tracking the number of packets received with dts === this.latestDts_, we
42048        // know how many should be ignored once we start receiving duplicates.
42049
42050
42051        if (event.dts < this.latestDts_) {
42052          // We've started getting older data, so set the flag.
42053          this.ignoreNextEqualDts_ = true;
42054          return;
42055        } else if (event.dts === this.latestDts_ && this.ignoreNextEqualDts_) {
42056          this.numSameDts_--;
42057
42058          if (!this.numSameDts_) {
42059            // We've received the last duplicate packet, time to start processing again
42060            this.ignoreNextEqualDts_ = false;
42061          }
42062
42063          return;
42064        } // parse out CC data packets and save them for later
42065
42066
42067        newCaptionPackets = captionPacketParser.parseCaptionPackets(event.pts, userData);
42068        this.captionPackets_ = this.captionPackets_.concat(newCaptionPackets);
42069
42070        if (this.latestDts_ !== event.dts) {
42071          this.numSameDts_ = 0;
42072        }
42073
42074        this.numSameDts_++;
42075        this.latestDts_ = event.dts;
42076      };
42077
vendor: 5,244 bytes, lines 42078-42211
42078      CaptionStream.prototype.flushCCStreams = function (flushType) {
42079        this.ccStreams_.forEach(function (cc) {
42080          return flushType === 'flush' ? cc.flush() : cc.partialFlush();
42081        }, this);
42082      };
42083
42084      CaptionStream.prototype.flushStream = function (flushType) {
42085        // make sure we actually parsed captions before proceeding
42086        if (!this.captionPackets_.length) {
42087          this.flushCCStreams(flushType);
42088          return;
42089        } // In Chrome, the Array#sort function is not stable so add a
42090        // presortIndex that we can use to ensure we get a stable-sort
42091
42092
42093        this.captionPackets_.forEach(function (elem, idx) {
42094          elem.presortIndex = idx;
42095        }); // sort caption byte-pairs based on their PTS values
42096
42097        this.captionPackets_.sort(function (a, b) {
42098          if (a.pts === b.pts) {
42099            return a.presortIndex - b.presortIndex;
42100          }
42101
42102          return a.pts - b.pts;
42103        });
42104        this.captionPackets_.forEach(function (packet) {
42105          if (packet.type < 2) {
42106            // Dispatch packet to the right Cea608Stream
42107            this.dispatchCea608Packet(packet);
42108          } // this is where an 'else' would go for a dispatching packets
42109          // to a theoretical Cea708Stream that handles SERVICEn data
42110
42111        }, this);
42112        this.captionPackets_.length = 0;
42113        this.flushCCStreams(flushType);
42114      };
42115
42116      CaptionStream.prototype.flush = function () {
42117        return this.flushStream('flush');
42118      }; // Only called if handling partial data
42119
42120
42121      CaptionStream.prototype.partialFlush = function () {
42122        return this.flushStream('partialFlush');
42123      };
42124
42125      CaptionStream.prototype.reset = function () {
42126        this.latestDts_ = null;
42127        this.ignoreNextEqualDts_ = false;
42128        this.numSameDts_ = 0;
42129        this.activeCea608Channel_ = [null, null];
42130        this.ccStreams_.forEach(function (ccStream) {
42131          ccStream.reset();
42132        });
42133      }; // From the CEA-608 spec:
42134
42135      /*
42136       * When XDS sub-packets are interleaved with other services, the end of each sub-packet shall be followed
42137       * by a control pair to change to a different service. When any of the control codes from 0x10 to 0x1F is
42138       * used to begin a control code pair, it indicates the return to captioning or Text data. The control code pair
42139       * and subsequent data should then be processed according to the FCC rules. It may be necessary for the
42140       * line 21 data encoder to automatically insert a control code pair (i.e. RCL, RU2, RU3, RU4, RDC, or RTD)
42141       * to switch to captioning or Text.
42142      */
42143      // With that in mind, we ignore any data between an XDS control code and a
42144      // subsequent closed-captioning control code.
42145
42146
42147      CaptionStream.prototype.dispatchCea608Packet = function (packet) {
42148        // NOTE: packet.type is the CEA608 field
42149        if (this.setsTextOrXDSActive(packet)) {
42150          this.activeCea608Channel_[packet.type] = null;
42151        } else if (this.setsChannel1Active(packet)) {
42152          this.activeCea608Channel_[packet.type] = 0;
42153        } else if (this.setsChannel2Active(packet)) {
42154          this.activeCea608Channel_[packet.type] = 1;
42155        }
42156
42157        if (this.activeCea608Channel_[packet.type] === null) {
42158          // If we haven't received anything to set the active channel, or the
42159          // packets are Text/XDS data, discard the data; we don't want jumbled
42160          // captions
42161          return;
42162        }
42163
42164        this.ccStreams_[(packet.type << 1) + this.activeCea608Channel_[packet.type]].push(packet);
42165      };
42166
42167      CaptionStream.prototype.setsChannel1Active = function (packet) {
42168        return (packet.ccData & 0x7800) === 0x1000;
42169      };
42170
42171      CaptionStream.prototype.setsChannel2Active = function (packet) {
42172        return (packet.ccData & 0x7800) === 0x1800;
42173      };
42174
42175      CaptionStream.prototype.setsTextOrXDSActive = function (packet) {
42176        return (packet.ccData & 0x7100) === 0x0100 || (packet.ccData & 0x78fe) === 0x102a || (packet.ccData & 0x78fe) === 0x182a;
42177      }; // ----------------------
42178      // Session to Application
42179      // ----------------------
42180      // This hash maps non-ASCII, special, and extended character codes to their
42181      // proper Unicode equivalent. The first keys that are only a single byte
42182      // are the non-standard ASCII characters, which simply map the CEA608 byte
42183      // to the standard ASCII/Unicode. The two-byte keys that follow are the CEA608
42184      // character codes, but have their MSB bitmasked with 0x03 so that a lookup
42185      // can be performed regardless of the field and data channel on which the
42186      // character code was received.
42187
42188
42189      var CHARACTER_TRANSLATION = {
42190        0x2a: 0xe1,
42191        // á
42192        0x5c: 0xe9,
42193        // é
42194        0x5e: 0xed,
42195        // í
42196        0x5f: 0xf3,
42197        // ó
42198        0x60: 0xfa,
42199        // ú
42200        0x7b: 0xe7,
42201        // ç
42202        0x7c: 0xf7,
42203        // ÷
42204        0x7d: 0xd1,
42205        // Ñ
42206        0x7e: 0xf1,
42207        // ñ
42208        0x7f: 0x2588,
42209        // █
42210        0x0130: 0xae,
42211        // ®
42212        0x0131: 0xb0,
42213        // °
42214        0x0132: 0xbd,
42215        // ½
42216        0x0133: 0xbf,
42217        // ¿
42218        0x0134: 0x2122,
42219        // ™
42220        0x0135: 0xa2,
42221        // ¢
42222        0x0136: 0xa3,
42223        // £
42224        0x0137: 0x266a,
42225        // ♪
42226        0x0138: 0xe0,
42227        // à
42228        0x0139: 0xa0,
42229        //
42230        0x013a: 0xe8,
42231        // è
42232        0x013b: 0xe2,
42233        // â
42234        0x013c: 0xea,
42235        // ê
42236        0x013d: 0xee,
42237        // î
42238        0x013e: 0xf4,
42239        // ô
42240        0x013f: 0xfb,
42241        // û
42242        0x0220: 0xc1,
42243        // Á
42244        0x0221: 0xc9,
42245        // É
42246        0x0222: 0xd3,
42247        // Ó
42248        0x0223: 0xda,
42249        // Ú
42250        0x0224: 0xdc,
42251        // Ü
42252        0x0225: 0xfc,
42253        // ü
42254        0x0226: 0x2018,
42255        // ‘
42256        0x0227: 0xa1,
42257        // ¡
42258        0x0228: 0x2a,
42259        // *
42260        0x0229: 0x27,
42261        // '
42262        0x022a: 0x2014,
42263        // —
42264        0x022b: 0xa9,
42265        // ©
42266        0x022c: 0x2120,
42267        // ℠
42268        0x022d: 0x2022,
42269        // •
42270        0x022e: 0x201c,
42271        // “
42272        0x022f: 0x201d,
42273        // ”
42274        0x0230: 0xc0,
42275        // À
42276        0x0231: 0xc2,
42277        // Â
42278        0x0232: 0xc7,
42279        // Ç
42280        0x0233: 0xc8,
42281        // È
42282        0x0234: 0xca,
42283        // Ê
42284        0x0235: 0xcb,
42285        // Ë
42286        0x0236: 0xeb,
42287        // ë
42288        0x0237: 0xce,
42289        // Î
42290        0x0238: 0xcf,
42291        // Ï
42292        0x0239: 0xef,
42293        // ï
42294        0x023a: 0xd4,
42295        // Ô
42296        0x023b: 0xd9,
42297        // Ù
42298        0x023c: 0xf9,
42299        // ù
42300        0x023d: 0xdb,
42301        // Û
42302        0x023e: 0xab,
42303        // «
42304        0x023f: 0xbb,
42305        // »
42306        0x0320: 0xc3,
42307        // Ã
42308        0x0321: 0xe3,
42309        // ã
42310        0x0322: 0xcd,
42311        // Í
42312        0x0323: 0xcc,
42313        // Ì
42314        0x0324: 0xec,
42315        // ì
42316        0x0325: 0xd2,
42317        // Ò
42318        0x0326: 0xf2,
42319        // ò
42320        0x0327: 0xd5,
42321        // Õ
42322        0x0328: 0xf5,
42323        // õ
42324        0x0329: 0x7b,
42325        // {
42326        0x032a: 0x7d,
42327        // }
42328        0x032b: 0x5c,
42329        // \
42330        0x032c: 0x5e,
42331        // ^
42332        0x032d: 0x5f,
42333        // _
42334        0x032e: 0x7c,
42335        // |
42336        0x032f: 0x7e,
42337        // ~
42338        0x0330: 0xc4,
42339        // Ä
42340        0x0331: 0xe4,
vendor: 20,174 bytes, lines 42341-42840
42341        // ä
42342        0x0332: 0xd6,
42343        // Ö
42344        0x0333: 0xf6,
42345        // ö
42346        0x0334: 0xdf,
42347        // ß
42348        0x0335: 0xa5,
42349        // ¥
42350        0x0336: 0xa4,
42351        // ¤
42352        0x0337: 0x2502,
42353        // │
42354        0x0338: 0xc5,
42355        // Å
42356        0x0339: 0xe5,
42357        // å
42358        0x033a: 0xd8,
42359        // Ø
42360        0x033b: 0xf8,
42361        // ø
42362        0x033c: 0x250c,
42363        // ┌
42364        0x033d: 0x2510,
42365        // ┐
42366        0x033e: 0x2514,
42367        // └
42368        0x033f: 0x2518 // ┘
42369
42370      };
42371
42372      var getCharFromCode = function getCharFromCode(code) {
42373        if (code === null) {
42374          return '';
42375        }
42376
42377        code = CHARACTER_TRANSLATION[code] || code;
42378        return String.fromCharCode(code);
42379      }; // the index of the last row in a CEA-608 display buffer
42380
42381
42382      var BOTTOM_ROW = 14; // This array is used for mapping PACs -> row #, since there's no way of
42383      // getting it through bit logic.
42384
42385      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
42386      // cells. The "bottom" row is the last element in the outer array.
42387
42388      var createDisplayBuffer = function createDisplayBuffer() {
42389        var result = [],
42390            i = BOTTOM_ROW + 1;
42391
42392        while (i--) {
42393          result.push('');
42394        }
42395
42396        return result;
42397      };
42398
42399      var Cea608Stream = function Cea608Stream(field, dataChannel) {
42400        Cea608Stream.prototype.init.call(this);
42401        this.field_ = field || 0;
42402        this.dataChannel_ = dataChannel || 0;
42403        this.name_ = 'CC' + ((this.field_ << 1 | this.dataChannel_) + 1);
42404        this.setConstants();
42405        this.reset();
42406
42407        this.push = function (packet) {
42408          var data, swap, char0, char1, text; // remove the parity bits
42409
42410          data = packet.ccData & 0x7f7f; // ignore duplicate control codes; the spec demands they're sent twice
42411
42412          if (data === this.lastControlCode_) {
42413            this.lastControlCode_ = null;
42414            return;
42415          } // Store control codes
42416
42417
42418          if ((data & 0xf000) === 0x1000) {
42419            this.lastControlCode_ = data;
42420          } else if (data !== this.PADDING_) {
42421            this.lastControlCode_ = null;
42422          }
42423
42424          char0 = data >>> 8;
42425          char1 = data & 0xff;
42426
42427          if (data === this.PADDING_) {
42428            return;
42429          } else if (data === this.RESUME_CAPTION_LOADING_) {
42430            this.mode_ = 'popOn';
42431          } else if (data === this.END_OF_CAPTION_) {
42432            // If an EOC is received while in paint-on mode, the displayed caption
42433            // text should be swapped to non-displayed memory as if it was a pop-on
42434            // caption. Because of that, we should explicitly switch back to pop-on
42435            // mode
42436            this.mode_ = 'popOn';
42437            this.clearFormatting(packet.pts); // if a caption was being displayed, it's gone now
42438
42439            this.flushDisplayed(packet.pts); // flip memory
42440
42441            swap = this.displayed_;
42442            this.displayed_ = this.nonDisplayed_;
42443            this.nonDisplayed_ = swap; // start measuring the time to display the caption
42444
42445            this.startPts_ = packet.pts;
42446          } else if (data === this.ROLL_UP_2_ROWS_) {
42447            this.rollUpRows_ = 2;
42448            this.setRollUp(packet.pts);
42449          } else if (data === this.ROLL_UP_3_ROWS_) {
42450            this.rollUpRows_ = 3;
42451            this.setRollUp(packet.pts);
42452          } else if (data === this.ROLL_UP_4_ROWS_) {
42453            this.rollUpRows_ = 4;
42454            this.setRollUp(packet.pts);
42455          } else if (data === this.CARRIAGE_RETURN_) {
42456            this.clearFormatting(packet.pts);
42457            this.flushDisplayed(packet.pts);
42458            this.shiftRowsUp_();
42459            this.startPts_ = packet.pts;
42460          } else if (data === this.BACKSPACE_) {
42461            if (this.mode_ === 'popOn') {
42462              this.nonDisplayed_[this.row_] = this.nonDisplayed_[this.row_].slice(0, -1);
42463            } else {
42464              this.displayed_[this.row_] = this.displayed_[this.row_].slice(0, -1);
42465            }
42466          } else if (data === this.ERASE_DISPLAYED_MEMORY_) {
42467            this.flushDisplayed(packet.pts);
42468            this.displayed_ = createDisplayBuffer();
42469          } else if (data === this.ERASE_NON_DISPLAYED_MEMORY_) {
42470            this.nonDisplayed_ = createDisplayBuffer();
42471          } else if (data === this.RESUME_DIRECT_CAPTIONING_) {
42472            if (this.mode_ !== 'paintOn') {
42473              // NOTE: This should be removed when proper caption positioning is
42474              // implemented
42475              this.flushDisplayed(packet.pts);
42476              this.displayed_ = createDisplayBuffer();
42477            }
42478
42479            this.mode_ = 'paintOn';
42480            this.startPts_ = packet.pts; // Append special characters to caption text
42481          } else if (this.isSpecialCharacter(char0, char1)) {
42482            // Bitmask char0 so that we can apply character transformations
42483            // regardless of field and data channel.
42484            // Then byte-shift to the left and OR with char1 so we can pass the
42485            // entire character code to `getCharFromCode`.
42486            char0 = (char0 & 0x03) << 8;
42487            text = getCharFromCode(char0 | char1);
42488            this[this.mode_](packet.pts, text);
42489            this.column_++; // Append extended characters to caption text
42490          } else if (this.isExtCharacter(char0, char1)) {
42491            // Extended characters always follow their "non-extended" equivalents.
42492            // IE if a "è" is desired, you'll always receive "eè"; non-compliant
42493            // decoders are supposed to drop the "è", while compliant decoders
42494            // backspace the "e" and insert "è".
42495            // Delete the previous character
42496            if (this.mode_ === 'popOn') {
42497              this.nonDisplayed_[this.row_] = this.nonDisplayed_[this.row_].slice(0, -1);
42498            } else {
42499              this.displayed_[this.row_] = this.displayed_[this.row_].slice(0, -1);
42500            } // Bitmask char0 so that we can apply character transformations
42501            // regardless of field and data channel.
42502            // Then byte-shift to the left and OR with char1 so we can pass the
42503            // entire character code to `getCharFromCode`.
42504
42505
42506            char0 = (char0 & 0x03) << 8;
42507            text = getCharFromCode(char0 | char1);
42508            this[this.mode_](packet.pts, text);
42509            this.column_++; // Process mid-row codes
42510          } else if (this.isMidRowCode(char0, char1)) {
42511            // Attributes are not additive, so clear all formatting
42512            this.clearFormatting(packet.pts); // According to the standard, mid-row codes
42513            // should be replaced with spaces, so add one now
42514
42515            this[this.mode_](packet.pts, ' ');
42516            this.column_++;
42517
42518            if ((char1 & 0xe) === 0xe) {
42519              this.addFormatting(packet.pts, ['i']);
42520            }
42521
42522            if ((char1 & 0x1) === 0x1) {
42523              this.addFormatting(packet.pts, ['u']);
42524            } // Detect offset control codes and adjust cursor
42525
42526          } else if (this.isOffsetControlCode(char0, char1)) {
42527            // Cursor position is set by indent PAC (see below) in 4-column
42528            // increments, with an additional offset code of 1-3 to reach any
42529            // of the 32 columns specified by CEA-608. So all we need to do
42530            // here is increment the column cursor by the given offset.
42531            this.column_ += char1 & 0x03; // Detect PACs (Preamble Address Codes)
42532          } else if (this.isPAC(char0, char1)) {
42533            // There's no logic for PAC -> row mapping, so we have to just
42534            // find the row code in an array and use its index :(
42535            var row = ROWS.indexOf(data & 0x1f20); // Configure the caption window if we're in roll-up mode
42536
42537            if (this.mode_ === 'rollUp') {
42538              // This implies that the base row is incorrectly set.
42539              // As per the recommendation in CEA-608(Base Row Implementation), defer to the number
42540              // of roll-up rows set.
42541              if (row - this.rollUpRows_ + 1 < 0) {
42542                row = this.rollUpRows_ - 1;
42543              }
42544
42545              this.setRollUp(packet.pts, row);
42546            }
42547
42548            if (row !== this.row_) {
42549              // formatting is only persistent for current row
42550              this.clearFormatting(packet.pts);
42551              this.row_ = row;
42552            } // All PACs can apply underline, so detect and apply
42553            // (All odd-numbered second bytes set underline)
42554
42555
42556            if (char1 & 0x1 && this.formatting_.indexOf('u') === -1) {
42557              this.addFormatting(packet.pts, ['u']);
42558            }
42559
42560            if ((data & 0x10) === 0x10) {
42561              // We've got an indent level code. Each successive even number
42562              // increments the column cursor by 4, so we can get the desired
42563              // column position by bit-shifting to the right (to get n/2)
42564              // and multiplying by 4.
42565              this.column_ = ((data & 0xe) >> 1) * 4;
42566            }
42567
42568            if (this.isColorPAC(char1)) {
42569              // it's a color code, though we only support white, which
42570              // can be either normal or italicized. white italics can be
42571              // either 0x4e or 0x6e depending on the row, so we just
42572              // bitwise-and with 0xe to see if italics should be turned on
42573              if ((char1 & 0xe) === 0xe) {
42574                this.addFormatting(packet.pts, ['i']);
42575              }
42576            } // We have a normal character in char0, and possibly one in char1
42577
42578          } else if (this.isNormalChar(char0)) {
42579            if (char1 === 0x00) {
42580              char1 = null;
42581            }
42582
42583            text = getCharFromCode(char0);
42584            text += getCharFromCode(char1);
42585            this[this.mode_](packet.pts, text);
42586            this.column_ += text.length;
42587          } // finish data processing
42588
42589        };
42590      };
42591
42592      Cea608Stream.prototype = new stream(); // Trigger a cue point that captures the current state of the
42593      // display buffer
42594
42595      Cea608Stream.prototype.flushDisplayed = function (pts) {
42596        var content = this.displayed_ // remove spaces from the start and end of the string
42597        .map(function (row) {
42598          try {
42599            return row.trim();
42600          } catch (e) {
42601            // Ordinarily, this shouldn't happen. However, caption
42602            // parsing errors should not throw exceptions and
42603            // break playback.
42604            // eslint-disable-next-line no-console
42605            console.error('Skipping malformed caption.');
42606            return '';
42607          }
42608        }) // combine all text rows to display in one cue
42609        .join('\n') // and remove blank rows from the start and end, but not the middle
42610        .replace(/^\n+|\n+$/g, '');
42611
42612        if (content.length) {
42613          this.trigger('data', {
42614            startPts: this.startPts_,
42615            endPts: pts,
42616            text: content,
42617            stream: this.name_
42618          });
42619        }
42620      };
42621      /**
42622       * Zero out the data, used for startup and on seek
42623       */
42624
42625
42626      Cea608Stream.prototype.reset = function () {
42627        this.mode_ = 'popOn'; // When in roll-up mode, the index of the last row that will
42628        // actually display captions. If a caption is shifted to a row
42629        // with a lower index than this, it is cleared from the display
42630        // buffer
42631
42632        this.topRow_ = 0;
42633        this.startPts_ = 0;
42634        this.displayed_ = createDisplayBuffer();
42635        this.nonDisplayed_ = createDisplayBuffer();
42636        this.lastControlCode_ = null; // Track row and column for proper line-breaking and spacing
42637
42638        this.column_ = 0;
42639        this.row_ = BOTTOM_ROW;
42640        this.rollUpRows_ = 2; // This variable holds currently-applied formatting
42641
42642        this.formatting_ = [];
42643      };
42644      /**
42645       * Sets up control code and related constants for this instance
42646       */
42647
42648
42649      Cea608Stream.prototype.setConstants = function () {
42650        // The following attributes have these uses:
42651        // ext_ :    char0 for mid-row codes, and the base for extended
42652        //           chars (ext_+0, ext_+1, and ext_+2 are char0s for
42653        //           extended codes)
42654        // control_: char0 for control codes, except byte-shifted to the
42655        //           left so that we can do this.control_ | CONTROL_CODE
42656        // offset_:  char0 for tab offset codes
42657        //
42658        // It's also worth noting that control codes, and _only_ control codes,
42659        // differ between field 1 and field2. Field 2 control codes are always
42660        // their field 1 value plus 1. That's why there's the "| field" on the
42661        // control value.
42662        if (this.dataChannel_ === 0) {
42663          this.BASE_ = 0x10;
42664          this.EXT_ = 0x11;
42665          this.CONTROL_ = (0x14 | this.field_) << 8;
42666          this.OFFSET_ = 0x17;
42667        } else if (this.dataChannel_ === 1) {
42668          this.BASE_ = 0x18;
42669          this.EXT_ = 0x19;
42670          this.CONTROL_ = (0x1c | this.field_) << 8;
42671          this.OFFSET_ = 0x1f;
42672        } // Constants for the LSByte command codes recognized by Cea608Stream. This
42673        // list is not exhaustive. For a more comprehensive listing and semantics see
42674        // http://www.gpo.gov/fdsys/pkg/CFR-2010-title47-vol1/pdf/CFR-2010-title47-vol1-sec15-119.pdf
42675        // Padding
42676
42677
42678        this.PADDING_ = 0x0000; // Pop-on Mode
42679
42680        this.RESUME_CAPTION_LOADING_ = this.CONTROL_ | 0x20;
42681        this.END_OF_CAPTION_ = this.CONTROL_ | 0x2f; // Roll-up Mode
42682
42683        this.ROLL_UP_2_ROWS_ = this.CONTROL_ | 0x25;
42684        this.ROLL_UP_3_ROWS_ = this.CONTROL_ | 0x26;
42685        this.ROLL_UP_4_ROWS_ = this.CONTROL_ | 0x27;
42686        this.CARRIAGE_RETURN_ = this.CONTROL_ | 0x2d; // paint-on mode
42687
42688        this.RESUME_DIRECT_CAPTIONING_ = this.CONTROL_ | 0x29; // Erasure
42689
42690        this.BACKSPACE_ = this.CONTROL_ | 0x21;
42691        this.ERASE_DISPLAYED_MEMORY_ = this.CONTROL_ | 0x2c;
42692        this.ERASE_NON_DISPLAYED_MEMORY_ = this.CONTROL_ | 0x2e;
42693      };
42694      /**
42695       * Detects if the 2-byte packet data is a special character
42696       *
42697       * Special characters have a second byte in the range 0x30 to 0x3f,
42698       * with the first byte being 0x11 (for data channel 1) or 0x19 (for
42699       * data channel 2).
42700       *
42701       * @param  {Integer} char0 The first byte
42702       * @param  {Integer} char1 The second byte
42703       * @return {Boolean}       Whether the 2 bytes are an special character
42704       */
42705
42706
42707      Cea608Stream.prototype.isSpecialCharacter = function (char0, char1) {
42708        return char0 === this.EXT_ && char1 >= 0x30 && char1 <= 0x3f;
42709      };
42710      /**
42711       * Detects if the 2-byte packet data is an extended character
42712       *
42713       * Extended characters have a second byte in the range 0x20 to 0x3f,
42714       * with the first byte being 0x12 or 0x13 (for data channel 1) or
42715       * 0x1a or 0x1b (for data channel 2).
42716       *
42717       * @param  {Integer} char0 The first byte
42718       * @param  {Integer} char1 The second byte
42719       * @return {Boolean}       Whether the 2 bytes are an extended character
42720       */
42721
42722
42723      Cea608Stream.prototype.isExtCharacter = function (char0, char1) {
42724        return (char0 === this.EXT_ + 1 || char0 === this.EXT_ + 2) && char1 >= 0x20 && char1 <= 0x3f;
42725      };
42726      /**
42727       * Detects if the 2-byte packet is a mid-row code
42728       *
42729       * Mid-row codes have a second byte in the range 0x20 to 0x2f, with
42730       * the first byte being 0x11 (for data channel 1) or 0x19 (for data
42731       * channel 2).
42732       *
42733       * @param  {Integer} char0 The first byte
42734       * @param  {Integer} char1 The second byte
42735       * @return {Boolean}       Whether the 2 bytes are a mid-row code
42736       */
42737
42738
42739      Cea608Stream.prototype.isMidRowCode = function (char0, char1) {
42740        return char0 === this.EXT_ && char1 >= 0x20 && char1 <= 0x2f;
42741      };
42742      /**
42743       * Detects if the 2-byte packet is an offset control code
42744       *
42745       * Offset control codes have a second byte in the range 0x21 to 0x23,
42746       * with the first byte being 0x17 (for data channel 1) or 0x1f (for
42747       * data channel 2).
42748       *
42749       * @param  {Integer} char0 The first byte
42750       * @param  {Integer} char1 The second byte
42751       * @return {Boolean}       Whether the 2 bytes are an offset control code
42752       */
42753
42754
42755      Cea608Stream.prototype.isOffsetControlCode = function (char0, char1) {
42756        return char0 === this.OFFSET_ && char1 >= 0x21 && char1 <= 0x23;
42757      };
42758      /**
42759       * Detects if the 2-byte packet is a Preamble Address Code
42760       *
42761       * PACs have a first byte in the range 0x10 to 0x17 (for data channel 1)
42762       * or 0x18 to 0x1f (for data channel 2), with the second byte in the
42763       * range 0x40 to 0x7f.
42764       *
42765       * @param  {Integer} char0 The first byte
42766       * @param  {Integer} char1 The second byte
42767       * @return {Boolean}       Whether the 2 bytes are a PAC
42768       */
42769
42770
42771      Cea608Stream.prototype.isPAC = function (char0, char1) {
42772        return char0 >= this.BASE_ && char0 < this.BASE_ + 8 && char1 >= 0x40 && char1 <= 0x7f;
42773      };
42774      /**
42775       * Detects if a packet's second byte is in the range of a PAC color code
42776       *
42777       * PAC color codes have the second byte be in the range 0x40 to 0x4f, or
42778       * 0x60 to 0x6f.
42779       *
42780       * @param  {Integer} char1 The second byte
42781       * @return {Boolean}       Whether the byte is a color PAC
42782       */
42783
42784
42785      Cea608Stream.prototype.isColorPAC = function (char1) {
42786        return char1 >= 0x40 && char1 <= 0x4f || char1 >= 0x60 && char1 <= 0x7f;
42787      };
42788      /**
42789       * Detects if a single byte is in the range of a normal character
42790       *
42791       * Normal text bytes are in the range 0x20 to 0x7f.
42792       *
42793       * @param  {Integer} char  The byte
42794       * @return {Boolean}       Whether the byte is a normal character
42795       */
42796
42797
42798      Cea608Stream.prototype.isNormalChar = function (_char) {
42799        return _char >= 0x20 && _char <= 0x7f;
42800      };
42801      /**
42802       * Configures roll-up
42803       *
42804       * @param  {Integer} pts         Current PTS
42805       * @param  {Integer} newBaseRow  Used by PACs to slide the current window to
42806       *                               a new position
42807       */
42808
42809
42810      Cea608Stream.prototype.setRollUp = function (pts, newBaseRow) {
42811        // Reset the base row to the bottom row when switching modes
42812        if (this.mode_ !== 'rollUp') {
42813          this.row_ = BOTTOM_ROW;
42814          this.mode_ = 'rollUp'; // Spec says to wipe memories when switching to roll-up
42815
42816          this.flushDisplayed(pts);
42817          this.nonDisplayed_ = createDisplayBuffer();
42818          this.displayed_ = createDisplayBuffer();
42819        }
42820
42821        if (newBaseRow !== undefined && newBaseRow !== this.row_) {
42822          // move currently displayed captions (up or down) to the new base row
42823          for (var i = 0; i < this.rollUpRows_; i++) {
42824            this.displayed_[newBaseRow - i] = this.displayed_[this.row_ - i];
42825            this.displayed_[this.row_ - i] = '';
42826          }
42827        }
42828
42829        if (newBaseRow === undefined) {
42830          newBaseRow = this.row_;
42831        }
42832
42833        this.topRow_ = newBaseRow - this.rollUpRows_ + 1;
42834      }; // Adds the opening HTML tag for the passed character to the caption text,
42835      // and keeps track of it for later closing
42836
42837
42838      Cea608Stream.prototype.addFormatting = function (pts, format) {
42839        this.formatting_ = this.formatting_.concat(format);
42840        var text = format.reduce(function (text, format) {
vendor: 16,176 bytes, lines 42841-43283
42841          return text + '<' + format + '>';
42842        }, '');
42843        this[this.mode_](pts, text);
42844      }; // Adds HTML closing tags for current formatting to caption text and
42845      // clears remembered formatting
42846
42847
42848      Cea608Stream.prototype.clearFormatting = function (pts) {
42849        if (!this.formatting_.length) {
42850          return;
42851        }
42852
42853        var text = this.formatting_.reverse().reduce(function (text, format) {
42854          return text + '</' + format + '>';
42855        }, '');
42856        this.formatting_ = [];
42857        this[this.mode_](pts, text);
42858      }; // Mode Implementations
42859
42860
42861      Cea608Stream.prototype.popOn = function (pts, text) {
42862        var baseRow = this.nonDisplayed_[this.row_]; // buffer characters
42863
42864        baseRow += text;
42865        this.nonDisplayed_[this.row_] = baseRow;
42866      };
42867
42868      Cea608Stream.prototype.rollUp = function (pts, text) {
42869        var baseRow = this.displayed_[this.row_];
42870        baseRow += text;
42871        this.displayed_[this.row_] = baseRow;
42872      };
42873
42874      Cea608Stream.prototype.shiftRowsUp_ = function () {
42875        var i; // clear out inactive rows
42876
42877        for (i = 0; i < this.topRow_; i++) {
42878          this.displayed_[i] = '';
42879        }
42880
42881        for (i = this.row_ + 1; i < BOTTOM_ROW + 1; i++) {
42882          this.displayed_[i] = '';
42883        } // shift displayed rows up
42884
42885
42886        for (i = this.topRow_; i < this.row_; i++) {
42887          this.displayed_[i] = this.displayed_[i + 1];
42888        } // clear out the bottom row
42889
42890
42891        this.displayed_[this.row_] = '';
42892      };
42893
42894      Cea608Stream.prototype.paintOn = function (pts, text) {
42895        var baseRow = this.displayed_[this.row_];
42896        baseRow += text;
42897        this.displayed_[this.row_] = baseRow;
42898      }; // exports
42899
42900
42901      var captionStream = {
42902        CaptionStream: CaptionStream,
42903        Cea608Stream: Cea608Stream
42904      };
42905      /**
42906       * mux.js
42907       *
42908       * Copyright (c) Brightcove
42909       * Licensed Apache-2.0 https://github.com/videojs/mux.js/blob/master/LICENSE
42910       */
42911
42912      var streamTypes = {
42913        H264_STREAM_TYPE: 0x1B,
42914        ADTS_STREAM_TYPE: 0x0F,
42915        METADATA_STREAM_TYPE: 0x15
42916      };
42917      var MAX_TS = 8589934592;
42918      var RO_THRESH = 4294967296;
42919      var TYPE_SHARED = 'shared';
42920
42921      var handleRollover = function handleRollover(value, reference) {
42922        var direction = 1;
42923
42924        if (value > reference) {
42925          // If the current timestamp value is greater than our reference timestamp and we detect a
42926          // timestamp rollover, this means the roll over is happening in the opposite direction.
42927          // Example scenario: Enter a long stream/video just after a rollover occurred. The reference
42928          // point will be set to a small number, e.g. 1. The user then seeks backwards over the
42929          // rollover point. In loading this segment, the timestamp values will be very large,
42930          // e.g. 2^33 - 1. Since this comes before the data we loaded previously, we want to adjust
42931          // the time stamp to be `value - 2^33`.
42932          direction = -1;
42933        } // Note: A seek forwards or back that is greater than the RO_THRESH (2^32, ~13 hours) will
42934        // cause an incorrect adjustment.
42935
42936
42937        while (Math.abs(reference - value) > RO_THRESH) {
42938          value += direction * MAX_TS;
42939        }
42940
42941        return value;
42942      };
42943
42944      var TimestampRolloverStream = function TimestampRolloverStream(type) {
42945        var lastDTS, referenceDTS;
42946        TimestampRolloverStream.prototype.init.call(this); // The "shared" type is used in cases where a stream will contain muxed
42947        // video and audio. We could use `undefined` here, but having a string
42948        // makes debugging a little clearer.
42949
42950        this.type_ = type || TYPE_SHARED;
42951
42952        this.push = function (data) {
42953          // Any "shared" rollover streams will accept _all_ data. Otherwise,
42954          // streams will only accept data that matches their type.
42955          if (this.type_ !== TYPE_SHARED && data.type !== this.type_) {
42956            return;
42957          }
42958
42959          if (referenceDTS === undefined) {
42960            referenceDTS = data.dts;
42961          }
42962
42963          data.dts = handleRollover(data.dts, referenceDTS);
42964          data.pts = handleRollover(data.pts, referenceDTS);
42965          lastDTS = data.dts;
42966          this.trigger('data', data);
42967        };
42968
42969        this.flush = function () {
42970          referenceDTS = lastDTS;
42971          this.trigger('done');
42972        };
42973
42974        this.endTimeline = function () {
42975          this.flush();
42976          this.trigger('endedtimeline');
42977        };
42978
42979        this.discontinuity = function () {
42980          referenceDTS = void 0;
42981          lastDTS = void 0;
42982        };
42983
42984        this.reset = function () {
42985          this.discontinuity();
42986          this.trigger('reset');
42987        };
42988      };
42989
42990      TimestampRolloverStream.prototype = new stream();
42991      var timestampRolloverStream = {
42992        TimestampRolloverStream: TimestampRolloverStream,
42993        handleRollover: handleRollover
42994      };
42995
42996      var percentEncode = function percentEncode(bytes, start, end) {
42997        var i,
42998            result = '';
42999
43000        for (i = start; i < end; i++) {
43001          result += '%' + ('00' + bytes[i].toString(16)).slice(-2);
43002        }
43003
43004        return result;
43005      },
43006          // return the string representation of the specified byte range,
43007      // interpreted as UTf-8.
43008      parseUtf8 = function parseUtf8(bytes, start, end) {
43009        return decodeURIComponent(percentEncode(bytes, start, end));
43010      },
43011          // return the string representation of the specified byte range,
43012      // interpreted as ISO-8859-1.
43013      parseIso88591 = function parseIso88591(bytes, start, end) {
43014        return unescape(percentEncode(bytes, start, end)); // jshint ignore:line
43015      },
43016          parseSyncSafeInteger = function parseSyncSafeInteger(data) {
43017        return data[0] << 21 | data[1] << 14 | data[2] << 7 | data[3];
43018      },
43019          tagParsers = {
43020        TXXX: function TXXX(tag) {
43021          var i;
43022
43023          if (tag.data[0] !== 3) {
43024            // ignore frames with unrecognized character encodings
43025            return;
43026          }
43027
43028          for (i = 1; i < tag.data.length; i++) {
43029            if (tag.data[i] === 0) {
43030              // parse the text fields
43031              tag.description = parseUtf8(tag.data, 1, i); // do not include the null terminator in the tag value
43032
43033              tag.value = parseUtf8(tag.data, i + 1, tag.data.length).replace(/\0*$/, '');
43034              break;
43035            }
43036          }
43037
43038          tag.data = tag.value;
43039        },
43040        WXXX: function WXXX(tag) {
43041          var i;
43042
43043          if (tag.data[0] !== 3) {
43044            // ignore frames with unrecognized character encodings
43045            return;
43046          }
43047
43048          for (i = 1; i < tag.data.length; i++) {
43049            if (tag.data[i] === 0) {
43050              // parse the description and URL fields
43051              tag.description = parseUtf8(tag.data, 1, i);
43052              tag.url = parseUtf8(tag.data, i + 1, tag.data.length);
43053              break;
43054            }
43055          }
43056        },
43057        PRIV: function PRIV(tag) {
43058          var i;
43059
43060          for (i = 0; i < tag.data.length; i++) {
43061            if (tag.data[i] === 0) {
43062              // parse the description and URL fields
43063              tag.owner = parseIso88591(tag.data, 0, i);
43064              break;
43065            }
43066          }
43067
43068          tag.privateData = tag.data.subarray(i + 1);
43069          tag.data = tag.privateData;
43070        }
43071      },
43072          _MetadataStream;
43073
43074      _MetadataStream = function MetadataStream(options) {
43075        var settings = {
43076          debug: !!(options && options.debug),
43077          // the bytes of the program-level descriptor field in MP2T
43078          // see ISO/IEC 13818-1:2013 (E), section 2.6 "Program and
43079          // program element descriptors"
43080          descriptor: options && options.descriptor
43081        },
43082            // the total size in bytes of the ID3 tag being parsed
43083        tagSize = 0,
43084            // tag data that is not complete enough to be parsed
43085        buffer = [],
43086            // the total number of bytes currently in the buffer
43087        bufferSize = 0,
43088            i;
43089
43090        _MetadataStream.prototype.init.call(this); // calculate the text track in-band metadata track dispatch type
43091        // https://html.spec.whatwg.org/multipage/embedded-content.html#steps-to-expose-a-media-resource-specific-text-track
43092
43093
43094        this.dispatchType = streamTypes.METADATA_STREAM_TYPE.toString(16);
43095
43096        if (settings.descriptor) {
43097          for (i = 0; i < settings.descriptor.length; i++) {
43098            this.dispatchType += ('00' + settings.descriptor[i].toString(16)).slice(-2);
43099          }
43100        }
43101
43102        this.push = function (chunk) {
43103          var tag, frameStart, frameSize, frame, i, frameHeader;
43104
43105          if (chunk.type !== 'timed-metadata') {
43106            return;
43107          } // if data_alignment_indicator is set in the PES header,
43108          // we must have the start of a new ID3 tag. Assume anything
43109          // remaining in the buffer was malformed and throw it out
43110
43111
43112          if (chunk.dataAlignmentIndicator) {
43113            bufferSize = 0;
43114            buffer.length = 0;
43115          } // ignore events that don't look like ID3 data
43116
43117
43118          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))) {
43119            if (settings.debug) {
43120              // eslint-disable-next-line no-console
43121              console.log('Skipping unrecognized metadata packet');
43122            }
43123
43124            return;
43125          } // add this chunk to the data we've collected so far
43126
43127
43128          buffer.push(chunk);
43129          bufferSize += chunk.data.byteLength; // grab the size of the entire frame from the ID3 header
43130
43131          if (buffer.length === 1) {
43132            // the frame size is transmitted as a 28-bit integer in the
43133            // last four bytes of the ID3 header.
43134            // The most significant bit of each byte is dropped and the
43135            // results concatenated to recover the actual value.
43136            tagSize = parseSyncSafeInteger(chunk.data.subarray(6, 10)); // ID3 reports the tag size excluding the header but it's more
43137            // convenient for our comparisons to include it
43138
43139            tagSize += 10;
43140          } // if the entire frame has not arrived, wait for more data
43141
43142
43143          if (bufferSize < tagSize) {
43144            return;
43145          } // collect the entire frame so it can be parsed
43146
43147
43148          tag = {
43149            data: new Uint8Array(tagSize),
43150            frames: [],
43151            pts: buffer[0].pts,
43152            dts: buffer[0].dts
43153          };
43154
43155          for (i = 0; i < tagSize;) {
43156            tag.data.set(buffer[0].data.subarray(0, tagSize - i), i);
43157            i += buffer[0].data.byteLength;
43158            bufferSize -= buffer[0].data.byteLength;
43159            buffer.shift();
43160          } // find the start of the first frame and the end of the tag
43161
43162
43163          frameStart = 10;
43164
43165          if (tag.data[5] & 0x40) {
43166            // advance the frame start past the extended header
43167            frameStart += 4; // header size field
43168
43169            frameStart += parseSyncSafeInteger(tag.data.subarray(10, 14)); // clip any padding off the end
43170
43171            tagSize -= parseSyncSafeInteger(tag.data.subarray(16, 20));
43172          } // parse one or more ID3 frames
43173          // http://id3.org/id3v2.3.0#ID3v2_frame_overview
43174
43175
43176          do {
43177            // determine the number of bytes in this frame
43178            frameSize = parseSyncSafeInteger(tag.data.subarray(frameStart + 4, frameStart + 8));
43179
43180            if (frameSize < 1) {
43181              // eslint-disable-next-line no-console
43182              return console.log('Malformed ID3 frame encountered. Skipping metadata parsing.');
43183            }
43184
43185            frameHeader = String.fromCharCode(tag.data[frameStart], tag.data[frameStart + 1], tag.data[frameStart + 2], tag.data[frameStart + 3]);
43186            frame = {
43187              id: frameHeader,
43188              data: tag.data.subarray(frameStart + 10, frameStart + frameSize + 10)
43189            };
43190            frame.key = frame.id;
43191
43192            if (tagParsers[frame.id]) {
43193              tagParsers[frame.id](frame); // handle the special PRIV frame used to indicate the start
43194              // time for raw AAC data
43195
43196              if (frame.owner === 'com.apple.streaming.transportStreamTimestamp') {
43197                var d = frame.data,
43198                    size = (d[3] & 0x01) << 30 | d[4] << 22 | d[5] << 14 | d[6] << 6 | d[7] >>> 2;
43199                size *= 4;
43200                size += d[7] & 0x03;
43201                frame.timeStamp = size; // in raw AAC, all subsequent data will be timestamped based
43202                // on the value of this frame
43203                // we couldn't have known the appropriate pts and dts before
43204                // parsing this ID3 tag so set those values now
43205
43206                if (tag.pts === undefined && tag.dts === undefined) {
43207                  tag.pts = frame.timeStamp;
43208                  tag.dts = frame.timeStamp;
43209                }
43210
43211                this.trigger('timestamp', frame);
43212              }
43213            }
43214
43215            tag.frames.push(frame);
43216            frameStart += 10; // advance past the frame header
43217
43218            frameStart += frameSize; // advance past the frame body
43219          } while (frameStart < tagSize);
43220
43221          this.trigger('data', tag);
43222        };
43223      };
43224
43225      _MetadataStream.prototype = new stream();
43226      var metadataStream = _MetadataStream;
43227      var TimestampRolloverStream$1 = timestampRolloverStream.TimestampRolloverStream; // object types
43228
43229      var _TransportPacketStream, _TransportParseStream, _ElementaryStream; // constants
43230
43231
43232      var MP2T_PACKET_LENGTH = 188,
43233          // bytes
43234      SYNC_BYTE = 0x47;
43235      /**
43236       * Splits an incoming stream of binary data into MPEG-2 Transport
43237       * Stream packets.
43238       */
43239
43240      _TransportPacketStream = function TransportPacketStream() {
43241        var buffer = new Uint8Array(MP2T_PACKET_LENGTH),
43242            bytesInBuffer = 0;
43243
43244        _TransportPacketStream.prototype.init.call(this); // Deliver new bytes to the stream.
43245
43246        /**
43247         * Split a stream of data into M2TS packets
43248        **/
43249
43250
43251        this.push = function (bytes) {
43252          var startIndex = 0,
43253              endIndex = MP2T_PACKET_LENGTH,
43254              everything; // If there are bytes remaining from the last segment, prepend them to the
43255          // bytes that were pushed in
43256
43257          if (bytesInBuffer) {
43258            everything = new Uint8Array(bytes.byteLength + bytesInBuffer);
43259            everything.set(buffer.subarray(0, bytesInBuffer));
43260            everything.set(bytes, bytesInBuffer);
43261            bytesInBuffer = 0;
43262          } else {
43263            everything = bytes;
43264          } // While we have enough data for a packet
43265
43266
43267          while (endIndex < everything.byteLength) {
43268            // Look for a pair of start and end sync bytes in the data..
43269            if (everything[startIndex] === SYNC_BYTE && everything[endIndex] === SYNC_BYTE) {
43270              // We found a packet so emit it and jump one whole packet forward in
43271              // the stream
43272              this.trigger('data', everything.subarray(startIndex, endIndex));
43273              startIndex += MP2T_PACKET_LENGTH;
43274              endIndex += MP2T_PACKET_LENGTH;
43275              continue;
43276            } // If we get here, we have somehow become de-synchronized and we need to step
43277            // forward one byte at a time until we find a pair of sync bytes that denote
43278            // a packet
43279
43280
43281            startIndex++;
43282            endIndex++;
43283          } // If there was some data left over at the end of the segment that couldn't
vendor: 17,064 bytes, lines 43284-43719
43284          // possibly be a whole packet, keep it because it might be the start of a packet
43285          // that continues in the next segment
43286
43287
43288          if (startIndex < everything.byteLength) {
43289            buffer.set(everything.subarray(startIndex), 0);
43290            bytesInBuffer = everything.byteLength - startIndex;
43291          }
43292        };
43293        /**
43294         * Passes identified M2TS packets to the TransportParseStream to be parsed
43295        **/
43296
43297
43298        this.flush = function () {
43299          // If the buffer contains a whole packet when we are being flushed, emit it
43300          // and empty the buffer. Otherwise hold onto the data because it may be
43301          // important for decoding the next segment
43302          if (bytesInBuffer === MP2T_PACKET_LENGTH && buffer[0] === SYNC_BYTE) {
43303            this.trigger('data', buffer);
43304            bytesInBuffer = 0;
43305          }
43306
43307          this.trigger('done');
43308        };
43309
43310        this.endTimeline = function () {
43311          this.flush();
43312          this.trigger('endedtimeline');
43313        };
43314
43315        this.reset = function () {
43316          bytesInBuffer = 0;
43317          this.trigger('reset');
43318        };
43319      };
43320
43321      _TransportPacketStream.prototype = new stream();
43322      /**
43323       * Accepts an MP2T TransportPacketStream and emits data events with parsed
43324       * forms of the individual transport stream packets.
43325       */
43326
43327      _TransportParseStream = function TransportParseStream() {
43328        var parsePsi, parsePat, parsePmt, self;
43329
43330        _TransportParseStream.prototype.init.call(this);
43331
43332        self = this;
43333        this.packetsWaitingForPmt = [];
43334        this.programMapTable = undefined;
43335
43336        parsePsi = function parsePsi(payload, psi) {
43337          var offset = 0; // PSI packets may be split into multiple sections and those
43338          // sections may be split into multiple packets. If a PSI
43339          // section starts in this packet, the payload_unit_start_indicator
43340          // will be true and the first byte of the payload will indicate
43341          // the offset from the current position to the start of the
43342          // section.
43343
43344          if (psi.payloadUnitStartIndicator) {
43345            offset += payload[offset] + 1;
43346          }
43347
43348          if (psi.type === 'pat') {
43349            parsePat(payload.subarray(offset), psi);
43350          } else {
43351            parsePmt(payload.subarray(offset), psi);
43352          }
43353        };
43354
43355        parsePat = function parsePat(payload, pat) {
43356          pat.section_number = payload[7]; // eslint-disable-line camelcase
43357
43358          pat.last_section_number = payload[8]; // eslint-disable-line camelcase
43359          // skip the PSI header and parse the first PMT entry
43360
43361          self.pmtPid = (payload[10] & 0x1F) << 8 | payload[11];
43362          pat.pmtPid = self.pmtPid;
43363        };
43364        /**
43365         * Parse out the relevant fields of a Program Map Table (PMT).
43366         * @param payload {Uint8Array} the PMT-specific portion of an MP2T
43367         * packet. The first byte in this array should be the table_id
43368         * field.
43369         * @param pmt {object} the object that should be decorated with
43370         * fields parsed from the PMT.
43371         */
43372
43373
43374        parsePmt = function parsePmt(payload, pmt) {
43375          var sectionLength, tableEnd, programInfoLength, offset; // PMTs can be sent ahead of the time when they should actually
43376          // take effect. We don't believe this should ever be the case
43377          // for HLS but we'll ignore "forward" PMT declarations if we see
43378          // them. Future PMT declarations have the current_next_indicator
43379          // set to zero.
43380
43381          if (!(payload[5] & 0x01)) {
43382            return;
43383          } // overwrite any existing program map table
43384
43385
43386          self.programMapTable = {
43387            video: null,
43388            audio: null,
43389            'timed-metadata': {}
43390          }; // the mapping table ends at the end of the current section
43391
43392          sectionLength = (payload[1] & 0x0f) << 8 | payload[2];
43393          tableEnd = 3 + sectionLength - 4; // to determine where the table is, we have to figure out how
43394          // long the program info descriptors are
43395
43396          programInfoLength = (payload[10] & 0x0f) << 8 | payload[11]; // advance the offset to the first entry in the mapping table
43397
43398          offset = 12 + programInfoLength;
43399
43400          while (offset < tableEnd) {
43401            var streamType = payload[offset];
43402            var pid = (payload[offset + 1] & 0x1F) << 8 | payload[offset + 2]; // only map a single elementary_pid for audio and video stream types
43403            // TODO: should this be done for metadata too? for now maintain behavior of
43404            //       multiple metadata streams
43405
43406            if (streamType === streamTypes.H264_STREAM_TYPE && self.programMapTable.video === null) {
43407              self.programMapTable.video = pid;
43408            } else if (streamType === streamTypes.ADTS_STREAM_TYPE && self.programMapTable.audio === null) {
43409              self.programMapTable.audio = pid;
43410            } else if (streamType === streamTypes.METADATA_STREAM_TYPE) {
43411              // map pid to stream type for metadata streams
43412              self.programMapTable['timed-metadata'][pid] = streamType;
43413            } // move to the next table entry
43414            // skip past the elementary stream descriptors, if present
43415
43416
43417            offset += ((payload[offset + 3] & 0x0F) << 8 | payload[offset + 4]) + 5;
43418          } // record the map on the packet as well
43419
43420
43421          pmt.programMapTable = self.programMapTable;
43422        };
43423        /**
43424         * Deliver a new MP2T packet to the next stream in the pipeline.
43425         */
43426
43427
43428        this.push = function (packet) {
43429          var result = {},
43430              offset = 4;
43431          result.payloadUnitStartIndicator = !!(packet[1] & 0x40); // pid is a 13-bit field starting at the last bit of packet[1]
43432
43433          result.pid = packet[1] & 0x1f;
43434          result.pid <<= 8;
43435          result.pid |= packet[2]; // if an adaption field is present, its length is specified by the
43436          // fifth byte of the TS packet header. The adaptation field is
43437          // used to add stuffing to PES packets that don't fill a complete
43438          // TS packet, and to specify some forms of timing and control data
43439          // that we do not currently use.
43440
43441          if ((packet[3] & 0x30) >>> 4 > 0x01) {
43442            offset += packet[offset] + 1;
43443          } // parse the rest of the packet based on the type
43444
43445
43446          if (result.pid === 0) {
43447            result.type = 'pat';
43448            parsePsi(packet.subarray(offset), result);
43449            this.trigger('data', result);
43450          } else if (result.pid === this.pmtPid) {
43451            result.type = 'pmt';
43452            parsePsi(packet.subarray(offset), result);
43453            this.trigger('data', result); // if there are any packets waiting for a PMT to be found, process them now
43454
43455            while (this.packetsWaitingForPmt.length) {
43456              this.processPes_.apply(this, this.packetsWaitingForPmt.shift());
43457            }
43458          } else if (this.programMapTable === undefined) {
43459            // When we have not seen a PMT yet, defer further processing of
43460            // PES packets until one has been parsed
43461            this.packetsWaitingForPmt.push([packet, offset, result]);
43462          } else {
43463            this.processPes_(packet, offset, result);
43464          }
43465        };
43466
43467        this.processPes_ = function (packet, offset, result) {
43468          // set the appropriate stream type
43469          if (result.pid === this.programMapTable.video) {
43470            result.streamType = streamTypes.H264_STREAM_TYPE;
43471          } else if (result.pid === this.programMapTable.audio) {
43472            result.streamType = streamTypes.ADTS_STREAM_TYPE;
43473          } else {
43474            // if not video or audio, it is timed-metadata or unknown
43475            // if unknown, streamType will be undefined
43476            result.streamType = this.programMapTable['timed-metadata'][result.pid];
43477          }
43478
43479          result.type = 'pes';
43480          result.data = packet.subarray(offset);
43481          this.trigger('data', result);
43482        };
43483      };
43484
43485      _TransportParseStream.prototype = new stream();
43486      _TransportParseStream.STREAM_TYPES = {
43487        h264: 0x1b,
43488        adts: 0x0f
43489      };
43490      /**
43491       * Reconsistutes program elementary stream (PES) packets from parsed
43492       * transport stream packets. That is, if you pipe an
43493       * mp2t.TransportParseStream into a mp2t.ElementaryStream, the output
43494       * events will be events which capture the bytes for individual PES
43495       * packets plus relevant metadata that has been extracted from the
43496       * container.
43497       */
43498
43499      _ElementaryStream = function ElementaryStream() {
43500        var self = this,
43501            // PES packet fragments
43502        video = {
43503          data: [],
43504          size: 0
43505        },
43506            audio = {
43507          data: [],
43508          size: 0
43509        },
43510            timedMetadata = {
43511          data: [],
43512          size: 0
43513        },
43514            programMapTable,
43515            parsePes = function parsePes(payload, pes) {
43516          var ptsDtsFlags; // get the packet length, this will be 0 for video
43517
43518          pes.packetLength = 6 + (payload[4] << 8 | payload[5]); // find out if this packets starts a new keyframe
43519
43520          pes.dataAlignmentIndicator = (payload[6] & 0x04) !== 0; // PES packets may be annotated with a PTS value, or a PTS value
43521          // and a DTS value. Determine what combination of values is
43522          // available to work with.
43523
43524          ptsDtsFlags = payload[7]; // PTS and DTS are normally stored as a 33-bit number.  Javascript
43525          // performs all bitwise operations on 32-bit integers but javascript
43526          // supports a much greater range (52-bits) of integer using standard
43527          // mathematical operations.
43528          // We construct a 31-bit value using bitwise operators over the 31
43529          // most significant bits and then multiply by 4 (equal to a left-shift
43530          // of 2) before we add the final 2 least significant bits of the
43531          // timestamp (equal to an OR.)
43532
43533          if (ptsDtsFlags & 0xC0) {
43534            // the PTS and DTS are not written out directly. For information
43535            // on how they are encoded, see
43536            // http://dvd.sourceforge.net/dvdinfo/pes-hdr.html
43537            pes.pts = (payload[9] & 0x0E) << 27 | (payload[10] & 0xFF) << 20 | (payload[11] & 0xFE) << 12 | (payload[12] & 0xFF) << 5 | (payload[13] & 0xFE) >>> 3;
43538            pes.pts *= 4; // Left shift by 2
43539
43540            pes.pts += (payload[13] & 0x06) >>> 1; // OR by the two LSBs
43541
43542            pes.dts = pes.pts;
43543
43544            if (ptsDtsFlags & 0x40) {
43545              pes.dts = (payload[14] & 0x0E) << 27 | (payload[15] & 0xFF) << 20 | (payload[16] & 0xFE) << 12 | (payload[17] & 0xFF) << 5 | (payload[18] & 0xFE) >>> 3;
43546              pes.dts *= 4; // Left shift by 2
43547
43548              pes.dts += (payload[18] & 0x06) >>> 1; // OR by the two LSBs
43549            }
43550          } // the data section starts immediately after the PES header.
43551          // pes_header_data_length specifies the number of header bytes
43552          // that follow the last byte of the field.
43553
43554
43555          pes.data = payload.subarray(9 + payload[8]);
43556        },
43557
43558        /**
43559          * Pass completely parsed PES packets to the next stream in the pipeline
43560         **/
43561        flushStream = function flushStream(stream$$1, type, forceFlush) {
43562          var packetData = new Uint8Array(stream$$1.size),
43563              event = {
43564            type: type
43565          },
43566              i = 0,
43567              offset = 0,
43568              packetFlushable = false,
43569              fragment; // do nothing if there is not enough buffered data for a complete
43570          // PES header
43571
43572          if (!stream$$1.data.length || stream$$1.size < 9) {
43573            return;
43574          }
43575
43576          event.trackId = stream$$1.data[0].pid; // reassemble the packet
43577
43578          for (i = 0; i < stream$$1.data.length; i++) {
43579            fragment = stream$$1.data[i];
43580            packetData.set(fragment.data, offset);
43581            offset += fragment.data.byteLength;
43582          } // parse assembled packet's PES header
43583
43584
43585          parsePes(packetData, event); // non-video PES packets MUST have a non-zero PES_packet_length
43586          // check that there is enough stream data to fill the packet
43587
43588          packetFlushable = type === 'video' || event.packetLength <= stream$$1.size; // flush pending packets if the conditions are right
43589
43590          if (forceFlush || packetFlushable) {
43591            stream$$1.size = 0;
43592            stream$$1.data.length = 0;
43593          } // only emit packets that are complete. this is to avoid assembling
43594          // incomplete PES packets due to poor segmentation
43595
43596
43597          if (packetFlushable) {
43598            self.trigger('data', event);
43599          }
43600        };
43601
43602        _ElementaryStream.prototype.init.call(this);
43603        /**
43604         * Identifies M2TS packet types and parses PES packets using metadata
43605         * parsed from the PMT
43606         **/
43607
43608
43609        this.push = function (data) {
43610          ({
43611            pat: function pat() {// we have to wait for the PMT to arrive as well before we
43612              // have any meaningful metadata
43613            },
43614            pes: function pes() {
43615              var stream$$1, streamType;
43616
43617              switch (data.streamType) {
43618                case streamTypes.H264_STREAM_TYPE:
43619                case streamTypes.H264_STREAM_TYPE:
43620                  stream$$1 = video;
43621                  streamType = 'video';
43622                  break;
43623
43624                case streamTypes.ADTS_STREAM_TYPE:
43625                  stream$$1 = audio;
43626                  streamType = 'audio';
43627                  break;
43628
43629                case streamTypes.METADATA_STREAM_TYPE:
43630                  stream$$1 = timedMetadata;
43631                  streamType = 'timed-metadata';
43632                  break;
43633
43634                default:
43635                  // ignore unknown stream types
43636                  return;
43637              } // if a new packet is starting, we can flush the completed
43638              // packet
43639
43640
43641              if (data.payloadUnitStartIndicator) {
43642                flushStream(stream$$1, streamType, true);
43643              } // buffer this fragment until we are sure we've received the
43644              // complete payload
43645
43646
43647              stream$$1.data.push(data);
43648              stream$$1.size += data.data.byteLength;
43649            },
43650            pmt: function pmt() {
43651              var event = {
43652                type: 'metadata',
43653                tracks: []
43654              };
43655              programMapTable = data.programMapTable; // translate audio and video streams to tracks
43656
43657              if (programMapTable.video !== null) {
43658                event.tracks.push({
43659                  timelineStartInfo: {
43660                    baseMediaDecodeTime: 0
43661                  },
43662                  id: +programMapTable.video,
43663                  codec: 'avc',
43664                  type: 'video'
43665                });
43666              }
43667
43668              if (programMapTable.audio !== null) {
43669                event.tracks.push({
43670                  timelineStartInfo: {
43671                    baseMediaDecodeTime: 0
43672                  },
43673                  id: +programMapTable.audio,
43674                  codec: 'adts',
43675                  type: 'audio'
43676                });
43677              }
43678
43679              self.trigger('data', event);
43680            }
43681          })[data.type]();
43682        };
43683
43684        this.reset = function () {
43685          video.size = 0;
43686          video.data.length = 0;
43687          audio.size = 0;
43688          audio.data.length = 0;
43689          this.trigger('reset');
43690        };
43691        /**
43692         * Flush any remaining input. Video PES packets may be of variable
43693         * length. Normally, the start of a new video packet can trigger the
43694         * finalization of the previous packet. That is not possible if no
43695         * more video is forthcoming, however. In that case, some other
43696         * mechanism (like the end of the file) has to be employed. When it is
43697         * clear that no additional data is forthcoming, calling this method
43698         * will flush the buffered packets.
43699         */
43700
43701
43702        this.flushStreams_ = function () {
43703          // !!THIS ORDER IS IMPORTANT!!
43704          // video first then audio
43705          flushStream(video, 'video');
43706          flushStream(audio, 'audio');
43707          flushStream(timedMetadata, 'timed-metadata');
43708        };
43709
43710        this.flush = function () {
43711          this.flushStreams_();
43712          this.trigger('done');
43713        };
43714      };
43715
43716      _ElementaryStream.prototype = new stream();
43717      var m2ts = {
43718        PAT_PID: 0x0000,
43719        MP2T_PACKET_LENGTH: MP2T_PACKET_LENGTH,
vendor: 6,108 bytes, lines 43720-43887
43720        TransportPacketStream: _TransportPacketStream,
43721        TransportParseStream: _TransportParseStream,
43722        ElementaryStream: _ElementaryStream,
43723        TimestampRolloverStream: TimestampRolloverStream$1,
43724        CaptionStream: captionStream.CaptionStream,
43725        Cea608Stream: captionStream.Cea608Stream,
43726        MetadataStream: metadataStream
43727      };
43728
43729      for (var type in streamTypes) {
43730        if (streamTypes.hasOwnProperty(type)) {
43731          m2ts[type] = streamTypes[type];
43732        }
43733      }
43734
43735      var m2ts_1 = m2ts;
43736      var ONE_SECOND_IN_TS$2 = clock.ONE_SECOND_IN_TS;
43737
43738      var _AdtsStream;
43739
43740      var ADTS_SAMPLING_FREQUENCIES = [96000, 88200, 64000, 48000, 44100, 32000, 24000, 22050, 16000, 12000, 11025, 8000, 7350];
43741      /*
43742       * Accepts a ElementaryStream and emits data events with parsed
43743       * AAC Audio Frames of the individual packets. Input audio in ADTS
43744       * format is unpacked and re-emitted as AAC frames.
43745       *
43746       * @see http://wiki.multimedia.cx/index.php?title=ADTS
43747       * @see http://wiki.multimedia.cx/?title=Understanding_AAC
43748       */
43749
43750      _AdtsStream = function AdtsStream(handlePartialSegments) {
43751        var buffer,
43752            frameNum = 0;
43753
43754        _AdtsStream.prototype.init.call(this);
43755
43756        this.push = function (packet) {
43757          var i = 0,
43758              frameLength,
43759              protectionSkipBytes,
43760              frameEnd,
43761              oldBuffer,
43762              sampleCount,
43763              adtsFrameDuration;
43764
43765          if (!handlePartialSegments) {
43766            frameNum = 0;
43767          }
43768
43769          if (packet.type !== 'audio') {
43770            // ignore non-audio data
43771            return;
43772          } // Prepend any data in the buffer to the input data so that we can parse
43773          // aac frames the cross a PES packet boundary
43774
43775
43776          if (buffer) {
43777            oldBuffer = buffer;
43778            buffer = new Uint8Array(oldBuffer.byteLength + packet.data.byteLength);
43779            buffer.set(oldBuffer);
43780            buffer.set(packet.data, oldBuffer.byteLength);
43781          } else {
43782            buffer = packet.data;
43783          } // unpack any ADTS frames which have been fully received
43784          // for details on the ADTS header, see http://wiki.multimedia.cx/index.php?title=ADTS
43785
43786
43787          while (i + 5 < buffer.length) {
43788            // Look for the start of an ADTS header..
43789            if (buffer[i] !== 0xFF || (buffer[i + 1] & 0xF6) !== 0xF0) {
43790              // If a valid header was not found,  jump one forward and attempt to
43791              // find a valid ADTS header starting at the next byte
43792              i++;
43793              continue;
43794            } // The protection skip bit tells us if we have 2 bytes of CRC data at the
43795            // end of the ADTS header
43796
43797
43798            protectionSkipBytes = (~buffer[i + 1] & 0x01) * 2; // Frame length is a 13 bit integer starting 16 bits from the
43799            // end of the sync sequence
43800
43801            frameLength = (buffer[i + 3] & 0x03) << 11 | buffer[i + 4] << 3 | (buffer[i + 5] & 0xe0) >> 5;
43802            sampleCount = ((buffer[i + 6] & 0x03) + 1) * 1024;
43803            adtsFrameDuration = sampleCount * ONE_SECOND_IN_TS$2 / ADTS_SAMPLING_FREQUENCIES[(buffer[i + 2] & 0x3c) >>> 2];
43804            frameEnd = i + frameLength; // If we don't have enough data to actually finish this ADTS frame, return
43805            // and wait for more data
43806
43807            if (buffer.byteLength < frameEnd) {
43808              return;
43809            } // Otherwise, deliver the complete AAC frame
43810
43811
43812            this.trigger('data', {
43813              pts: packet.pts + frameNum * adtsFrameDuration,
43814              dts: packet.dts + frameNum * adtsFrameDuration,
43815              sampleCount: sampleCount,
43816              audioobjecttype: (buffer[i + 2] >>> 6 & 0x03) + 1,
43817              channelcount: (buffer[i + 2] & 1) << 2 | (buffer[i + 3] & 0xc0) >>> 6,
43818              samplerate: ADTS_SAMPLING_FREQUENCIES[(buffer[i + 2] & 0x3c) >>> 2],
43819              samplingfrequencyindex: (buffer[i + 2] & 0x3c) >>> 2,
43820              // assume ISO/IEC 14496-12 AudioSampleEntry default of 16
43821              samplesize: 16,
43822              data: buffer.subarray(i + 7 + protectionSkipBytes, frameEnd)
43823            });
43824            frameNum++; // If the buffer is empty, clear it and return
43825
43826            if (buffer.byteLength === frameEnd) {
43827              buffer = undefined;
43828              return;
43829            } // Remove the finished frame from the buffer and start the process again
43830
43831
43832            buffer = buffer.subarray(frameEnd);
43833          }
43834        };
43835
43836        this.flush = function () {
43837          frameNum = 0;
43838          this.trigger('done');
43839        };
43840
43841        this.reset = function () {
43842          buffer = void 0;
43843          this.trigger('reset');
43844        };
43845
43846        this.endTimeline = function () {
43847          buffer = void 0;
43848          this.trigger('endedtimeline');
43849        };
43850      };
43851
43852      _AdtsStream.prototype = new stream();
43853      var adts = _AdtsStream;
43854      /**
43855       * mux.js
43856       *
43857       * Copyright (c) Brightcove
43858       * Licensed Apache-2.0 https://github.com/videojs/mux.js/blob/master/LICENSE
43859       */
43860
43861      var ExpGolomb;
43862      /**
43863       * Parser for exponential Golomb codes, a variable-bitwidth number encoding
43864       * scheme used by h264.
43865       */
43866
43867      ExpGolomb = function ExpGolomb(workingData) {
43868        var // the number of bytes left to examine in workingData
43869        workingBytesAvailable = workingData.byteLength,
43870            // the current word being examined
43871        workingWord = 0,
43872            // :uint
43873        // the number of bits left to examine in the current word
43874        workingBitsAvailable = 0; // :uint;
43875        // ():uint
43876
43877        this.length = function () {
43878          return 8 * workingBytesAvailable;
43879        }; // ():uint
43880
43881
43882        this.bitsAvailable = function () {
43883          return 8 * workingBytesAvailable + workingBitsAvailable;
43884        }; // ():void
43885
43886
43887        this.loadWord = function () {
vendor: 8,404 bytes, lines 43888-44165
43888          var position = workingData.byteLength - workingBytesAvailable,
43889              workingBytes = new Uint8Array(4),
43890              availableBytes = Math.min(4, workingBytesAvailable);
43891
43892          if (availableBytes === 0) {
43893            throw new Error('no bytes available');
43894          }
43895
43896          workingBytes.set(workingData.subarray(position, position + availableBytes));
43897          workingWord = new DataView(workingBytes.buffer).getUint32(0); // track the amount of workingData that has been processed
43898
43899          workingBitsAvailable = availableBytes * 8;
43900          workingBytesAvailable -= availableBytes;
43901        }; // (count:int):void
43902
43903
43904        this.skipBits = function (count) {
43905          var skipBytes; // :int
43906
43907          if (workingBitsAvailable > count) {
43908            workingWord <<= count;
43909            workingBitsAvailable -= count;
43910          } else {
43911            count -= workingBitsAvailable;
43912            skipBytes = Math.floor(count / 8);
43913            count -= skipBytes * 8;
43914            workingBytesAvailable -= skipBytes;
43915            this.loadWord();
43916            workingWord <<= count;
43917            workingBitsAvailable -= count;
43918          }
43919        }; // (size:int):uint
43920
43921
43922        this.readBits = function (size) {
43923          var bits = Math.min(workingBitsAvailable, size),
43924              // :uint
43925          valu = workingWord >>> 32 - bits; // :uint
43926          // if size > 31, handle error
43927
43928          workingBitsAvailable -= bits;
43929
43930          if (workingBitsAvailable > 0) {
43931            workingWord <<= bits;
43932          } else if (workingBytesAvailable > 0) {
43933            this.loadWord();
43934          }
43935
43936          bits = size - bits;
43937
43938          if (bits > 0) {
43939            return valu << bits | this.readBits(bits);
43940          }
43941
43942          return valu;
43943        }; // ():uint
43944
43945
43946        this.skipLeadingZeros = function () {
43947          var leadingZeroCount; // :uint
43948
43949          for (leadingZeroCount = 0; leadingZeroCount < workingBitsAvailable; ++leadingZeroCount) {
43950            if ((workingWord & 0x80000000 >>> leadingZeroCount) !== 0) {
43951              // the first bit of working word is 1
43952              workingWord <<= leadingZeroCount;
43953              workingBitsAvailable -= leadingZeroCount;
43954              return leadingZeroCount;
43955            }
43956          } // we exhausted workingWord and still have not found a 1
43957
43958
43959          this.loadWord();
43960          return leadingZeroCount + this.skipLeadingZeros();
43961        }; // ():void
43962
43963
43964        this.skipUnsignedExpGolomb = function () {
43965          this.skipBits(1 + this.skipLeadingZeros());
43966        }; // ():void
43967
43968
43969        this.skipExpGolomb = function () {
43970          this.skipBits(1 + this.skipLeadingZeros());
43971        }; // ():uint
43972
43973
43974        this.readUnsignedExpGolomb = function () {
43975          var clz = this.skipLeadingZeros(); // :uint
43976
43977          return this.readBits(clz + 1) - 1;
43978        }; // ():int
43979
43980
43981        this.readExpGolomb = function () {
43982          var valu = this.readUnsignedExpGolomb(); // :int
43983
43984          if (0x01 & valu) {
43985            // the number is odd if the low order bit is set
43986            return 1 + valu >>> 1; // add 1 to make it even, and divide by 2
43987          }
43988
43989          return -1 * (valu >>> 1); // divide by two then make it negative
43990        }; // Some convenience functions
43991        // :Boolean
43992
43993
43994        this.readBoolean = function () {
43995          return this.readBits(1) === 1;
43996        }; // ():int
43997
43998
43999        this.readUnsignedByte = function () {
44000          return this.readBits(8);
44001        };
44002
44003        this.loadWord();
44004      };
44005
44006      var expGolomb = ExpGolomb;
44007
44008      var _H264Stream, _NalByteStream;
44009
44010      var PROFILES_WITH_OPTIONAL_SPS_DATA;
44011      /**
44012       * Accepts a NAL unit byte stream and unpacks the embedded NAL units.
44013       */
44014
44015      _NalByteStream = function NalByteStream() {
44016        var syncPoint = 0,
44017            i,
44018            buffer;
44019
44020        _NalByteStream.prototype.init.call(this);
44021        /*
44022         * Scans a byte stream and triggers a data event with the NAL units found.
44023         * @param {Object} data Event received from H264Stream
44024         * @param {Uint8Array} data.data The h264 byte stream to be scanned
44025         *
44026         * @see H264Stream.push
44027         */
44028
44029
44030        this.push = function (data) {
44031          var swapBuffer;
44032
44033          if (!buffer) {
44034            buffer = data.data;
44035          } else {
44036            swapBuffer = new Uint8Array(buffer.byteLength + data.data.byteLength);
44037            swapBuffer.set(buffer);
44038            swapBuffer.set(data.data, buffer.byteLength);
44039            buffer = swapBuffer;
44040          }
44041
44042          var len = buffer.byteLength; // Rec. ITU-T H.264, Annex B
44043          // scan for NAL unit boundaries
44044          // a match looks like this:
44045          // 0 0 1 .. NAL .. 0 0 1
44046          // ^ sync point        ^ i
44047          // or this:
44048          // 0 0 1 .. NAL .. 0 0 0
44049          // ^ sync point        ^ i
44050          // advance the sync point to a NAL start, if necessary
44051
44052          for (; syncPoint < len - 3; syncPoint++) {
44053            if (buffer[syncPoint + 2] === 1) {
44054              // the sync point is properly aligned
44055              i = syncPoint + 5;
44056              break;
44057            }
44058          }
44059
44060          while (i < len) {
44061            // look at the current byte to determine if we've hit the end of
44062            // a NAL unit boundary
44063            switch (buffer[i]) {
44064              case 0:
44065                // skip past non-sync sequences
44066                if (buffer[i - 1] !== 0) {
44067                  i += 2;
44068                  break;
44069                } else if (buffer[i - 2] !== 0) {
44070                  i++;
44071                  break;
44072                } // deliver the NAL unit if it isn't empty
44073
44074
44075                if (syncPoint + 3 !== i - 2) {
44076                  this.trigger('data', buffer.subarray(syncPoint + 3, i - 2));
44077                } // drop trailing zeroes
44078
44079
44080                do {
44081                  i++;
44082                } while (buffer[i] !== 1 && i < len);
44083
44084                syncPoint = i - 2;
44085                i += 3;
44086                break;
44087
44088              case 1:
44089                // skip past non-sync sequences
44090                if (buffer[i - 1] !== 0 || buffer[i - 2] !== 0) {
44091                  i += 3;
44092                  break;
44093                } // deliver the NAL unit
44094
44095
44096                this.trigger('data', buffer.subarray(syncPoint + 3, i - 2));
44097                syncPoint = i - 2;
44098                i += 3;
44099                break;
44100
44101              default:
44102                // the current byte isn't a one or zero, so it cannot be part
44103                // of a sync sequence
44104                i += 3;
44105                break;
44106            }
44107          } // filter out the NAL units that were delivered
44108
44109
44110          buffer = buffer.subarray(syncPoint);
44111          i -= syncPoint;
44112          syncPoint = 0;
44113        };
44114
44115        this.reset = function () {
44116          buffer = null;
44117          syncPoint = 0;
44118          this.trigger('reset');
44119        };
44120
44121        this.flush = function () {
44122          // deliver the last buffered NAL unit
44123          if (buffer && buffer.byteLength > 3) {
44124            this.trigger('data', buffer.subarray(syncPoint + 3));
44125          } // reset the stream state
44126
44127
44128          buffer = null;
44129          syncPoint = 0;
44130          this.trigger('done');
44131        };
44132
44133        this.endTimeline = function () {
44134          this.flush();
44135          this.trigger('endedtimeline');
44136        };
44137      };
44138
44139      _NalByteStream.prototype = new stream(); // values of profile_idc that indicate additional fields are included in the SPS
44140      // see Recommendation ITU-T H.264 (4/2013),
44141      // 7.3.2.1.1 Sequence parameter set data syntax
44142
44143      PROFILES_WITH_OPTIONAL_SPS_DATA = {
44144        100: true,
44145        110: true,
44146        122: true,
44147        244: true,
44148        44: true,
44149        83: true,
44150        86: true,
44151        118: true,
44152        128: true,
44153        138: true,
44154        139: true,
44155        134: true
44156      };
44157      /**
44158       * Accepts input from a ElementaryStream and produces H.264 NAL unit data
44159       * events.
44160       */
44161
44162      _H264Stream = function H264Stream() {
44163        var nalByteStream = new _NalByteStream(),
44164            self,
44165            trackId,
44166            currentPts,
44167            currentDts,
44168            discardEmulationPreventionBytes,
44169            readSequenceParameterSet,
44170            skipScalingList;
44171
44172        _H264Stream.prototype.init.call(this);
44173
44174        self = this;
44175        /*
44176         * Pushes a packet from a stream onto the NalByteStream
44177         *
44178         * @param {Object} packet - A packet received from a stream
44179         * @param {Uint8Array} packet.data - The raw bytes of the packet
44180         * @param {Number} packet.dts - Decode timestamp of the packet
44181         * @param {Number} packet.pts - Presentation timestamp of the packet
44182         * @param {Number} packet.trackId - The id of the h264 track this packet came from
44183         * @param {('video'|'audio')} packet.type - The type of packet
44184         *
44185         */
44186
44187        this.push = function (packet) {
44188          if (packet.type !== 'video') {
44189            return;
44190          }
44191
44192          trackId = packet.trackId;
44193          currentPts = packet.pts;
44194          currentDts = packet.dts;
44195          nalByteStream.push(packet);
44196        };
44197        /*
44198         * Identify NAL unit types and pass on the NALU, trackId, presentation and decode timestamps
44199         * for the NALUs to the next stream component.
44200         * Also, preprocess caption and sequence parameter NALUs.
44201         *
44202         * @param {Uint8Array} data - A NAL unit identified by `NalByteStream.push`
44203         * @see NalByteStream.push
44204         */
44205
44206
44207        nalByteStream.on('data', function (data) {
44208          var event = {
44209            trackId: trackId,
vendor: 16,384 bytes, lines 44210-44680
44210            pts: currentPts,
44211            dts: currentDts,
44212            data: data
44213          };
44214
44215          switch (data[0] & 0x1f) {
44216            case 0x05:
44217              event.nalUnitType = 'slice_layer_without_partitioning_rbsp_idr';
44218              break;
44219
44220            case 0x06:
44221              event.nalUnitType = 'sei_rbsp';
44222              event.escapedRBSP = discardEmulationPreventionBytes(data.subarray(1));
44223              break;
44224
44225            case 0x07:
44226              event.nalUnitType = 'seq_parameter_set_rbsp';
44227              event.escapedRBSP = discardEmulationPreventionBytes(data.subarray(1));
44228              event.config = readSequenceParameterSet(event.escapedRBSP);
44229              break;
44230
44231            case 0x08:
44232              event.nalUnitType = 'pic_parameter_set_rbsp';
44233              break;
44234
44235            case 0x09:
44236              event.nalUnitType = 'access_unit_delimiter_rbsp';
44237              break;
44238
44239            default:
44240              break;
44241          } // This triggers data on the H264Stream
44242
44243
44244          self.trigger('data', event);
44245        });
44246        nalByteStream.on('done', function () {
44247          self.trigger('done');
44248        });
44249        nalByteStream.on('partialdone', function () {
44250          self.trigger('partialdone');
44251        });
44252        nalByteStream.on('reset', function () {
44253          self.trigger('reset');
44254        });
44255        nalByteStream.on('endedtimeline', function () {
44256          self.trigger('endedtimeline');
44257        });
44258
44259        this.flush = function () {
44260          nalByteStream.flush();
44261        };
44262
44263        this.partialFlush = function () {
44264          nalByteStream.partialFlush();
44265        };
44266
44267        this.reset = function () {
44268          nalByteStream.reset();
44269        };
44270
44271        this.endTimeline = function () {
44272          nalByteStream.endTimeline();
44273        };
44274        /**
44275         * Advance the ExpGolomb decoder past a scaling list. The scaling
44276         * list is optionally transmitted as part of a sequence parameter
44277         * set and is not relevant to transmuxing.
44278         * @param count {number} the number of entries in this scaling list
44279         * @param expGolombDecoder {object} an ExpGolomb pointed to the
44280         * start of a scaling list
44281         * @see Recommendation ITU-T H.264, Section 7.3.2.1.1.1
44282         */
44283
44284
44285        skipScalingList = function skipScalingList(count, expGolombDecoder) {
44286          var lastScale = 8,
44287              nextScale = 8,
44288              j,
44289              deltaScale;
44290
44291          for (j = 0; j < count; j++) {
44292            if (nextScale !== 0) {
44293              deltaScale = expGolombDecoder.readExpGolomb();
44294              nextScale = (lastScale + deltaScale + 256) % 256;
44295            }
44296
44297            lastScale = nextScale === 0 ? lastScale : nextScale;
44298          }
44299        };
44300        /**
44301         * Expunge any "Emulation Prevention" bytes from a "Raw Byte
44302         * Sequence Payload"
44303         * @param data {Uint8Array} the bytes of a RBSP from a NAL
44304         * unit
44305         * @return {Uint8Array} the RBSP without any Emulation
44306         * Prevention Bytes
44307         */
44308
44309
44310        discardEmulationPreventionBytes = function discardEmulationPreventionBytes(data) {
44311          var length = data.byteLength,
44312              emulationPreventionBytesPositions = [],
44313              i = 1,
44314              newLength,
44315              newData; // Find all `Emulation Prevention Bytes`
44316
44317          while (i < length - 2) {
44318            if (data[i] === 0 && data[i + 1] === 0 && data[i + 2] === 0x03) {
44319              emulationPreventionBytesPositions.push(i + 2);
44320              i += 2;
44321            } else {
44322              i++;
44323            }
44324          } // If no Emulation Prevention Bytes were found just return the original
44325          // array
44326
44327
44328          if (emulationPreventionBytesPositions.length === 0) {
44329            return data;
44330          } // Create a new array to hold the NAL unit data
44331
44332
44333          newLength = length - emulationPreventionBytesPositions.length;
44334          newData = new Uint8Array(newLength);
44335          var sourceIndex = 0;
44336
44337          for (i = 0; i < newLength; sourceIndex++, i++) {
44338            if (sourceIndex === emulationPreventionBytesPositions[0]) {
44339              // Skip this byte
44340              sourceIndex++; // Remove this position index
44341
44342              emulationPreventionBytesPositions.shift();
44343            }
44344
44345            newData[i] = data[sourceIndex];
44346          }
44347
44348          return newData;
44349        };
44350        /**
44351         * Read a sequence parameter set and return some interesting video
44352         * properties. A sequence parameter set is the H264 metadata that
44353         * describes the properties of upcoming video frames.
44354         * @param data {Uint8Array} the bytes of a sequence parameter set
44355         * @return {object} an object with configuration parsed from the
44356         * sequence parameter set, including the dimensions of the
44357         * associated video frames.
44358         */
44359
44360
44361        readSequenceParameterSet = function readSequenceParameterSet(data) {
44362          var frameCropLeftOffset = 0,
44363              frameCropRightOffset = 0,
44364              frameCropTopOffset = 0,
44365              frameCropBottomOffset = 0,
44366              sarScale = 1,
44367              expGolombDecoder,
44368              profileIdc,
44369              levelIdc,
44370              profileCompatibility,
44371              chromaFormatIdc,
44372              picOrderCntType,
44373              numRefFramesInPicOrderCntCycle,
44374              picWidthInMbsMinus1,
44375              picHeightInMapUnitsMinus1,
44376              frameMbsOnlyFlag,
44377              scalingListCount,
44378              sarRatio,
44379              aspectRatioIdc,
44380              i;
44381          expGolombDecoder = new expGolomb(data);
44382          profileIdc = expGolombDecoder.readUnsignedByte(); // profile_idc
44383
44384          profileCompatibility = expGolombDecoder.readUnsignedByte(); // constraint_set[0-5]_flag
44385
44386          levelIdc = expGolombDecoder.readUnsignedByte(); // level_idc u(8)
44387
44388          expGolombDecoder.skipUnsignedExpGolomb(); // seq_parameter_set_id
44389          // some profiles have more optional data we don't need
44390
44391          if (PROFILES_WITH_OPTIONAL_SPS_DATA[profileIdc]) {
44392            chromaFormatIdc = expGolombDecoder.readUnsignedExpGolomb();
44393
44394            if (chromaFormatIdc === 3) {
44395              expGolombDecoder.skipBits(1); // separate_colour_plane_flag
44396            }
44397
44398            expGolombDecoder.skipUnsignedExpGolomb(); // bit_depth_luma_minus8
44399
44400            expGolombDecoder.skipUnsignedExpGolomb(); // bit_depth_chroma_minus8
44401
44402            expGolombDecoder.skipBits(1); // qpprime_y_zero_transform_bypass_flag
44403
44404            if (expGolombDecoder.readBoolean()) {
44405              // seq_scaling_matrix_present_flag
44406              scalingListCount = chromaFormatIdc !== 3 ? 8 : 12;
44407
44408              for (i = 0; i < scalingListCount; i++) {
44409                if (expGolombDecoder.readBoolean()) {
44410                  // seq_scaling_list_present_flag[ i ]
44411                  if (i < 6) {
44412                    skipScalingList(16, expGolombDecoder);
44413                  } else {
44414                    skipScalingList(64, expGolombDecoder);
44415                  }
44416                }
44417              }
44418            }
44419          }
44420
44421          expGolombDecoder.skipUnsignedExpGolomb(); // log2_max_frame_num_minus4
44422
44423          picOrderCntType = expGolombDecoder.readUnsignedExpGolomb();
44424
44425          if (picOrderCntType === 0) {
44426            expGolombDecoder.readUnsignedExpGolomb(); // log2_max_pic_order_cnt_lsb_minus4
44427          } else if (picOrderCntType === 1) {
44428            expGolombDecoder.skipBits(1); // delta_pic_order_always_zero_flag
44429
44430            expGolombDecoder.skipExpGolomb(); // offset_for_non_ref_pic
44431
44432            expGolombDecoder.skipExpGolomb(); // offset_for_top_to_bottom_field
44433
44434            numRefFramesInPicOrderCntCycle = expGolombDecoder.readUnsignedExpGolomb();
44435
44436            for (i = 0; i < numRefFramesInPicOrderCntCycle; i++) {
44437              expGolombDecoder.skipExpGolomb(); // offset_for_ref_frame[ i ]
44438            }
44439          }
44440
44441          expGolombDecoder.skipUnsignedExpGolomb(); // max_num_ref_frames
44442
44443          expGolombDecoder.skipBits(1); // gaps_in_frame_num_value_allowed_flag
44444
44445          picWidthInMbsMinus1 = expGolombDecoder.readUnsignedExpGolomb();
44446          picHeightInMapUnitsMinus1 = expGolombDecoder.readUnsignedExpGolomb();
44447          frameMbsOnlyFlag = expGolombDecoder.readBits(1);
44448
44449          if (frameMbsOnlyFlag === 0) {
44450            expGolombDecoder.skipBits(1); // mb_adaptive_frame_field_flag
44451          }
44452
44453          expGolombDecoder.skipBits(1); // direct_8x8_inference_flag
44454
44455          if (expGolombDecoder.readBoolean()) {
44456            // frame_cropping_flag
44457            frameCropLeftOffset = expGolombDecoder.readUnsignedExpGolomb();
44458            frameCropRightOffset = expGolombDecoder.readUnsignedExpGolomb();
44459            frameCropTopOffset = expGolombDecoder.readUnsignedExpGolomb();
44460            frameCropBottomOffset = expGolombDecoder.readUnsignedExpGolomb();
44461          }
44462
44463          if (expGolombDecoder.readBoolean()) {
44464            // vui_parameters_present_flag
44465            if (expGolombDecoder.readBoolean()) {
44466              // aspect_ratio_info_present_flag
44467              aspectRatioIdc = expGolombDecoder.readUnsignedByte();
44468
44469              switch (aspectRatioIdc) {
44470                case 1:
44471                  sarRatio = [1, 1];
44472                  break;
44473
44474                case 2:
44475                  sarRatio = [12, 11];
44476                  break;
44477
44478                case 3:
44479                  sarRatio = [10, 11];
44480                  break;
44481
44482                case 4:
44483                  sarRatio = [16, 11];
44484                  break;
44485
44486                case 5:
44487                  sarRatio = [40, 33];
44488                  break;
44489
44490                case 6:
44491                  sarRatio = [24, 11];
44492                  break;
44493
44494                case 7:
44495                  sarRatio = [20, 11];
44496                  break;
44497
44498                case 8:
44499                  sarRatio = [32, 11];
44500                  break;
44501
44502                case 9:
44503                  sarRatio = [80, 33];
44504                  break;
44505
44506                case 10:
44507                  sarRatio = [18, 11];
44508                  break;
44509
44510                case 11:
44511                  sarRatio = [15, 11];
44512                  break;
44513
44514                case 12:
44515                  sarRatio = [64, 33];
44516                  break;
44517
44518                case 13:
44519                  sarRatio = [160, 99];
44520                  break;
44521
44522                case 14:
44523                  sarRatio = [4, 3];
44524                  break;
44525
44526                case 15:
44527                  sarRatio = [3, 2];
44528                  break;
44529
44530                case 16:
44531                  sarRatio = [2, 1];
44532                  break;
44533
44534                case 255:
44535                  {
44536                    sarRatio = [expGolombDecoder.readUnsignedByte() << 8 | expGolombDecoder.readUnsignedByte(), expGolombDecoder.readUnsignedByte() << 8 | expGolombDecoder.readUnsignedByte()];
44537                    break;
44538                  }
44539              }
44540
44541              if (sarRatio) {
44542                sarScale = sarRatio[0] / sarRatio[1];
44543              }
44544            }
44545          }
44546
44547          return {
44548            profileIdc: profileIdc,
44549            levelIdc: levelIdc,
44550            profileCompatibility: profileCompatibility,
44551            width: Math.ceil(((picWidthInMbsMinus1 + 1) * 16 - frameCropLeftOffset * 2 - frameCropRightOffset * 2) * sarScale),
44552            height: (2 - frameMbsOnlyFlag) * (picHeightInMapUnitsMinus1 + 1) * 16 - frameCropTopOffset * 2 - frameCropBottomOffset * 2,
44553            sarRatio: sarRatio
44554          };
44555        };
44556      };
44557
44558      _H264Stream.prototype = new stream();
44559      var h264 = {
44560        H264Stream: _H264Stream,
44561        NalByteStream: _NalByteStream
44562      };
44563      /**
44564       * mux.js
44565       *
44566       * Copyright (c) Brightcove
44567       * Licensed Apache-2.0 https://github.com/videojs/mux.js/blob/master/LICENSE
44568       *
44569       * Utilities to detect basic properties and metadata about Aac data.
44570       */
44571
44572      var ADTS_SAMPLING_FREQUENCIES$1 = [96000, 88200, 64000, 48000, 44100, 32000, 24000, 22050, 16000, 12000, 11025, 8000, 7350];
44573
44574      var isLikelyAacData = function isLikelyAacData(data) {
44575        if (data[0] === 'I'.charCodeAt(0) && data[1] === 'D'.charCodeAt(0) && data[2] === '3'.charCodeAt(0)) {
44576          return true;
44577        }
44578
44579        return false;
44580      };
44581
44582      var parseSyncSafeInteger$1 = function parseSyncSafeInteger(data) {
44583        return data[0] << 21 | data[1] << 14 | data[2] << 7 | data[3];
44584      }; // return a percent-encoded representation of the specified byte range
44585      // @see http://en.wikipedia.org/wiki/Percent-encoding
44586
44587
44588      var percentEncode$1 = function percentEncode(bytes, start, end) {
44589        var i,
44590            result = '';
44591
44592        for (i = start; i < end; i++) {
44593          result += '%' + ('00' + bytes[i].toString(16)).slice(-2);
44594        }
44595
44596        return result;
44597      }; // return the string representation of the specified byte range,
44598      // interpreted as ISO-8859-1.
44599
44600
44601      var parseIso88591$1 = function parseIso88591(bytes, start, end) {
44602        return unescape(percentEncode$1(bytes, start, end)); // jshint ignore:line
44603      };
44604
44605      var parseId3TagSize = function parseId3TagSize(header, byteIndex) {
44606        var returnSize = header[byteIndex + 6] << 21 | header[byteIndex + 7] << 14 | header[byteIndex + 8] << 7 | header[byteIndex + 9],
44607            flags = header[byteIndex + 5],
44608            footerPresent = (flags & 16) >> 4;
44609
44610        if (footerPresent) {
44611          return returnSize + 20;
44612        }
44613
44614        return returnSize + 10;
44615      };
44616
44617      var parseAdtsSize = function parseAdtsSize(header, byteIndex) {
44618        var lowThree = (header[byteIndex + 5] & 0xE0) >> 5,
44619            middle = header[byteIndex + 4] << 3,
44620            highTwo = header[byteIndex + 3] & 0x3 << 11;
44621        return highTwo | middle | lowThree;
44622      };
44623
44624      var parseType = function parseType(header, byteIndex) {
44625        if (header[byteIndex] === 'I'.charCodeAt(0) && header[byteIndex + 1] === 'D'.charCodeAt(0) && header[byteIndex + 2] === '3'.charCodeAt(0)) {
44626          return 'timed-metadata';
44627        } else if (header[byteIndex] & 0xff === 0xff && (header[byteIndex + 1] & 0xf0) === 0xf0) {
44628          return 'audio';
44629        }
44630
44631        return null;
44632      };
44633
44634      var parseSampleRate = function parseSampleRate(packet) {
44635        var i = 0;
44636
44637        while (i + 5 < packet.length) {
44638          if (packet[i] !== 0xFF || (packet[i + 1] & 0xF6) !== 0xF0) {
44639            // If a valid header was not found,  jump one forward and attempt to
44640            // find a valid ADTS header starting at the next byte
44641            i++;
44642            continue;
44643          }
44644
44645          return ADTS_SAMPLING_FREQUENCIES$1[(packet[i + 2] & 0x3c) >>> 2];
44646        }
44647
44648        return null;
44649      };
44650
44651      var parseAacTimestamp = function parseAacTimestamp(packet) {
44652        var frameStart, frameSize, frame, frameHeader; // find the start of the first frame and the end of the tag
44653
44654        frameStart = 10;
44655
44656        if (packet[5] & 0x40) {
44657          // advance the frame start past the extended header
44658          frameStart += 4; // header size field
44659
44660          frameStart += parseSyncSafeInteger$1(packet.subarray(10, 14));
44661        } // parse one or more ID3 frames
44662        // http://id3.org/id3v2.3.0#ID3v2_frame_overview
44663
44664
44665        do {
44666          // determine the number of bytes in this frame
44667          frameSize = parseSyncSafeInteger$1(packet.subarray(frameStart + 4, frameStart + 8));
44668
44669          if (frameSize < 1) {
44670            return null;
44671          }
44672
44673          frameHeader = String.fromCharCode(packet[frameStart], packet[frameStart + 1], packet[frameStart + 2], packet[frameStart + 3]);
44674
44675          if (frameHeader === 'PRIV') {
44676            frame = packet.subarray(frameStart + 10, frameStart + frameSize + 10);
44677
44678            for (var i = 0; i < frame.byteLength; i++) {
44679              if (frame[i] === 0) {
44680                var owner = parseIso88591$1(frame, 
vendor: 5,833 bytes, lines 44680-44852
446800, i);
44681
44682                if (owner === 'com.apple.streaming.transportStreamTimestamp') {
44683                  var d = frame.subarray(i + 1);
44684                  var size = (d[3] & 0x01) << 30 | d[4] << 22 | d[5] << 14 | d[6] << 6 | d[7] >>> 2;
44685                  size *= 4;
44686                  size += d[7] & 0x03;
44687                  return size;
44688                }
44689
44690                break;
44691              }
44692            }
44693          }
44694
44695          frameStart += 10; // advance past the frame header
44696
44697          frameStart += frameSize; // advance past the frame body
44698        } while (frameStart < packet.byteLength);
44699
44700        return null;
44701      };
44702
44703      var utils = {
44704        isLikelyAacData: isLikelyAacData,
44705        parseId3TagSize: parseId3TagSize,
44706        parseAdtsSize: parseAdtsSize,
44707        parseType: parseType,
44708        parseSampleRate: parseSampleRate,
44709        parseAacTimestamp: parseAacTimestamp
44710      }; // Constants
44711
44712      var _AacStream;
44713      /**
44714       * Splits an incoming stream of binary data into ADTS and ID3 Frames.
44715       */
44716
44717
44718      _AacStream = function AacStream() {
44719        var everything = new Uint8Array(),
44720            timeStamp = 0;
44721
44722        _AacStream.prototype.init.call(this);
44723
44724        this.setTimestamp = function (timestamp) {
44725          timeStamp = timestamp;
44726        };
44727
44728        this.push = function (bytes) {
44729          var frameSize = 0,
44730              byteIndex = 0,
44731              bytesLeft,
44732              chunk,
44733              packet,
44734              tempLength; // If there are bytes remaining from the last segment, prepend them to the
44735          // bytes that were pushed in
44736
44737          if (everything.length) {
44738            tempLength = everything.length;
44739            everything = new Uint8Array(bytes.byteLength + tempLength);
44740            everything.set(everything.subarray(0, tempLength));
44741            everything.set(bytes, tempLength);
44742          } else {
44743            everything = bytes;
44744          }
44745
44746          while (everything.length - byteIndex >= 3) {
44747            if (everything[byteIndex] === 'I'.charCodeAt(0) && everything[byteIndex + 1] === 'D'.charCodeAt(0) && everything[byteIndex + 2] === '3'.charCodeAt(0)) {
44748              // Exit early because we don't have enough to parse
44749              // the ID3 tag header
44750              if (everything.length - byteIndex < 10) {
44751                break;
44752              } // check framesize
44753
44754
44755              frameSize = utils.parseId3TagSize(everything, byteIndex); // Exit early if we don't have enough in the buffer
44756              // to emit a full packet
44757              // Add to byteIndex to support multiple ID3 tags in sequence
44758
44759              if (byteIndex + frameSize > everything.length) {
44760                break;
44761              }
44762
44763              chunk = {
44764                type: 'timed-metadata',
44765                data: everything.subarray(byteIndex, byteIndex + frameSize)
44766              };
44767              this.trigger('data', chunk);
44768              byteIndex += frameSize;
44769              continue;
44770            } else if ((everything[byteIndex] & 0xff) === 0xff && (everything[byteIndex + 1] & 0xf0) === 0xf0) {
44771              // Exit early because we don't have enough to parse
44772              // the ADTS frame header
44773              if (everything.length - byteIndex < 7) {
44774                break;
44775              }
44776
44777              frameSize = utils.parseAdtsSize(everything, byteIndex); // Exit early if we don't have enough in the buffer
44778              // to emit a full packet
44779
44780              if (byteIndex + frameSize > everything.length) {
44781                break;
44782              }
44783
44784              packet = {
44785                type: 'audio',
44786                data: everything.subarray(byteIndex, byteIndex + frameSize),
44787                pts: timeStamp,
44788                dts: timeStamp
44789              };
44790              this.trigger('data', packet);
44791              byteIndex += frameSize;
44792              continue;
44793            }
44794
44795            byteIndex++;
44796          }
44797
44798          bytesLeft = everything.length - byteIndex;
44799
44800          if (bytesLeft > 0) {
44801            everything = everything.subarray(byteIndex);
44802          } else {
44803            everything = new Uint8Array();
44804          }
44805        };
44806
44807        this.reset = function () {
44808          everything = new Uint8Array();
44809          this.trigger('reset');
44810        };
44811
44812        this.endTimeline = function () {
44813          everything = new Uint8Array();
44814          this.trigger('endedtimeline');
44815        };
44816      };
44817
44818      _AacStream.prototype = new stream();
44819      var aac = _AacStream;
44820      var H264Stream = h264.H264Stream;
44821      var isLikelyAacData$1 = utils.isLikelyAacData;
44822      var ONE_SECOND_IN_TS$3 = clock.ONE_SECOND_IN_TS; // constants
44823
44824      var AUDIO_PROPERTIES = ['audioobjecttype', 'channelcount', 'samplerate', 'samplingfrequencyindex', 'samplesize'];
44825      var VIDEO_PROPERTIES = ['width', 'height', 'profileIdc', 'levelIdc', 'profileCompatibility', 'sarRatio']; // object types
44826
44827      var _VideoSegmentStream, _AudioSegmentStream, _Transmuxer, _CoalesceStream;
44828      /**
44829       * Compare two arrays (even typed) for same-ness
44830       */
44831
44832
44833      var arrayEquals = function arrayEquals(a, b) {
44834        var i;
44835
44836        if (a.length !== b.length) {
44837          return false;
44838        } // compare the value of each element in the array
44839
44840
44841        for (i = 0; i < a.length; i++) {
44842          if (a[i] !== b[i]) {
44843            return false;
44844          }
44845        }
44846
44847        return true;
44848      };
44849
44850      var generateVideoSegmentTimingInfo = function generateVideoSegmentTimingInfo(baseMediaDecodeTime, startDts, startPts, endDts, endPts, prependedContentDuration) {
44851        var ptsOffsetFromDts = startPts - startDts,
44852            decodeDuration = endDts - startDts,
vendor: 6,058 bytes, lines 44853-44995
44853            presentationDuration = endPts - startPts; // The PTS and DTS values are based on the actual stream times from the segment,
44854        // however, the player time values will reflect a start from the baseMediaDecodeTime.
44855        // In order to provide relevant values for the player times, base timing info on the
44856        // baseMediaDecodeTime and the DTS and PTS durations of the segment.
44857
44858        return {
44859          start: {
44860            dts: baseMediaDecodeTime,
44861            pts: baseMediaDecodeTime + ptsOffsetFromDts
44862          },
44863          end: {
44864            dts: baseMediaDecodeTime + decodeDuration,
44865            pts: baseMediaDecodeTime + presentationDuration
44866          },
44867          prependedContentDuration: prependedContentDuration,
44868          baseMediaDecodeTime: baseMediaDecodeTime
44869        };
44870      };
44871      /**
44872       * Constructs a single-track, ISO BMFF media segment from AAC data
44873       * events. The output of this stream can be fed to a SourceBuffer
44874       * configured with a suitable initialization segment.
44875       * @param track {object} track metadata configuration
44876       * @param options {object} transmuxer options object
44877       * @param options.keepOriginalTimestamps {boolean} If true, keep the timestamps
44878       *        in the source; false to adjust the first segment to start at 0.
44879       */
44880
44881
44882      _AudioSegmentStream = function AudioSegmentStream(track, options) {
44883        var adtsFrames = [],
44884            sequenceNumber = 0,
44885            earliestAllowedDts = 0,
44886            audioAppendStartTs = 0,
44887            videoBaseMediaDecodeTime = Infinity;
44888        options = options || {};
44889
44890        _AudioSegmentStream.prototype.init.call(this);
44891
44892        this.push = function (data) {
44893          trackDecodeInfo.collectDtsInfo(track, data);
44894
44895          if (track) {
44896            AUDIO_PROPERTIES.forEach(function (prop) {
44897              track[prop] = data[prop];
44898            });
44899          } // buffer audio data until end() is called
44900
44901
44902          adtsFrames.push(data);
44903        };
44904
44905        this.setEarliestDts = function (earliestDts) {
44906          earliestAllowedDts = earliestDts - track.timelineStartInfo.baseMediaDecodeTime;
44907        };
44908
44909        this.setVideoBaseMediaDecodeTime = function (baseMediaDecodeTime) {
44910          videoBaseMediaDecodeTime = baseMediaDecodeTime;
44911        };
44912
44913        this.setAudioAppendStart = function (timestamp) {
44914          audioAppendStartTs = timestamp;
44915        };
44916
44917        this.flush = function () {
44918          var frames, moof, mdat, boxes, frameDuration; // return early if no audio data has been observed
44919
44920          if (adtsFrames.length === 0) {
44921            this.trigger('done', 'AudioSegmentStream');
44922            return;
44923          }
44924
44925          frames = audioFrameUtils.trimAdtsFramesByEarliestDts(adtsFrames, track, earliestAllowedDts);
44926          track.baseMediaDecodeTime = trackDecodeInfo.calculateTrackBaseMediaDecodeTime(track, options.keepOriginalTimestamps);
44927          audioFrameUtils.prefixWithSilence(track, frames, audioAppendStartTs, videoBaseMediaDecodeTime); // we have to build the index from byte locations to
44928          // samples (that is, adts frames) in the audio data
44929
44930          track.samples = audioFrameUtils.generateSampleTable(frames); // concatenate the audio data to constuct the mdat
44931
44932          mdat = mp4Generator.mdat(audioFrameUtils.concatenateFrameData(frames));
44933          adtsFrames = [];
44934          moof = mp4Generator.moof(sequenceNumber, [track]);
44935          boxes = new Uint8Array(moof.byteLength + mdat.byteLength); // bump the sequence number for next time
44936
44937          sequenceNumber++;
44938          boxes.set(moof);
44939          boxes.set(mdat, moof.byteLength);
44940          trackDecodeInfo.clearDtsInfo(track);
44941          frameDuration = Math.ceil(ONE_SECOND_IN_TS$3 * 1024 / track.samplerate); // TODO this check was added to maintain backwards compatibility (particularly with
44942          // tests) on adding the timingInfo event. However, it seems unlikely that there's a
44943          // valid use-case where an init segment/data should be triggered without associated
44944          // frames. Leaving for now, but should be looked into.
44945
44946          if (frames.length) {
44947            this.trigger('timingInfo', {
44948              start: frames[0].dts,
44949              end: frames[0].dts + frames.length * frameDuration
44950            });
44951          }
44952
44953          this.trigger('data', {
44954            track: track,
44955            boxes: boxes
44956          });
44957          this.trigger('done', 'AudioSegmentStream');
44958        };
44959
44960        this.reset = function () {
44961          trackDecodeInfo.clearDtsInfo(track);
44962          adtsFrames = [];
44963          this.trigger('reset');
44964        };
44965      };
44966
44967      _AudioSegmentStream.prototype = new stream();
44968      /**
44969       * Constructs a single-track, ISO BMFF media segment from H264 data
44970       * events. The output of this stream can be fed to a SourceBuffer
44971       * configured with a suitable initialization segment.
44972       * @param track {object} track metadata configuration
44973       * @param options {object} transmuxer options object
44974       * @param options.alignGopsAtEnd {boolean} If true, start from the end of the
44975       *        gopsToAlignWith list when attempting to align gop pts
44976       * @param options.keepOriginalTimestamps {boolean} If true, keep the timestamps
44977       *        in the source; false to adjust the first segment to start at 0.
44978       */
44979
44980      _VideoSegmentStream = function VideoSegmentStream(track, options) {
44981        var sequenceNumber = 0,
44982            nalUnits = [],
44983            gopsToAlignWith = [],
44984            config,
44985            pps;
44986        options = options || {};
44987
44988        _VideoSegmentStream.prototype.init.call(this);
44989
44990        delete track.minPTS;
44991        this.gopCache_ = [];
44992        /**
44993          * Constructs a ISO BMFF segment given H264 nalUnits
44994          * @param {Object} nalUnit A data event representing a nalUnit
44995          * @param {String} nalUnit.nalUnitType
vendor: 12,823 bytes, lines 44996-45319
44996          * @param {Object} nalUnit.config Properties for a mp4 track
44997          * @param {Uint8Array} nalUnit.data The nalUnit bytes
44998          * @see lib/codecs/h264.js
44999         **/
45000
45001        this.push = function (nalUnit) {
45002          trackDecodeInfo.collectDtsInfo(track, nalUnit); // record the track config
45003
45004          if (nalUnit.nalUnitType === 'seq_parameter_set_rbsp' && !config) {
45005            config = nalUnit.config;
45006            track.sps = [nalUnit.data];
45007            VIDEO_PROPERTIES.forEach(function (prop) {
45008              track[prop] = config[prop];
45009            }, this);
45010          }
45011
45012          if (nalUnit.nalUnitType === 'pic_parameter_set_rbsp' && !pps) {
45013            pps = nalUnit.data;
45014            track.pps = [nalUnit.data];
45015          } // buffer video until flush() is called
45016
45017
45018          nalUnits.push(nalUnit);
45019        };
45020        /**
45021          * Pass constructed ISO BMFF track and boxes on to the
45022          * next stream in the pipeline
45023         **/
45024
45025
45026        this.flush = function () {
45027          var frames,
45028              gopForFusion,
45029              gops,
45030              moof,
45031              mdat,
45032              boxes,
45033              prependedContentDuration = 0,
45034              firstGop,
45035              lastGop; // Throw away nalUnits at the start of the byte stream until
45036          // we find the first AUD
45037
45038          while (nalUnits.length) {
45039            if (nalUnits[0].nalUnitType === 'access_unit_delimiter_rbsp') {
45040              break;
45041            }
45042
45043            nalUnits.shift();
45044          } // Return early if no video data has been observed
45045
45046
45047          if (nalUnits.length === 0) {
45048            this.resetStream_();
45049            this.trigger('done', 'VideoSegmentStream');
45050            return;
45051          } // Organize the raw nal-units into arrays that represent
45052          // higher-level constructs such as frames and gops
45053          // (group-of-pictures)
45054
45055
45056          frames = frameUtils.groupNalsIntoFrames(nalUnits);
45057          gops = frameUtils.groupFramesIntoGops(frames); // If the first frame of this fragment is not a keyframe we have
45058          // a problem since MSE (on Chrome) requires a leading keyframe.
45059          //
45060          // We have two approaches to repairing this situation:
45061          // 1) GOP-FUSION:
45062          //    This is where we keep track of the GOPS (group-of-pictures)
45063          //    from previous fragments and attempt to find one that we can
45064          //    prepend to the current fragment in order to create a valid
45065          //    fragment.
45066          // 2) KEYFRAME-PULLING:
45067          //    Here we search for the first keyframe in the fragment and
45068          //    throw away all the frames between the start of the fragment
45069          //    and that keyframe. We then extend the duration and pull the
45070          //    PTS of the keyframe forward so that it covers the time range
45071          //    of the frames that were disposed of.
45072          //
45073          // #1 is far prefereable over #2 which can cause "stuttering" but
45074          // requires more things to be just right.
45075
45076          if (!gops[0][0].keyFrame) {
45077            // Search for a gop for fusion from our gopCache
45078            gopForFusion = this.getGopForFusion_(nalUnits[0], track);
45079
45080            if (gopForFusion) {
45081              // in order to provide more accurate timing information about the segment, save
45082              // the number of seconds prepended to the original segment due to GOP fusion
45083              prependedContentDuration = gopForFusion.duration;
45084              gops.unshift(gopForFusion); // Adjust Gops' metadata to account for the inclusion of the
45085              // new gop at the beginning
45086
45087              gops.byteLength += gopForFusion.byteLength;
45088              gops.nalCount += gopForFusion.nalCount;
45089              gops.pts = gopForFusion.pts;
45090              gops.dts = gopForFusion.dts;
45091              gops.duration += gopForFusion.duration;
45092            } else {
45093              // If we didn't find a candidate gop fall back to keyframe-pulling
45094              gops = frameUtils.extendFirstKeyFrame(gops);
45095            }
45096          } // Trim gops to align with gopsToAlignWith
45097
45098
45099          if (gopsToAlignWith.length) {
45100            var alignedGops;
45101
45102            if (options.alignGopsAtEnd) {
45103              alignedGops = this.alignGopsAtEnd_(gops);
45104            } else {
45105              alignedGops = this.alignGopsAtStart_(gops);
45106            }
45107
45108            if (!alignedGops) {
45109              // save all the nals in the last GOP into the gop cache
45110              this.gopCache_.unshift({
45111                gop: gops.pop(),
45112                pps: track.pps,
45113                sps: track.sps
45114              }); // Keep a maximum of 6 GOPs in the cache
45115
45116              this.gopCache_.length = Math.min(6, this.gopCache_.length); // Clear nalUnits
45117
45118              nalUnits = []; // return early no gops can be aligned with desired gopsToAlignWith
45119
45120              this.resetStream_();
45121              this.trigger('done', 'VideoSegmentStream');
45122              return;
45123            } // Some gops were trimmed. clear dts info so minSegmentDts and pts are correct
45124            // when recalculated before sending off to CoalesceStream
45125
45126
45127            trackDecodeInfo.clearDtsInfo(track);
45128            gops = alignedGops;
45129          }
45130
45131          trackDecodeInfo.collectDtsInfo(track, gops); // First, we have to build the index from byte locations to
45132          // samples (that is, frames) in the video data
45133
45134          track.samples = frameUtils.generateSampleTable(gops); // Concatenate the video data and construct the mdat
45135
45136          mdat = mp4Generator.mdat(frameUtils.concatenateNalData(gops));
45137          track.baseMediaDecodeTime = trackDecodeInfo.calculateTrackBaseMediaDecodeTime(track, options.keepOriginalTimestamps);
45138          this.trigger('processedGopsInfo', gops.map(function (gop) {
45139            return {
45140              pts: gop.pts,
45141              dts: gop.dts,
45142              byteLength: gop.byteLength
45143            };
45144          }));
45145          firstGop = gops[0];
45146          lastGop = gops[gops.length - 1];
45147          this.trigger('segmentTimingInfo', generateVideoSegmentTimingInfo(track.baseMediaDecodeTime, firstGop.dts, firstGop.pts, lastGop.dts + lastGop.duration, lastGop.pts + lastGop.duration, prependedContentDuration));
45148          this.trigger('timingInfo', {
45149            start: gops[0].dts,
45150            end: gops[gops.length - 1].dts + gops[gops.length - 1].duration
45151          }); // save all the nals in the last GOP into the gop cache
45152
45153          this.gopCache_.unshift({
45154            gop: gops.pop(),
45155            pps: track.pps,
45156            sps: track.sps
45157          }); // Keep a maximum of 6 GOPs in the cache
45158
45159          this.gopCache_.length = Math.min(6, this.gopCache_.length); // Clear nalUnits
45160
45161          nalUnits = [];
45162          this.trigger('baseMediaDecodeTime', track.baseMediaDecodeTime);
45163          this.trigger('timelineStartInfo', track.timelineStartInfo);
45164          moof = mp4Generator.moof(sequenceNumber, [track]); // it would be great to allocate this array up front instead of
45165          // throwing away hundreds of media segment fragments
45166
45167          boxes = new Uint8Array(moof.byteLength + mdat.byteLength); // Bump the sequence number for next time
45168
45169          sequenceNumber++;
45170          boxes.set(moof);
45171          boxes.set(mdat, moof.byteLength);
45172          this.trigger('data', {
45173            track: track,
45174            boxes: boxes
45175          });
45176          this.resetStream_(); // Continue with the flush process now
45177
45178          this.trigger('done', 'VideoSegmentStream');
45179        };
45180
45181        this.reset = function () {
45182          this.resetStream_();
45183          nalUnits = [];
45184          this.gopCache_.length = 0;
45185          gopsToAlignWith.length = 0;
45186          this.trigger('reset');
45187        };
45188
45189        this.resetStream_ = function () {
45190          trackDecodeInfo.clearDtsInfo(track); // reset config and pps because they may differ across segments
45191          // for instance, when we are rendition switching
45192
45193          config = undefined;
45194          pps = undefined;
45195        }; // Search for a candidate Gop for gop-fusion from the gop cache and
45196        // return it or return null if no good candidate was found
45197
45198
45199        this.getGopForFusion_ = function (nalUnit) {
45200          var halfSecond = 45000,
45201              // Half-a-second in a 90khz clock
45202          allowableOverlap = 10000,
45203              // About 3 frames @ 30fps
45204          nearestDistance = Infinity,
45205              dtsDistance,
45206              nearestGopObj,
45207              currentGop,
45208              currentGopObj,
45209              i; // Search for the GOP nearest to the beginning of this nal unit
45210
45211          for (i = 0; i < this.gopCache_.length; i++) {
45212            currentGopObj = this.gopCache_[i];
45213            currentGop = currentGopObj.gop; // Reject Gops with different SPS or PPS
45214
45215            if (!(track.pps && arrayEquals(track.pps[0], currentGopObj.pps[0])) || !(track.sps && arrayEquals(track.sps[0], currentGopObj.sps[0]))) {
45216              continue;
45217            } // Reject Gops that would require a negative baseMediaDecodeTime
45218
45219
45220            if (currentGop.dts < track.timelineStartInfo.dts) {
45221              continue;
45222            } // The distance between the end of the gop and the start of the nalUnit
45223
45224
45225            dtsDistance = nalUnit.dts - currentGop.dts - currentGop.duration; // Only consider GOPS that start before the nal unit and end within
45226            // a half-second of the nal unit
45227
45228            if (dtsDistance >= -allowableOverlap && dtsDistance <= halfSecond) {
45229              // Always use the closest GOP we found if there is more than
45230              // one candidate
45231              if (!nearestGopObj || nearestDistance > dtsDistance) {
45232                nearestGopObj = currentGopObj;
45233                nearestDistance = dtsDistance;
45234              }
45235            }
45236          }
45237
45238          if (nearestGopObj) {
45239            return nearestGopObj.gop;
45240          }
45241
45242          return null;
45243        }; // trim gop list to the first gop found that has a matching pts with a gop in the list
45244        // of gopsToAlignWith starting from the START of the list
45245
45246
45247        this.alignGopsAtStart_ = function (gops) {
45248          var alignIndex, gopIndex, align, gop, byteLength, nalCount, duration, alignedGops;
45249          byteLength = gops.byteLength;
45250          nalCount = gops.nalCount;
45251          duration = gops.duration;
45252          alignIndex = gopIndex = 0;
45253
45254          while (alignIndex < gopsToAlignWith.length && gopIndex < gops.length) {
45255            align = gopsToAlignWith[alignIndex];
45256            gop = gops[gopIndex];
45257
45258            if (align.pts === gop.pts) {
45259              break;
45260            }
45261
45262            if (gop.pts > align.pts) {
45263              // this current gop starts after the current gop we want to align on, so increment
45264              // align index
45265              alignIndex++;
45266              continue;
45267            } // current gop starts before the current gop we want to align on. so increment gop
45268            // index
45269
45270
45271            gopIndex++;
45272            byteLength -= gop.byteLength;
45273            nalCount -= gop.nalCount;
45274            duration -= gop.duration;
45275          }
45276
45277          if (gopIndex === 0) {
45278            // no gops to trim
45279            return gops;
45280          }
45281
45282          if (gopIndex === gops.length) {
45283            // all gops trimmed, skip appending all gops
45284            return null;
45285          }
45286
45287          alignedGops = gops.slice(gopIndex);
45288          alignedGops.byteLength = byteLength;
45289          alignedGops.duration = duration;
45290          alignedGops.nalCount = nalCount;
45291          alignedGops.pts = alignedGops[0].pts;
45292          alignedGops.dts = alignedGops[0].dts;
45293          return alignedGops;
45294        }; // trim gop list to the first gop found that has a matching pts with a gop in the list
45295        // of gopsToAlignWith starting from the END of the list
45296
45297
45298        this.alignGopsAtEnd_ = function (gops) {
45299          var alignIndex, gopIndex, align, gop, alignEndIndex, matchFound;
45300          alignIndex = gopsToAlignWith.length - 1;
45301          gopIndex = gops.length - 1;
45302          alignEndIndex = null;
45303          matchFound = false;
45304
45305          while (alignIndex >= 0 && gopIndex >= 0) {
45306            align = gopsToAlignWith[alignIndex];
45307            gop = gops[gopIndex];
45308
45309            if (align.pts === gop.pts) {
45310              matchFound = true;
45311              break;
45312            }
45313
45314            if (align.pts > gop.pts) {
45315              alignIndex--;
45316              continue;
45317            }
45318
45319            if (alignIndex === gopsToAlignWith.length - 1) {
vendor: 4,564 bytes, lines 45320-45450
45320              // gop.pts is greater than the last alignment candidate. If no match is found
45321              // by the end of this loop, we still want to append gops that come after this
45322              // point
45323              alignEndIndex = gopIndex;
45324            }
45325
45326            gopIndex--;
45327          }
45328
45329          if (!matchFound && alignEndIndex === null) {
45330            return null;
45331          }
45332
45333          var trimIndex;
45334
45335          if (matchFound) {
45336            trimIndex = gopIndex;
45337          } else {
45338            trimIndex = alignEndIndex;
45339          }
45340
45341          if (trimIndex === 0) {
45342            return gops;
45343          }
45344
45345          var alignedGops = gops.slice(trimIndex);
45346          var metadata = alignedGops.reduce(function (total, gop) {
45347            total.byteLength += gop.byteLength;
45348            total.duration += gop.duration;
45349            total.nalCount += gop.nalCount;
45350            return total;
45351          }, {
45352            byteLength: 0,
45353            duration: 0,
45354            nalCount: 0
45355          });
45356          alignedGops.byteLength = metadata.byteLength;
45357          alignedGops.duration = metadata.duration;
45358          alignedGops.nalCount = metadata.nalCount;
45359          alignedGops.pts = alignedGops[0].pts;
45360          alignedGops.dts = alignedGops[0].dts;
45361          return alignedGops;
45362        };
45363
45364        this.alignGopsWith = function (newGopsToAlignWith) {
45365          gopsToAlignWith = newGopsToAlignWith;
45366        };
45367      };
45368
45369      _VideoSegmentStream.prototype = new stream();
45370      /**
45371       * A Stream that can combine multiple streams (ie. audio & video)
45372       * into a single output segment for MSE. Also supports audio-only
45373       * and video-only streams.
45374       * @param options {object} transmuxer options object
45375       * @param options.keepOriginalTimestamps {boolean} If true, keep the timestamps
45376       *        in the source; false to adjust the first segment to start at media timeline start.
45377       */
45378
45379      _CoalesceStream = function CoalesceStream(options, metadataStream) {
45380        // Number of Tracks per output segment
45381        // If greater than 1, we combine multiple
45382        // tracks into a single segment
45383        this.numberOfTracks = 0;
45384        this.metadataStream = metadataStream;
45385        options = options || {};
45386
45387        if (typeof options.remux !== 'undefined') {
45388          this.remuxTracks = !!options.remux;
45389        } else {
45390          this.remuxTracks = true;
45391        }
45392
45393        if (typeof options.keepOriginalTimestamps === 'boolean') {
45394          this.keepOriginalTimestamps = options.keepOriginalTimestamps;
45395        } else {
45396          this.keepOriginalTimestamps = false;
45397        }
45398
45399        this.pendingTracks = [];
45400        this.videoTrack = null;
45401        this.pendingBoxes = [];
45402        this.pendingCaptions = [];
45403        this.pendingMetadata = [];
45404        this.pendingBytes = 0;
45405        this.emittedTracks = 0;
45406
45407        _CoalesceStream.prototype.init.call(this); // Take output from multiple
45408
45409
45410        this.push = function (output) {
45411          // buffer incoming captions until the associated video segment
45412          // finishes
45413          if (output.text) {
45414            return this.pendingCaptions.push(output);
45415          } // buffer incoming id3 tags until the final flush
45416
45417
45418          if (output.frames) {
45419            return this.pendingMetadata.push(output);
45420          } // Add this track to the list of pending tracks and store
45421          // important information required for the construction of
45422          // the final segment
45423
45424
45425          this.pendingTracks.push(output.track);
45426          this.pendingBytes += output.boxes.byteLength; // TODO: is there an issue for this against chrome?
45427          // We unshift audio and push video because
45428          // as of Chrome 75 when switching from
45429          // one init segment to another if the video
45430          // mdat does not appear after the audio mdat
45431          // only audio will play for the duration of our transmux.
45432
45433          if (output.track.type === 'video') {
45434            this.videoTrack = output.track;
45435            this.pendingBoxes.push(output.boxes);
45436          }
45437
45438          if (output.track.type === 'audio') {
45439            this.audioTrack = output.track;
45440            this.pendingBoxes.unshift(output.boxes);
45441          }
45442        };
45443      };
45444
45445      _CoalesceStream.prototype = new stream();
45446
45447      _CoalesceStream.prototype.flush = function (flushSource) {
45448        var offset = 0,
45449            event = {
45450          captions: [],
vendor: 4,830 bytes, lines 45451-45556
45451          captionStreams: {},
45452          metadata: [],
45453          info: {}
45454        },
45455            caption,
45456            id3,
45457            initSegment,
45458            timelineStartPts = 0,
45459            i;
45460
45461        if (this.pendingTracks.length < this.numberOfTracks) {
45462          if (flushSource !== 'VideoSegmentStream' && flushSource !== 'AudioSegmentStream') {
45463            // Return because we haven't received a flush from a data-generating
45464            // portion of the segment (meaning that we have only recieved meta-data
45465            // or captions.)
45466            return;
45467          } else if (this.remuxTracks) {
45468            // Return until we have enough tracks from the pipeline to remux (if we
45469            // are remuxing audio and video into a single MP4)
45470            return;
45471          } else if (this.pendingTracks.length === 0) {
45472            // In the case where we receive a flush without any data having been
45473            // received we consider it an emitted track for the purposes of coalescing
45474            // `done` events.
45475            // We do this for the case where there is an audio and video track in the
45476            // segment but no audio data. (seen in several playlists with alternate
45477            // audio tracks and no audio present in the main TS segments.)
45478            this.emittedTracks++;
45479
45480            if (this.emittedTracks >= this.numberOfTracks) {
45481              this.trigger('done');
45482              this.emittedTracks = 0;
45483            }
45484
45485            return;
45486          }
45487        }
45488
45489        if (this.videoTrack) {
45490          timelineStartPts = this.videoTrack.timelineStartInfo.pts;
45491          VIDEO_PROPERTIES.forEach(function (prop) {
45492            event.info[prop] = this.videoTrack[prop];
45493          }, this);
45494        } else if (this.audioTrack) {
45495          timelineStartPts = this.audioTrack.timelineStartInfo.pts;
45496          AUDIO_PROPERTIES.forEach(function (prop) {
45497            event.info[prop] = this.audioTrack[prop];
45498          }, this);
45499        }
45500
45501        if (this.videoTrack || this.audioTrack) {
45502          if (this.pendingTracks.length === 1) {
45503            event.type = this.pendingTracks[0].type;
45504          } else {
45505            event.type = 'combined';
45506          }
45507
45508          this.emittedTracks += this.pendingTracks.length;
45509          initSegment = mp4Generator.initSegment(this.pendingTracks); // Create a new typed array to hold the init segment
45510
45511          event.initSegment = new Uint8Array(initSegment.byteLength); // Create an init segment containing a moov
45512          // and track definitions
45513
45514          event.initSegment.set(initSegment); // Create a new typed array to hold the moof+mdats
45515
45516          event.data = new Uint8Array(this.pendingBytes); // Append each moof+mdat (one per track) together
45517
45518          for (i = 0; i < this.pendingBoxes.length; i++) {
45519            event.data.set(this.pendingBoxes[i], offset);
45520            offset += this.pendingBoxes[i].byteLength;
45521          } // Translate caption PTS times into second offsets to match the
45522          // video timeline for the segment, and add track info
45523
45524
45525          for (i = 0; i < this.pendingCaptions.length; i++) {
45526            caption = this.pendingCaptions[i];
45527            caption.startTime = clock.metadataTsToSeconds(caption.startPts, timelineStartPts, this.keepOriginalTimestamps);
45528            caption.endTime = clock.metadataTsToSeconds(caption.endPts, timelineStartPts, this.keepOriginalTimestamps);
45529            event.captionStreams[caption.stream] = true;
45530            event.captions.push(caption);
45531          } // Translate ID3 frame PTS times into second offsets to match the
45532          // video timeline for the segment
45533
45534
45535          for (i = 0; i < this.pendingMetadata.length; i++) {
45536            id3 = this.pendingMetadata[i];
45537            id3.cueTime = clock.metadataTsToSeconds(id3.pts, timelineStartPts, this.keepOriginalTimestamps);
45538            event.metadata.push(id3);
45539          } // We add this to every single emitted segment even though we only need
45540          // it for the first
45541
45542
45543          event.metadata.dispatchType = this.metadataStream.dispatchType; // Reset stream state
45544
45545          this.pendingTracks.length = 0;
45546          this.videoTrack = null;
45547          this.pendingBoxes.length = 0;
45548          this.pendingCaptions.length = 0;
45549          this.pendingBytes = 0;
45550          this.pendingMetadata.length = 0; // Emit the built segment
45551          // We include captions and ID3 tags for backwards compatibility,
45552          // ideally we should send only video and audio in the data event
45553
45554          this.trigger('data', event); // Emit each caption to the outside world
45555          // Ideally, this would happen immediately on parsing captions,
45556          // but we need to ensure that video data is sent back first
vendor: 14,618 bytes, lines 45557-45889
45557          // so that caption timing can be adjusted to match video timing
45558
45559          for (i = 0; i < event.captions.length; i++) {
45560            caption = event.captions[i];
45561            this.trigger('caption', caption);
45562          } // Emit each id3 tag to the outside world
45563          // Ideally, this would happen immediately on parsing the tag,
45564          // but we need to ensure that video data is sent back first
45565          // so that ID3 frame timing can be adjusted to match video timing
45566
45567
45568          for (i = 0; i < event.metadata.length; i++) {
45569            id3 = event.metadata[i];
45570            this.trigger('id3Frame', id3);
45571          }
45572        } // Only emit `done` if all tracks have been flushed and emitted
45573
45574
45575        if (this.emittedTracks >= this.numberOfTracks) {
45576          this.trigger('done');
45577          this.emittedTracks = 0;
45578        }
45579      };
45580
45581      _CoalesceStream.prototype.setRemux = function (val) {
45582        this.remuxTracks = val;
45583      };
45584      /**
45585       * A Stream that expects MP2T binary data as input and produces
45586       * corresponding media segments, suitable for use with Media Source
45587       * Extension (MSE) implementations that support the ISO BMFF byte
45588       * stream format, like Chrome.
45589       */
45590
45591
45592      _Transmuxer = function Transmuxer(options) {
45593        var self = this,
45594            hasFlushed = true,
45595            videoTrack,
45596            audioTrack;
45597
45598        _Transmuxer.prototype.init.call(this);
45599
45600        options = options || {};
45601        this.baseMediaDecodeTime = options.baseMediaDecodeTime || 0;
45602        this.transmuxPipeline_ = {};
45603
45604        this.setupAacPipeline = function () {
45605          var pipeline = {};
45606          this.transmuxPipeline_ = pipeline;
45607          pipeline.type = 'aac';
45608          pipeline.metadataStream = new m2ts_1.MetadataStream(); // set up the parsing pipeline
45609
45610          pipeline.aacStream = new aac();
45611          pipeline.audioTimestampRolloverStream = new m2ts_1.TimestampRolloverStream('audio');
45612          pipeline.timedMetadataTimestampRolloverStream = new m2ts_1.TimestampRolloverStream('timed-metadata');
45613          pipeline.adtsStream = new adts();
45614          pipeline.coalesceStream = new _CoalesceStream(options, pipeline.metadataStream);
45615          pipeline.headOfPipeline = pipeline.aacStream;
45616          pipeline.aacStream.pipe(pipeline.audioTimestampRolloverStream).pipe(pipeline.adtsStream);
45617          pipeline.aacStream.pipe(pipeline.timedMetadataTimestampRolloverStream).pipe(pipeline.metadataStream).pipe(pipeline.coalesceStream);
45618          pipeline.metadataStream.on('timestamp', function (frame) {
45619            pipeline.aacStream.setTimestamp(frame.timeStamp);
45620          });
45621          pipeline.aacStream.on('data', function (data) {
45622            if (data.type === 'timed-metadata' && !pipeline.audioSegmentStream) {
45623              audioTrack = audioTrack || {
45624                timelineStartInfo: {
45625                  baseMediaDecodeTime: self.baseMediaDecodeTime
45626                },
45627                codec: 'adts',
45628                type: 'audio'
45629              }; // hook up the audio segment stream to the first track with aac data
45630
45631              pipeline.coalesceStream.numberOfTracks++;
45632              pipeline.audioSegmentStream = new _AudioSegmentStream(audioTrack, options);
45633              pipeline.audioSegmentStream.on('timingInfo', self.trigger.bind(self, 'audioTimingInfo')); // Set up the final part of the audio pipeline
45634
45635              pipeline.adtsStream.pipe(pipeline.audioSegmentStream).pipe(pipeline.coalesceStream);
45636            } // emit pmt info
45637
45638
45639            self.trigger('trackinfo', {
45640              hasAudio: !!audioTrack,
45641              hasVideo: !!videoTrack
45642            });
45643          }); // Re-emit any data coming from the coalesce stream to the outside world
45644
45645          pipeline.coalesceStream.on('data', this.trigger.bind(this, 'data')); // Let the consumer know we have finished flushing the entire pipeline
45646
45647          pipeline.coalesceStream.on('done', this.trigger.bind(this, 'done'));
45648        };
45649
45650        this.setupTsPipeline = function () {
45651          var pipeline = {};
45652          this.transmuxPipeline_ = pipeline;
45653          pipeline.type = 'ts';
45654          pipeline.metadataStream = new m2ts_1.MetadataStream(); // set up the parsing pipeline
45655
45656          pipeline.packetStream = new m2ts_1.TransportPacketStream();
45657          pipeline.parseStream = new m2ts_1.TransportParseStream();
45658          pipeline.elementaryStream = new m2ts_1.ElementaryStream();
45659          pipeline.timestampRolloverStream = new m2ts_1.TimestampRolloverStream();
45660          pipeline.adtsStream = new adts();
45661          pipeline.h264Stream = new H264Stream();
45662          pipeline.captionStream = new m2ts_1.CaptionStream();
45663          pipeline.coalesceStream = new _CoalesceStream(options, pipeline.metadataStream);
45664          pipeline.headOfPipeline = pipeline.packetStream; // disassemble MPEG2-TS packets into elementary streams
45665
45666          pipeline.packetStream.pipe(pipeline.parseStream).pipe(pipeline.elementaryStream).pipe(pipeline.timestampRolloverStream); // !!THIS ORDER IS IMPORTANT!!
45667          // demux the streams
45668
45669          pipeline.timestampRolloverStream.pipe(pipeline.h264Stream);
45670          pipeline.timestampRolloverStream.pipe(pipeline.adtsStream);
45671          pipeline.timestampRolloverStream.pipe(pipeline.metadataStream).pipe(pipeline.coalesceStream); // Hook up CEA-608/708 caption stream
45672
45673          pipeline.h264Stream.pipe(pipeline.captionStream).pipe(pipeline.coalesceStream);
45674          pipeline.elementaryStream.on('data', function (data) {
45675            var i;
45676
45677            if (data.type === 'metadata') {
45678              i = data.tracks.length; // scan the tracks listed in the metadata
45679
45680              while (i--) {
45681                if (!videoTrack && data.tracks[i].type === 'video') {
45682                  videoTrack = data.tracks[i];
45683                  videoTrack.timelineStartInfo.baseMediaDecodeTime = self.baseMediaDecodeTime;
45684                } else if (!audioTrack && data.tracks[i].type === 'audio') {
45685                  audioTrack = data.tracks[i];
45686                  audioTrack.timelineStartInfo.baseMediaDecodeTime = self.baseMediaDecodeTime;
45687                }
45688              } // hook up the video segment stream to the first track with h264 data
45689
45690
45691              if (videoTrack && !pipeline.videoSegmentStream) {
45692                pipeline.coalesceStream.numberOfTracks++;
45693                pipeline.videoSegmentStream = new _VideoSegmentStream(videoTrack, options);
45694                pipeline.videoSegmentStream.on('timelineStartInfo', function (timelineStartInfo) {
45695                  // When video emits timelineStartInfo data after a flush, we forward that
45696                  // info to the AudioSegmentStream, if it exists, because video timeline
45697                  // data takes precedence.
45698                  if (audioTrack) {
45699                    audioTrack.timelineStartInfo = timelineStartInfo; // On the first segment we trim AAC frames that exist before the
45700                    // very earliest DTS we have seen in video because Chrome will
45701                    // interpret any video track with a baseMediaDecodeTime that is
45702                    // non-zero as a gap.
45703
45704                    pipeline.audioSegmentStream.setEarliestDts(timelineStartInfo.dts);
45705                  }
45706                });
45707                pipeline.videoSegmentStream.on('processedGopsInfo', self.trigger.bind(self, 'gopInfo'));
45708                pipeline.videoSegmentStream.on('segmentTimingInfo', self.trigger.bind(self, 'videoSegmentTimingInfo'));
45709                pipeline.videoSegmentStream.on('baseMediaDecodeTime', function (baseMediaDecodeTime) {
45710                  if (audioTrack) {
45711                    pipeline.audioSegmentStream.setVideoBaseMediaDecodeTime(baseMediaDecodeTime);
45712                  }
45713                });
45714                pipeline.videoSegmentStream.on('timingInfo', self.trigger.bind(self, 'videoTimingInfo')); // Set up the final part of the video pipeline
45715
45716                pipeline.h264Stream.pipe(pipeline.videoSegmentStream).pipe(pipeline.coalesceStream);
45717              }
45718
45719              if (audioTrack && !pipeline.audioSegmentStream) {
45720                // hook up the audio segment stream to the first track with aac data
45721                pipeline.coalesceStream.numberOfTracks++;
45722                pipeline.audioSegmentStream = new _AudioSegmentStream(audioTrack, options);
45723                pipeline.audioSegmentStream.on('timingInfo', self.trigger.bind(self, 'audioTimingInfo')); // Set up the final part of the audio pipeline
45724
45725                pipeline.adtsStream.pipe(pipeline.audioSegmentStream).pipe(pipeline.coalesceStream);
45726              } // emit pmt info
45727
45728
45729              self.trigger('trackinfo', {
45730                hasAudio: !!audioTrack,
45731                hasVideo: !!videoTrack
45732              });
45733            }
45734          }); // Re-emit any data coming from the coalesce stream to the outside world
45735
45736          pipeline.coalesceStream.on('data', this.trigger.bind(this, 'data'));
45737          pipeline.coalesceStream.on('id3Frame', function (id3Frame) {
45738            id3Frame.dispatchType = pipeline.metadataStream.dispatchType;
45739            self.trigger('id3Frame', id3Frame);
45740          });
45741          pipeline.coalesceStream.on('caption', this.trigger.bind(this, 'caption')); // Let the consumer know we have finished flushing the entire pipeline
45742
45743          pipeline.coalesceStream.on('done', this.trigger.bind(this, 'done'));
45744        }; // hook up the segment streams once track metadata is delivered
45745
45746
45747        this.setBaseMediaDecodeTime = function (baseMediaDecodeTime) {
45748          var pipeline = this.transmuxPipeline_;
45749
45750          if (!options.keepOriginalTimestamps) {
45751            this.baseMediaDecodeTime = baseMediaDecodeTime;
45752          }
45753
45754          if (audioTrack) {
45755            audioTrack.timelineStartInfo.dts = undefined;
45756            audioTrack.timelineStartInfo.pts = undefined;
45757            trackDecodeInfo.clearDtsInfo(audioTrack);
45758
45759            if (!options.keepOriginalTimestamps) {
45760              audioTrack.timelineStartInfo.baseMediaDecodeTime = baseMediaDecodeTime;
45761            }
45762
45763            if (pipeline.audioTimestampRolloverStream) {
45764              pipeline.audioTimestampRolloverStream.discontinuity();
45765            }
45766          }
45767
45768          if (videoTrack) {
45769            if (pipeline.videoSegmentStream) {
45770              pipeline.videoSegmentStream.gopCache_ = [];
45771            }
45772
45773            videoTrack.timelineStartInfo.dts = undefined;
45774            videoTrack.timelineStartInfo.pts = undefined;
45775            trackDecodeInfo.clearDtsInfo(videoTrack);
45776            pipeline.captionStream.reset();
45777
45778            if (!options.keepOriginalTimestamps) {
45779              videoTrack.timelineStartInfo.baseMediaDecodeTime = baseMediaDecodeTime;
45780            }
45781          }
45782
45783          if (pipeline.timestampRolloverStream) {
45784            pipeline.timestampRolloverStream.discontinuity();
45785          }
45786        };
45787
45788        this.setAudioAppendStart = function (timestamp) {
45789          if (audioTrack) {
45790            this.transmuxPipeline_.audioSegmentStream.setAudioAppendStart(timestamp);
45791          }
45792        };
45793
45794        this.setRemux = function (val) {
45795          var pipeline = this.transmuxPipeline_;
45796          options.remux = val;
45797
45798          if (pipeline && pipeline.coalesceStream) {
45799            pipeline.coalesceStream.setRemux(val);
45800          }
45801        };
45802
45803        this.alignGopsWith = function (gopsToAlignWith) {
45804          if (videoTrack && this.transmuxPipeline_.videoSegmentStream) {
45805            this.transmuxPipeline_.videoSegmentStream.alignGopsWith(gopsToAlignWith);
45806          }
45807        }; // feed incoming data to the front of the parsing pipeline
45808
45809
45810        this.push = function (data) {
45811          if (hasFlushed) {
45812            var isAac = isLikelyAacData$1(data);
45813
45814            if (isAac && this.transmuxPipeline_.type !== 'aac') {
45815              this.setupAacPipeline();
45816            } else if (!isAac && this.transmuxPipeline_.type !== 'ts') {
45817              this.setupTsPipeline();
45818            }
45819
45820            hasFlushed = false;
45821          }
45822
45823          this.transmuxPipeline_.headOfPipeline.push(data);
45824        }; // flush any buffered data
45825
45826
45827        this.flush = function () {
45828          hasFlushed = true; // Start at the top of the pipeline and flush all pending work
45829
45830          this.transmuxPipeline_.headOfPipeline.flush();
45831        };
45832
45833        this.endTimeline = function () {
45834          this.transmuxPipeline_.headOfPipeline.endTimeline();
45835        };
45836
45837        this.reset = function () {
45838          if (this.transmuxPipeline_.headOfPipeline) {
45839            this.transmuxPipeline_.headOfPipeline.reset();
45840          }
45841        }; // Caption data has to be reset when seeking outside buffered range
45842
45843
45844        this.resetCaptions = function () {
45845          if (this.transmuxPipeline_.captionStream) {
45846            this.transmuxPipeline_.captionStream.reset();
45847          }
45848        };
45849      };
45850
45851      _Transmuxer.prototype = new stream();
45852      var transmuxer = {
45853        Transmuxer: _Transmuxer,
45854        VideoSegmentStream: _VideoSegmentStream,
45855        AudioSegmentStream: _AudioSegmentStream,
45856        AUDIO_PROPERTIES: AUDIO_PROPERTIES,
45857        VIDEO_PROPERTIES: VIDEO_PROPERTIES,
45858        // exported for testing
45859        generateVideoSegmentTimingInfo: generateVideoSegmentTimingInfo
45860      };
45861
45862      var classCallCheck = function classCallCheck(instance, Constructor) {
45863        if (!(instance instanceof Constructor)) {
45864          throw new TypeError("Cannot call a class as a function");
45865        }
45866      };
45867
45868      var createClass = function () {
45869        function defineProperties(target, props) {
45870          for (var i = 0; i < props.length; i++) {
45871            var descriptor = props[i];
45872            descriptor.enumerable = descriptor.enumerable || false;
45873            descriptor.configurable = true;
45874            if ("value" in descriptor) descriptor.writable = true;
45875            Object.defineProperty(target, descriptor.key, descriptor);
45876          }
45877        }
45878
45879        return function (Constructor, protoProps, staticProps) {
45880          if (protoProps) defineProperties(Constructor.prototype, protoProps);
45881          if (staticProps) defineProperties(Constructor, staticProps);
45882          return Constructor;
45883        };
45884      }();
45885      /**
45886       * @file transmuxer-worker.js
45887       */
45888
45889      /**
vendor: 32,238 bytes, lines 45890-46850
45890       * Re-emits transmuxer events by converting them into messages to the
45891       * world outside the worker.
45892       *
45893       * @param {Object} transmuxer the transmuxer to wire events on
45894       * @private
45895       */
45896
45897
45898      var wireTransmuxerEvents = function wireTransmuxerEvents(self, transmuxer$$1) {
45899        transmuxer$$1.on('data', function (segment) {
45900          // transfer ownership of the underlying ArrayBuffer
45901          // instead of doing a copy to save memory
45902          // ArrayBuffers are transferable but generic TypedArrays are not
45903          // @link https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API/Using_web_workers#Passing_data_by_transferring_ownership_(transferable_objects)
45904          var initArray = segment.initSegment;
45905          segment.initSegment = {
45906            data: initArray.buffer,
45907            byteOffset: initArray.byteOffset,
45908            byteLength: initArray.byteLength
45909          };
45910          var typedArray = segment.data;
45911          segment.data = typedArray.buffer;
45912          self.postMessage({
45913            action: 'data',
45914            segment: segment,
45915            byteOffset: typedArray.byteOffset,
45916            byteLength: typedArray.byteLength
45917          }, [segment.data]);
45918        });
45919
45920        if (transmuxer$$1.captionStream) {
45921          transmuxer$$1.captionStream.on('data', function (caption) {
45922            self.postMessage({
45923              action: 'caption',
45924              data: caption
45925            });
45926          });
45927        }
45928
45929        transmuxer$$1.on('done', function (data) {
45930          self.postMessage({
45931            action: 'done'
45932          });
45933        });
45934        transmuxer$$1.on('gopInfo', function (gopInfo) {
45935          self.postMessage({
45936            action: 'gopInfo',
45937            gopInfo: gopInfo
45938          });
45939        });
45940        transmuxer$$1.on('videoSegmentTimingInfo', function (videoSegmentTimingInfo) {
45941          self.postMessage({
45942            action: 'videoSegmentTimingInfo',
45943            videoSegmentTimingInfo: videoSegmentTimingInfo
45944          });
45945        });
45946      };
45947      /**
45948       * All incoming messages route through this hash. If no function exists
45949       * to handle an incoming message, then we ignore the message.
45950       *
45951       * @class MessageHandlers
45952       * @param {Object} options the options to initialize with
45953       */
45954
45955
45956      var MessageHandlers = function () {
45957        function MessageHandlers(self, options) {
45958          classCallCheck(this, MessageHandlers);
45959          this.options = options || {};
45960          this.self = self;
45961          this.init();
45962        }
45963        /**
45964         * initialize our web worker and wire all the events.
45965         */
45966
45967
45968        createClass(MessageHandlers, [{
45969          key: 'init',
45970          value: function init() {
45971            if (this.transmuxer) {
45972              this.transmuxer.dispose();
45973            }
45974
45975            this.transmuxer = new transmuxer.Transmuxer(this.options);
45976            wireTransmuxerEvents(this.self, this.transmuxer);
45977          }
45978          /**
45979           * Adds data (a ts segment) to the start of the transmuxer pipeline for
45980           * processing.
45981           *
45982           * @param {ArrayBuffer} data data to push into the muxer
45983           */
45984
45985        }, {
45986          key: 'push',
45987          value: function push(data) {
45988            // Cast array buffer to correct type for transmuxer
45989            var segment = new Uint8Array(data.data, data.byteOffset, data.byteLength);
45990            this.transmuxer.push(segment);
45991          }
45992          /**
45993           * Recreate the transmuxer so that the next segment added via `push`
45994           * start with a fresh transmuxer.
45995           */
45996
45997        }, {
45998          key: 'reset',
45999          value: function reset() {
46000            this.init();
46001          }
46002          /**
46003           * Set the value that will be used as the `baseMediaDecodeTime` time for the
46004           * next segment pushed in. Subsequent segments will have their `baseMediaDecodeTime`
46005           * set relative to the first based on the PTS values.
46006           *
46007           * @param {Object} data used to set the timestamp offset in the muxer
46008           */
46009
46010        }, {
46011          key: 'setTimestampOffset',
46012          value: function setTimestampOffset(data) {
46013            var timestampOffset = data.timestampOffset || 0;
46014            this.transmuxer.setBaseMediaDecodeTime(Math.round(timestampOffset * 90000));
46015          }
46016        }, {
46017          key: 'setAudioAppendStart',
46018          value: function setAudioAppendStart(data) {
46019            this.transmuxer.setAudioAppendStart(Math.ceil(data.appendStart * 90000));
46020          }
46021          /**
46022           * Forces the pipeline to finish processing the last segment and emit it's
46023           * results.
46024           *
46025           * @param {Object} data event data, not really used
46026           */
46027
46028        }, {
46029          key: 'flush',
46030          value: function flush(data) {
46031            this.transmuxer.flush();
46032          }
46033        }, {
46034          key: 'resetCaptions',
46035          value: function resetCaptions() {
46036            this.transmuxer.resetCaptions();
46037          }
46038        }, {
46039          key: 'alignGopsWith',
46040          value: function alignGopsWith(data) {
46041            this.transmuxer.alignGopsWith(data.gopsToAlignWith.slice());
46042          }
46043        }]);
46044        return MessageHandlers;
46045      }();
46046      /**
46047       * Our web wroker interface so that things can talk to mux.js
46048       * that will be running in a web worker. the scope is passed to this by
46049       * webworkify.
46050       *
46051       * @param {Object} self the scope for the web worker
46052       */
46053
46054
46055      var TransmuxerWorker = function TransmuxerWorker(self) {
46056        self.onmessage = function (event) {
46057          if (event.data.action === 'init' && event.data.options) {
46058            this.messageHandlers = new MessageHandlers(self, event.data.options);
46059            return;
46060          }
46061
46062          if (!this.messageHandlers) {
46063            this.messageHandlers = new MessageHandlers(self);
46064          }
46065
46066          if (event.data && event.data.action && event.data.action !== 'init') {
46067            if (this.messageHandlers[event.data.action]) {
46068              this.messageHandlers[event.data.action](event.data);
46069            }
46070          }
46071        };
46072      };
46073
46074      var transmuxerWorker = new TransmuxerWorker(self);
46075      return transmuxerWorker;
46076    }();
46077  });
46078  /**
46079   * @file - codecs.js - Handles tasks regarding codec strings such as translating them to
46080   * codec strings, or translating codec strings into objects that can be examined.
46081   */
46082  // Default codec parameters if none were provided for video and/or audio
46083
46084  var defaultCodecs = {
46085    videoCodec: 'avc1',
46086    videoObjectTypeIndicator: '.4d400d',
46087    // AAC-LC
46088    audioProfile: '2'
46089  };
46090  /**
46091   * Replace the old apple-style `avc1.<dd>.<dd>` codec string with the standard
46092   * `avc1.<hhhhhh>`
46093   *
46094   * @param {Array} codecs an array of codec strings to fix
46095   * @return {Array} the translated codec array
46096   * @private
46097   */
46098
46099  var translateLegacyCodecs = function translateLegacyCodecs(codecs) {
46100    return codecs.map(function (codec) {
46101      return codec.replace(/avc1\.(\d+)\.(\d+)/i, function (orig, profile, avcLevel) {
46102        var profileHex = ('00' + Number(profile).toString(16)).slice(-2);
46103        var avcLevelHex = ('00' + Number(avcLevel).toString(16)).slice(-2);
46104        return 'avc1.' + profileHex + '00' + avcLevelHex;
46105      });
46106    });
46107  };
46108  /**
46109   * Parses a codec string to retrieve the number of codecs specified,
46110   * the video codec and object type indicator, and the audio profile.
46111   */
46112
46113
46114  var parseCodecs = function parseCodecs() {
46115    var codecs = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : '';
46116    var result = {
46117      codecCount: 0
46118    };
46119    var parsed = void 0;
46120    result.codecCount = codecs.split(',').length;
46121    result.codecCount = result.codecCount || 2; // parse the video codec
46122
46123    parsed = /(^|\s|,)+(avc[13])([^ ,]*)/i.exec(codecs);
46124
46125    if (parsed) {
46126      result.videoCodec = parsed[2];
46127      result.videoObjectTypeIndicator = parsed[3];
46128    } // parse the last field of the audio codec
46129
46130
46131    result.audioProfile = /(^|\s|,)+mp4a.[0-9A-Fa-f]+\.([0-9A-Fa-f]+)/i.exec(codecs);
46132    result.audioProfile = result.audioProfile && result.audioProfile[2];
46133    return result;
46134  };
46135  /**
46136   * Replace codecs in the codec string with the old apple-style `avc1.<dd>.<dd>` to the
46137   * standard `avc1.<hhhhhh>`.
46138   *
46139   * @param codecString {String} the codec string
46140   * @return {String} the codec string with old apple-style codecs replaced
46141   *
46142   * @private
46143   */
46144
46145
46146  var mapLegacyAvcCodecs = function mapLegacyAvcCodecs(codecString) {
46147    return codecString.replace(/avc1\.(\d+)\.(\d+)/i, function (match) {
46148      return translateLegacyCodecs([match])[0];
46149    });
46150  };
46151  /**
46152   * Build a media mime-type string from a set of parameters
46153   * @param {String} type either 'audio' or 'video'
46154   * @param {String} container either 'mp2t' or 'mp4'
46155   * @param {Array} codecs an array of codec strings to add
46156   * @return {String} a valid media mime-type
46157   */
46158
46159
46160  var makeMimeTypeString = function makeMimeTypeString(type, container, codecs) {
46161    // The codecs array is filtered so that falsey values are
46162    // dropped and don't cause Array#join to create spurious
46163    // commas
46164    return type + '/' + container + '; codecs="' + codecs.filter(function (c) {
46165      return !!c;
46166    }).join(', ') + '"';
46167  };
46168  /**
46169   * Returns the type container based on information in the playlist
46170   * @param {Playlist} media the current media playlist
46171   * @return {String} a valid media container type
46172   */
46173
46174
46175  var getContainerType = function getContainerType(media) {
46176    // An initialization segment means the media playlist is an iframe
46177    // playlist or is using the mp4 container. We don't currently
46178    // support iframe playlists, so assume this is signalling mp4
46179    // fragments.
46180    if (media.segments && media.segments.length && media.segments[0].map) {
46181      return 'mp4';
46182    }
46183
46184    return 'mp2t';
46185  };
46186  /**
46187   * Returns a set of codec strings parsed from the playlist or the default
46188   * codec strings if no codecs were specified in the playlist
46189   * @param {Playlist} media the current media playlist
46190   * @return {Object} an object with the video and audio codecs
46191   */
46192
46193
46194  var getCodecs = function getCodecs(media) {
46195    // if the codecs were explicitly specified, use them instead of the
46196    // defaults
46197    var mediaAttributes = media.attributes || {};
46198
46199    if (mediaAttributes.CODECS) {
46200      return parseCodecs(mediaAttributes.CODECS);
46201    }
46202
46203    return defaultCodecs;
46204  };
46205
46206  var audioProfileFromDefault = function audioProfileFromDefault(master, audioGroupId) {
46207    if (!master.mediaGroups.AUDIO || !audioGroupId) {
46208      return null;
46209    }
46210
46211    var audioGroup = master.mediaGroups.AUDIO[audioGroupId];
46212
46213    if (!audioGroup) {
46214      return null;
46215    }
46216
46217    for (var name in audioGroup) {
46218      var audioType = audioGroup[name];
46219
46220      if (audioType["default"] && audioType.playlists) {
46221        // codec should be the same for all playlists within the audio type
46222        return parseCodecs(audioType.playlists[0].attributes.CODECS).audioProfile;
46223      }
46224    }
46225
46226    return null;
46227  };
46228  /**
46229   * Calculates the MIME type strings for a working configuration of
46230   * SourceBuffers to play variant streams in a master playlist. If
46231   * there is no possible working configuration, an empty array will be
46232   * returned.
46233   *
46234   * @param master {Object} the m3u8 object for the master playlist
46235   * @param media {Object} the m3u8 object for the variant playlist
46236   * @return {Array} the MIME type strings. If the array has more than
46237   * one entry, the first element should be applied to the video
46238   * SourceBuffer and the second to the audio SourceBuffer.
46239   *
46240   * @private
46241   */
46242
46243
46244  var mimeTypesForPlaylist = function mimeTypesForPlaylist(master, media) {
46245    var containerType = getContainerType(media);
46246    var codecInfo = getCodecs(media);
46247    var mediaAttributes = media.attributes || {}; // Default condition for a traditional HLS (no demuxed audio/video)
46248
46249    var isMuxed = true;
46250    var isMaat = false;
46251
46252    if (!media) {
46253      // Not enough information
46254      return [];
46255    }
46256
46257    if (master.mediaGroups.AUDIO && mediaAttributes.AUDIO) {
46258      var audioGroup = master.mediaGroups.AUDIO[mediaAttributes.AUDIO]; // Handle the case where we are in a multiple-audio track scenario
46259
46260      if (audioGroup) {
46261        isMaat = true; // Start with the everything demuxed then...
46262
46263        isMuxed = false; // ...check to see if any audio group tracks are muxed (ie. lacking a uri)
46264
46265        for (var groupId in audioGroup) {
46266          // either a uri is present (if the case of HLS and an external playlist), or
46267          // playlists is present (in the case of DASH where we don't have external audio
46268          // playlists)
46269          if (!audioGroup[groupId].uri && !audioGroup[groupId].playlists) {
46270            isMuxed = true;
46271            break;
46272          }
46273        }
46274      }
46275    } // HLS with multiple-audio tracks must always get an audio codec.
46276    // Put another way, there is no way to have a video-only multiple-audio HLS!
46277
46278
46279    if (isMaat && !codecInfo.audioProfile) {
46280      if (!isMuxed) {
46281        // It is possible for codecs to be specified on the audio media group playlist but
46282        // not on the rendition playlist. This is mostly the case for DASH, where audio and
46283        // video are always separate (and separately specified).
46284        codecInfo.audioProfile = audioProfileFromDefault(master, mediaAttributes.AUDIO);
46285      }
46286
46287      if (!codecInfo.audioProfile) {
46288        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)');
46289        codecInfo.audioProfile = defaultCodecs.audioProfile;
46290      }
46291    } // Generate the final codec strings from the codec object generated above
46292
46293
46294    var codecStrings = {};
46295
46296    if (codecInfo.videoCodec) {
46297      codecStrings.video = '' + codecInfo.videoCodec + codecInfo.videoObjectTypeIndicator;
46298    }
46299
46300    if (codecInfo.audioProfile) {
46301      codecStrings.audio = 'mp4a.40.' + codecInfo.audioProfile;
46302    } // Finally, make and return an array with proper mime-types depending on
46303    // the configuration
46304
46305
46306    var justAudio = makeMimeTypeString('audio', containerType, [codecStrings.audio]);
46307    var justVideo = makeMimeTypeString('video', containerType, [codecStrings.video]);
46308    var bothVideoAudio = makeMimeTypeString('video', containerType, [codecStrings.video, codecStrings.audio]);
46309
46310    if (isMaat) {
46311      if (!isMuxed && codecStrings.video) {
46312        return [justVideo, justAudio];
46313      }
46314
46315      if (!isMuxed && !codecStrings.video) {
46316        // There is no muxed content and no video codec string, so this is an audio only
46317        // stream with alternate audio.
46318        return [justAudio, justAudio];
46319      } // There exists the possiblity that this will return a `video/container`
46320      // mime-type for the first entry in the array even when there is only audio.
46321      // This doesn't appear to be a problem and simplifies the code.
46322
46323
46324      return [bothVideoAudio, justAudio];
46325    } // If there is no video codec at all, always just return a single
46326    // audio/<container> mime-type
46327
46328
46329    if (!codecStrings.video) {
46330      return [justAudio];
46331    } // When not using separate audio media groups, audio and video is
46332    // *always* muxed
46333
46334
46335    return [bothVideoAudio];
46336  };
46337  /**
46338   * Parse a content type header into a type and parameters
46339   * object
46340   *
46341   * @param {String} type the content type header
46342   * @return {Object} the parsed content-type
46343   * @private
46344   */
46345
46346
46347  var parseContentType = function parseContentType(type) {
46348    var object = {
46349      type: '',
46350      parameters: {}
46351    };
46352    var parameters = type.trim().split(';'); // first parameter should always be content-type
46353
46354    object.type = parameters.shift().trim();
46355    parameters.forEach(function (parameter) {
46356      var pair = parameter.trim().split('=');
46357
46358      if (pair.length > 1) {
46359        var name = pair[0].replace(/"/g, '').trim();
46360        var value = pair[1].replace(/"/g, '').trim();
46361        object.parameters[name] = value;
46362      }
46363    });
46364    return object;
46365  };
46366  /**
46367   * Check if a codec string refers to an audio codec.
46368   *
46369   * @param {String} codec codec string to check
46370   * @return {Boolean} if this is an audio codec
46371   * @private
46372   */
46373
46374
46375  var isAudioCodec = function isAudioCodec(codec) {
46376    return /mp4a\.\d+.\d+/i.test(codec);
46377  };
46378  /**
46379   * Check if a codec string refers to a video codec.
46380   *
46381   * @param {String} codec codec string to check
46382   * @return {Boolean} if this is a video codec
46383   * @private
46384   */
46385
46386
46387  var isVideoCodec = function isVideoCodec(codec) {
46388    return /avc1\.[\da-f]+/i.test(codec);
46389  };
46390  /**
46391   * Returns a list of gops in the buffer that have a pts value of 3 seconds or more in
46392   * front of current time.
46393   *
46394   * @param {Array} buffer
46395   *        The current buffer of gop information
46396   * @param {Number} currentTime
46397   *        The current time
46398   * @param {Double} mapping
46399   *        Offset to map display time to stream presentation time
46400   * @return {Array}
46401   *         List of gops considered safe to append over
46402   */
46403
46404
46405  var gopsSafeToAlignWith = function gopsSafeToAlignWith(buffer, currentTime, mapping) {
46406    if (typeof currentTime === 'undefined' || currentTime === null || !buffer.length) {
46407      return [];
46408    } // pts value for current time + 3 seconds to give a bit more wiggle room
46409
46410
46411    var currentTimePts = Math.ceil((currentTime - mapping + 3) * 90000);
46412    var i = void 0;
46413
46414    for (i = 0; i < buffer.length; i++) {
46415      if (buffer[i].pts > currentTimePts) {
46416        break;
46417      }
46418    }
46419
46420    return buffer.slice(i);
46421  };
46422  /**
46423   * Appends gop information (timing and byteLength) received by the transmuxer for the
46424   * gops appended in the last call to appendBuffer
46425   *
46426   * @param {Array} buffer
46427   *        The current buffer of gop information
46428   * @param {Array} gops
46429   *        List of new gop information
46430   * @param {boolean} replace
46431   *        If true, replace the buffer with the new gop information. If false, append the
46432   *        new gop information to the buffer in the right location of time.
46433   * @return {Array}
46434   *         Updated list of gop information
46435   */
46436
46437
46438  var updateGopBuffer = function updateGopBuffer(buffer, gops, replace) {
46439    if (!gops.length) {
46440      return buffer;
46441    }
46442
46443    if (replace) {
46444      // If we are in safe append mode, then completely overwrite the gop buffer
46445      // with the most recent appeneded data. This will make sure that when appending
46446      // future segments, we only try to align with gops that are both ahead of current
46447      // time and in the last segment appended.
46448      return gops.slice();
46449    }
46450
46451    var start = gops[0].pts;
46452    var i = 0;
46453
46454    for (i; i < buffer.length; i++) {
46455      if (buffer[i].pts >= start) {
46456        break;
46457      }
46458    }
46459
46460    return buffer.slice(0, i).concat(gops);
46461  };
46462  /**
46463   * Removes gop information in buffer that overlaps with provided start and end
46464   *
46465   * @param {Array} buffer
46466   *        The current buffer of gop information
46467   * @param {Double} start
46468   *        position to start the remove at
46469   * @param {Double} end
46470   *        position to end the remove at
46471   * @param {Double} mapping
46472   *        Offset to map display time to stream presentation time
46473   */
46474
46475
46476  var removeGopBuffer = function removeGopBuffer(buffer, start, end, mapping) {
46477    var startPts = Math.ceil((start - mapping) * 90000);
46478    var endPts = Math.ceil((end - mapping) * 90000);
46479    var updatedBuffer = buffer.slice();
46480    var i = buffer.length;
46481
46482    while (i--) {
46483      if (buffer[i].pts <= endPts) {
46484        break;
46485      }
46486    }
46487
46488    if (i === -1) {
46489      // no removal because end of remove range is before start of buffer
46490      return updatedBuffer;
46491    }
46492
46493    var j = i + 1;
46494
46495    while (j--) {
46496      if (buffer[j].pts <= startPts) {
46497        break;
46498      }
46499    } // clamp remove range start to 0 index
46500
46501
46502    j = Math.max(j, 0);
46503    updatedBuffer.splice(j, i - j + 1);
46504    return updatedBuffer;
46505  };
46506
46507  var buffered = function buffered(videoBuffer, audioBuffer, audioDisabled) {
46508    var start = null;
46509    var end = null;
46510    var arity = 0;
46511    var extents = [];
46512    var ranges = []; // neither buffer has been created yet
46513
46514    if (!videoBuffer && !audioBuffer) {
46515      return videojs$1.createTimeRange();
46516    } // only one buffer is configured
46517
46518
46519    if (!videoBuffer) {
46520      return audioBuffer.buffered;
46521    }
46522
46523    if (!audioBuffer) {
46524      return videoBuffer.buffered;
46525    } // both buffers are configured
46526
46527
46528    if (audioDisabled) {
46529      return videoBuffer.buffered;
46530    } // both buffers are empty
46531
46532
46533    if (videoBuffer.buffered.length === 0 && audioBuffer.buffered.length === 0) {
46534      return videojs$1.createTimeRange();
46535    } // Handle the case where we have both buffers and create an
46536    // intersection of the two
46537
46538
46539    var videoBuffered = videoBuffer.buffered;
46540    var audioBuffered = audioBuffer.buffered;
46541    var count = videoBuffered.length; // A) Gather up all start and end times
46542
46543    while (count--) {
46544      extents.push({
46545        time: videoBuffered.start(count),
46546        type: 'start'
46547      });
46548      extents.push({
46549        time: videoBuffered.end(count),
46550        type: 'end'
46551      });
46552    }
46553
46554    count = audioBuffered.length;
46555
46556    while (count--) {
46557      extents.push({
46558        time: audioBuffered.start(count),
46559        type: 'start'
46560      });
46561      extents.push({
46562        time: audioBuffered.end(count),
46563        type: 'end'
46564      });
46565    } // B) Sort them by time
46566
46567
46568    extents.sort(function (a, b) {
46569      return a.time - b.time;
46570    }); // C) Go along one by one incrementing arity for start and decrementing
46571    //    arity for ends
46572
46573    for (count = 0; count < extents.length; count++) {
46574      if (extents[count].type === 'start') {
46575        arity++; // D) If arity is ever incremented to 2 we are entering an
46576        //    overlapping range
46577
46578        if (arity === 2) {
46579          start = extents[count].time;
46580        }
46581      } else if (extents[count].type === 'end') {
46582        arity--; // E) If arity is ever decremented to 1 we leaving an
46583        //    overlapping range
46584
46585        if (arity === 1) {
46586          end = extents[count].time;
46587        }
46588      } // F) Record overlapping ranges
46589
46590
46591      if (start !== null && end !== null) {
46592        ranges.push([start, end]);
46593        start = null;
46594        end = null;
46595      }
46596    }
46597
46598    return videojs$1.createTimeRanges(ranges);
46599  };
46600  /**
46601   * @file virtual-source-buffer.js
46602   */
46603
46604
46605  var ONE_SECOND_IN_TS$2 = 90000; // We create a wrapper around the SourceBuffer so that we can manage the
46606  // state of the `updating` property manually. We have to do this because
46607  // Firefox changes `updating` to false long before triggering `updateend`
46608  // events and that was causing strange problems in videojs-contrib-hls
46609
46610  var makeWrappedSourceBuffer = function makeWrappedSourceBuffer(mediaSource, mimeType) {
46611    var sourceBuffer = mediaSource.addSourceBuffer(mimeType);
46612    var wrapper = Object.create(null);
46613    wrapper.updating = false;
46614    wrapper.realBuffer_ = sourceBuffer;
46615
46616    var _loop = function _loop(key) {
46617      if (typeof sourceBuffer[key] === 'function') {
46618        wrapper[key] = function () {
46619          return sourceBuffer[key].apply(sourceBuffer, arguments);
46620        };
46621      } else if (typeof wrapper[key] === 'undefined') {
46622        Object.defineProperty(wrapper, key, {
46623          get: function get$$1() {
46624            return sourceBuffer[key];
46625          },
46626          set: function set$$1(v) {
46627            return sourceBuffer[key] = v;
46628          }
46629        });
46630      }
46631    };
46632
46633    for (var key in sourceBuffer) {
46634      _loop(key);
46635    }
46636
46637    return wrapper;
46638  };
46639  /**
46640   * VirtualSourceBuffers exist so that we can transmux non native formats
46641   * into a native format, but keep the same api as a native source buffer.
46642   * It creates a transmuxer, that works in its own thread (a web worker) and
46643   * that transmuxer muxes the data into a native format. VirtualSourceBuffer will
46644   * then send all of that data to the naive sourcebuffer so that it is
46645   * indestinguishable from a natively supported format.
46646   *
46647   * @param {HtmlMediaSource} mediaSource the parent mediaSource
46648   * @param {Array} codecs array of codecs that we will be dealing with
46649   * @class VirtualSourceBuffer
46650   * @extends video.js.EventTarget
46651   */
46652
46653
46654  var VirtualSourceBuffer = function (_videojs$EventTarget) {
46655    inherits$1(VirtualSourceBuffer, _videojs$EventTarget);
46656
46657    function VirtualSourceBuffer(mediaSource, codecs) {
46658      classCallCheck$1(this, VirtualSourceBuffer);
46659
46660      var _this = possibleConstructorReturn$1(this, (VirtualSourceBuffer.__proto__ || Object.getPrototypeOf(VirtualSourceBuffer)).call(this, videojs$1.EventTarget));
46661
46662      _this.timestampOffset_ = 0;
46663      _this.pendingBuffers_ = [];
46664      _this.bufferUpdating_ = false;
46665      _this.mediaSource_ = mediaSource;
46666      _this.codecs_ = codecs;
46667      _this.audioCodec_ = null;
46668      _this.videoCodec_ = null;
46669      _this.audioDisabled_ = false;
46670      _this.appendAudioInitSegment_ = true;
46671      _this.gopBuffer_ = [];
46672      _this.timeMapping_ = 0;
46673      _this.safeAppend_ = videojs$1.browser.IE_VERSION >= 11;
46674      var options = {
46675        remux: false,
46676        alignGopsAtEnd: _this.safeAppend_
46677      };
46678
46679      _this.codecs_.forEach(function (codec) {
46680        if (isAudioCodec(codec)) {
46681          _this.audioCodec_ = codec;
46682        } else if (isVideoCodec(codec)) {
46683          _this.videoCodec_ = codec;
46684        }
46685      }); // append muxed segments to their respective native buffers as
46686      // soon as they are available
46687
46688
46689      _this.transmuxer_ = new TransmuxWorker();
46690
46691      _this.transmuxer_.postMessage({
46692        action: 'init',
46693        options: options
46694      });
46695
46696      _this.transmuxer_.onmessage = function (event) {
46697        if (event.data.action === 'data') {
46698          return _this.data_(event);
46699        }
46700
46701        if (event.data.action === 'done') {
46702          return _this.done_(event);
46703        }
46704
46705        if (event.data.action === 'gopInfo') {
46706          return _this.appendGopInfo_(event);
46707        }
46708
46709        if (event.data.action === 'videoSegmentTimingInfo') {
46710          return _this.videoSegmentTimingInfo_(event.data.videoSegmentTimingInfo);
46711        }
46712      }; // this timestampOffset is a property with the side-effect of resetting
46713      // baseMediaDecodeTime in the transmuxer on the setter
46714
46715
46716      Object.defineProperty(_this, 'timestampOffset', {
46717        get: function get$$1() {
46718          return this.timestampOffset_;
46719        },
46720        set: function set$$1(val) {
46721          if (typeof val === 'number' && val >= 0) {
46722            this.timestampOffset_ = val;
46723            this.appendAudioInitSegment_ = true; // reset gop buffer on timestampoffset as this signals a change in timeline
46724
46725            this.gopBuffer_.length = 0;
46726            this.timeMapping_ = 0; // We have to tell the transmuxer to set the baseMediaDecodeTime to
46727            // the desired timestampOffset for the next segment
46728
46729            this.transmuxer_.postMessage({
46730              action: 'setTimestampOffset',
46731              timestampOffset: val
46732            });
46733          }
46734        }
46735      }); // setting the append window affects both source buffers
46736
46737      Object.defineProperty(_this, 'appendWindowStart', {
46738        get: function get$$1() {
46739          return (this.videoBuffer_ || this.audioBuffer_).appendWindowStart;
46740        },
46741        set: function set$$1(start) {
46742          if (this.videoBuffer_) {
46743            this.videoBuffer_.appendWindowStart = start;
46744          }
46745
46746          if (this.audioBuffer_) {
46747            this.audioBuffer_.appendWindowStart = start;
46748          }
46749        }
46750      }); // this buffer is "updating" if either of its native buffers are
46751
46752      Object.defineProperty(_this, 'updating', {
46753        get: function get$$1() {
46754          return !!(this.bufferUpdating_ || !this.audioDisabled_ && this.audioBuffer_ && this.audioBuffer_.updating || this.videoBuffer_ && this.videoBuffer_.updating);
46755        }
46756      }); // the buffered property is the intersection of the buffered
46757      // ranges of the native source buffers
46758
46759      Object.defineProperty(_this, 'buffered', {
46760        get: function get$$1() {
46761          return buffered(this.videoBuffer_, this.audioBuffer_, this.audioDisabled_);
46762        }
46763      });
46764      return _this;
46765    }
46766    /**
46767     * When we get a data event from the transmuxer
46768     * we call this function and handle the data that
46769     * was sent to us
46770     *
46771     * @private
46772     * @param {Event} event the data event from the transmuxer
46773     */
46774
46775
46776    createClass$1(VirtualSourceBuffer, [{
46777      key: 'data_',
46778      value: function data_(event) {
46779        var segment = event.data.segment; // Cast ArrayBuffer to TypedArray
46780
46781        segment.data = new Uint8Array(segment.data, event.data.byteOffset, event.data.byteLength);
46782        segment.initSegment = new Uint8Array(segment.initSegment.data, segment.initSegment.byteOffset, segment.initSegment.byteLength);
46783        createTextTracksIfNecessary(this, this.mediaSource_, segment); // Add the segments to the pendingBuffers array
46784
46785        this.pendingBuffers_.push(segment);
46786        return;
46787      }
46788      /**
46789       * When we get a done event from the transmuxer
46790       * we call this function and we process all
46791       * of the pending data that we have been saving in the
46792       * data_ function
46793       *
46794       * @private
46795       * @param {Event} event the done event from the transmuxer
46796       */
46797
46798    }, {
46799      key: 'done_',
46800      value: function done_(event) {
46801        // Don't process and append data if the mediaSource is closed
46802        if (this.mediaSource_.readyState === 'closed') {
46803          this.pendingBuffers_.length = 0;
46804          return;
46805        } // All buffers should have been flushed from the muxer
46806        // start processing anything we have received
46807
46808
46809        this.processPendingSegments_();
46810        return;
46811      }
46812    }, {
46813      key: 'videoSegmentTimingInfo_',
46814      value: function videoSegmentTimingInfo_(timingInfo) {
46815        var timingInfoInSeconds = {
46816          start: {
46817            decode: timingInfo.start.dts / ONE_SECOND_IN_TS$2,
46818            presentation: timingInfo.start.pts / ONE_SECOND_IN_TS$2
46819          },
46820          end: {
46821            decode: timingInfo.end.dts / ONE_SECOND_IN_TS$2,
46822            presentation: timingInfo.end.pts / ONE_SECOND_IN_TS$2
46823          },
46824          baseMediaDecodeTime: timingInfo.baseMediaDecodeTime / ONE_SECOND_IN_TS$2
46825        };
46826
46827        if (timingInfo.prependedContentDuration) {
46828          timingInfoInSeconds.prependedContentDuration = timingInfo.prependedContentDuration / ONE_SECOND_IN_TS$2;
46829        }
46830
46831        this.trigger({
46832          type: 'videoSegmentTimingInfo',
46833          videoSegmentTimingInfo: timingInfoInSeconds
46834        });
46835      }
46836      /**
46837       * Create our internal native audio/video source buffers and add
46838       * event handlers to them with the following conditions:
46839       * 1. they do not already exist on the mediaSource
46840       * 2. this VSB has a codec for them
46841       *
46842       * @private
46843       */
46844
46845    }, {
46846      key: 'createRealSourceBuffers_',
46847      value: function createRealSourceBuffers_() {
46848        var _this2 = this;
46849
46850        var types = ['audio', 'video'];
46851        types.forEach(function (type) {
46852          // Don't create a SourceBuffer of this type if we don't have a
46853          // codec for it
46854          if (!_this2[type + 'Codec_']) {
46855            return;
46856          } // Do nothing if a SourceBuffer of this type already exists
46857
46858
46859          if (_this2[type + 'Buffer_']) {
46860            return;
46861          }
46862
46863          var buffer = null; // If the mediasource already has a SourceBuffer for the codec
46864          // use that
46865
46866          if (_this2.mediaSource_[type + 'Buffer_']) {
46867            buffer = _this2.mediaSource_[type + 'Buffer_']; // In multiple audio track cases, the audio source buffer is disabled
46868            // on the main VirtualSourceBuffer by the HTMLMediaSource much earlier
46869            // than createRealSourceBuffers_ is called to create the second
46870            // VirtualSourceBuffer because that happens as a side-effect of
46871            // videojs-contrib-hls starting the audioSegmentLoader. As a result,
46872            // the audioBuffer is essentially "ownerless" and no one will toggle
vendor: 30,121 bytes, lines 46873-47698
46873            // the `updating` state back to false once the `updateend` event is received
46874            //
46875            // Setting `updating` to false manually will work around this
46876            // situation and allow work to continue
46877
46878            buffer.updating = false;
46879          } else {
46880            var codecProperty = type + 'Codec_';
46881            var mimeType = type + '/mp4;codecs="' + _this2[codecProperty] + '"';
46882            buffer = makeWrappedSourceBuffer(_this2.mediaSource_.nativeMediaSource_, mimeType);
46883            _this2.mediaSource_[type + 'Buffer_'] = buffer;
46884          }
46885
46886          _this2[type + 'Buffer_'] = buffer; // Wire up the events to the SourceBuffer
46887
46888          ['update', 'updatestart', 'updateend'].forEach(function (event) {
46889            buffer.addEventListener(event, function () {
46890              // if audio is disabled
46891              if (type === 'audio' && _this2.audioDisabled_) {
46892                return;
46893              }
46894
46895              if (event === 'updateend') {
46896                _this2[type + 'Buffer_'].updating = false;
46897              }
46898
46899              var shouldTrigger = types.every(function (t) {
46900                // skip checking audio's updating status if audio
46901                // is not enabled
46902                if (t === 'audio' && _this2.audioDisabled_) {
46903                  return true;
46904                } // if the other type is updating we don't trigger
46905
46906
46907                if (type !== t && _this2[t + 'Buffer_'] && _this2[t + 'Buffer_'].updating) {
46908                  return false;
46909                }
46910
46911                return true;
46912              });
46913
46914              if (shouldTrigger) {
46915                return _this2.trigger(event);
46916              }
46917            });
46918          });
46919        });
46920      }
46921      /**
46922       * Emulate the native mediasource function, but our function will
46923       * send all of the proposed segments to the transmuxer so that we
46924       * can transmux them before we append them to our internal
46925       * native source buffers in the correct format.
46926       *
46927       * @link https://developer.mozilla.org/en-US/docs/Web/API/SourceBuffer/appendBuffer
46928       * @param {Uint8Array} segment the segment to append to the buffer
46929       */
46930
46931    }, {
46932      key: 'appendBuffer',
46933      value: function appendBuffer(segment) {
46934        // Start the internal "updating" state
46935        this.bufferUpdating_ = true;
46936
46937        if (this.audioBuffer_ && this.audioBuffer_.buffered.length) {
46938          var audioBuffered = this.audioBuffer_.buffered;
46939          this.transmuxer_.postMessage({
46940            action: 'setAudioAppendStart',
46941            appendStart: audioBuffered.end(audioBuffered.length - 1)
46942          });
46943        }
46944
46945        if (this.videoBuffer_) {
46946          this.transmuxer_.postMessage({
46947            action: 'alignGopsWith',
46948            gopsToAlignWith: gopsSafeToAlignWith(this.gopBuffer_, this.mediaSource_.player_ ? this.mediaSource_.player_.currentTime() : null, this.timeMapping_)
46949          });
46950        }
46951
46952        this.transmuxer_.postMessage({
46953          action: 'push',
46954          // Send the typed-array of data as an ArrayBuffer so that
46955          // it can be sent as a "Transferable" and avoid the costly
46956          // memory copy
46957          data: segment.buffer,
46958          // To recreate the original typed-array, we need information
46959          // about what portion of the ArrayBuffer it was a view into
46960          byteOffset: segment.byteOffset,
46961          byteLength: segment.byteLength
46962        }, [segment.buffer]);
46963        this.transmuxer_.postMessage({
46964          action: 'flush'
46965        });
46966      }
46967      /**
46968       * Appends gop information (timing and byteLength) received by the transmuxer for the
46969       * gops appended in the last call to appendBuffer
46970       *
46971       * @param {Event} event
46972       *        The gopInfo event from the transmuxer
46973       * @param {Array} event.data.gopInfo
46974       *        List of gop info to append
46975       */
46976
46977    }, {
46978      key: 'appendGopInfo_',
46979      value: function appendGopInfo_(event) {
46980        this.gopBuffer_ = updateGopBuffer(this.gopBuffer_, event.data.gopInfo, this.safeAppend_);
46981      }
46982      /**
46983       * Emulate the native mediasource function and remove parts
46984       * of the buffer from any of our internal buffers that exist
46985       *
46986       * @link https://developer.mozilla.org/en-US/docs/Web/API/SourceBuffer/remove
46987       * @param {Double} start position to start the remove at
46988       * @param {Double} end position to end the remove at
46989       */
46990
46991    }, {
46992      key: 'remove',
46993      value: function remove(start, end) {
46994        if (this.videoBuffer_) {
46995          this.videoBuffer_.updating = true;
46996          this.videoBuffer_.remove(start, end);
46997          this.gopBuffer_ = removeGopBuffer(this.gopBuffer_, start, end, this.timeMapping_);
46998        }
46999
47000        if (!this.audioDisabled_ && this.audioBuffer_) {
47001          this.audioBuffer_.updating = true;
47002          this.audioBuffer_.remove(start, end);
47003        } // Remove Metadata Cues (id3)
47004
47005
47006        removeCuesFromTrack(start, end, this.metadataTrack_); // Remove Any Captions
47007
47008        if (this.inbandTextTracks_) {
47009          for (var track in this.inbandTextTracks_) {
47010            removeCuesFromTrack(start, end, this.inbandTextTracks_[track]);
47011          }
47012        }
47013      }
47014      /**
47015       * Process any segments that the muxer has output
47016       * Concatenate segments together based on type and append them into
47017       * their respective sourceBuffers
47018       *
47019       * @private
47020       */
47021
47022    }, {
47023      key: 'processPendingSegments_',
47024      value: function processPendingSegments_() {
47025        var sortedSegments = {
47026          video: {
47027            segments: [],
47028            bytes: 0
47029          },
47030          audio: {
47031            segments: [],
47032            bytes: 0
47033          },
47034          captions: [],
47035          metadata: []
47036        };
47037
47038        if (!this.pendingBuffers_.length) {
47039          // We are no longer in the internal "updating" state
47040          this.trigger('updateend');
47041          this.bufferUpdating_ = false;
47042          return;
47043        } // Sort segments into separate video/audio arrays and
47044        // keep track of their total byte lengths
47045
47046
47047        sortedSegments = this.pendingBuffers_.reduce(function (segmentObj, segment) {
47048          var type = segment.type;
47049          var data = segment.data;
47050          var initSegment = segment.initSegment;
47051          segmentObj[type].segments.push(data);
47052          segmentObj[type].bytes += data.byteLength;
47053          segmentObj[type].initSegment = initSegment; // Gather any captions into a single array
47054
47055          if (segment.captions) {
47056            segmentObj.captions = segmentObj.captions.concat(segment.captions);
47057          }
47058
47059          if (segment.info) {
47060            segmentObj[type].info = segment.info;
47061          } // Gather any metadata into a single array
47062
47063
47064          if (segment.metadata) {
47065            segmentObj.metadata = segmentObj.metadata.concat(segment.metadata);
47066          }
47067
47068          return segmentObj;
47069        }, sortedSegments); // Create the real source buffers if they don't exist by now since we
47070        // finally are sure what tracks are contained in the source
47071
47072        if (!this.videoBuffer_ && !this.audioBuffer_) {
47073          // Remove any codecs that may have been specified by default but
47074          // are no longer applicable now
47075          if (sortedSegments.video.bytes === 0) {
47076            this.videoCodec_ = null;
47077          }
47078
47079          if (sortedSegments.audio.bytes === 0) {
47080            this.audioCodec_ = null;
47081          }
47082
47083          this.createRealSourceBuffers_();
47084        }
47085
47086        if (sortedSegments.audio.info) {
47087          this.mediaSource_.trigger({
47088            type: 'audioinfo',
47089            info: sortedSegments.audio.info
47090          });
47091        }
47092
47093        if (sortedSegments.video.info) {
47094          this.mediaSource_.trigger({
47095            type: 'videoinfo',
47096            info: sortedSegments.video.info
47097          });
47098        }
47099
47100        if (this.appendAudioInitSegment_) {
47101          if (!this.audioDisabled_ && this.audioBuffer_) {
47102            sortedSegments.audio.segments.unshift(sortedSegments.audio.initSegment);
47103            sortedSegments.audio.bytes += sortedSegments.audio.initSegment.byteLength;
47104          }
47105
47106          this.appendAudioInitSegment_ = false;
47107        }
47108
47109        var triggerUpdateend = false; // Merge multiple video and audio segments into one and append
47110
47111        if (this.videoBuffer_ && sortedSegments.video.bytes) {
47112          sortedSegments.video.segments.unshift(sortedSegments.video.initSegment);
47113          sortedSegments.video.bytes += sortedSegments.video.initSegment.byteLength;
47114          this.concatAndAppendSegments_(sortedSegments.video, this.videoBuffer_);
47115        } else if (this.videoBuffer_ && (this.audioDisabled_ || !this.audioBuffer_)) {
47116          // The transmuxer did not return any bytes of video, meaning it was all trimmed
47117          // for gop alignment. Since we have a video buffer and audio is disabled, updateend
47118          // will never be triggered by this source buffer, which will cause contrib-hls
47119          // to be stuck forever waiting for updateend. If audio is not disabled, updateend
47120          // will be triggered by the audio buffer, which will be sent upwards since the video
47121          // buffer will not be in an updating state.
47122          triggerUpdateend = true;
47123        } // Add text-track data for all
47124
47125
47126        addTextTrackData(this, sortedSegments.captions, sortedSegments.metadata);
47127
47128        if (!this.audioDisabled_ && this.audioBuffer_) {
47129          this.concatAndAppendSegments_(sortedSegments.audio, this.audioBuffer_);
47130        }
47131
47132        this.pendingBuffers_.length = 0;
47133
47134        if (triggerUpdateend) {
47135          this.trigger('updateend');
47136        } // We are no longer in the internal "updating" state
47137
47138
47139        this.bufferUpdating_ = false;
47140      }
47141      /**
47142       * Combine all segments into a single Uint8Array and then append them
47143       * to the destination buffer
47144       *
47145       * @param {Object} segmentObj
47146       * @param {SourceBuffer} destinationBuffer native source buffer to append data to
47147       * @private
47148       */
47149
47150    }, {
47151      key: 'concatAndAppendSegments_',
47152      value: function concatAndAppendSegments_(segmentObj, destinationBuffer) {
47153        var offset = 0;
47154        var tempBuffer = void 0;
47155
47156        if (segmentObj.bytes) {
47157          tempBuffer = new Uint8Array(segmentObj.bytes); // Combine the individual segments into one large typed-array
47158
47159          segmentObj.segments.forEach(function (segment) {
47160            tempBuffer.set(segment, offset);
47161            offset += segment.byteLength;
47162          });
47163
47164          try {
47165            destinationBuffer.updating = true;
47166            destinationBuffer.appendBuffer(tempBuffer);
47167          } catch (error) {
47168            if (this.mediaSource_.player_) {
47169              this.mediaSource_.player_.error({
47170                code: -3,
47171                type: 'APPEND_BUFFER_ERR',
47172                message: error.message,
47173                originalError: error
47174              });
47175            }
47176          }
47177        }
47178      }
47179      /**
47180       * Emulate the native mediasource function. abort any soureBuffer
47181       * actions and throw out any un-appended data.
47182       *
47183       * @link https://developer.mozilla.org/en-US/docs/Web/API/SourceBuffer/abort
47184       */
47185
47186    }, {
47187      key: 'abort',
47188      value: function abort() {
47189        if (this.videoBuffer_) {
47190          this.videoBuffer_.abort();
47191        }
47192
47193        if (!this.audioDisabled_ && this.audioBuffer_) {
47194          this.audioBuffer_.abort();
47195        }
47196
47197        if (this.transmuxer_) {
47198          this.transmuxer_.postMessage({
47199            action: 'reset'
47200          });
47201        }
47202
47203        this.pendingBuffers_.length = 0;
47204        this.bufferUpdating_ = false;
47205      }
47206    }]);
47207    return VirtualSourceBuffer;
47208  }(videojs$1.EventTarget);
47209  /**
47210   * @file html-media-source.js
47211   */
47212
47213  /**
47214   * Our MediaSource implementation in HTML, mimics native
47215   * MediaSource where/if possible.
47216   *
47217   * @link https://developer.mozilla.org/en-US/docs/Web/API/MediaSource
47218   * @class HtmlMediaSource
47219   * @extends videojs.EventTarget
47220   */
47221
47222
47223  var HtmlMediaSource = function (_videojs$EventTarget) {
47224    inherits$1(HtmlMediaSource, _videojs$EventTarget);
47225
47226    function HtmlMediaSource() {
47227      classCallCheck$1(this, HtmlMediaSource);
47228
47229      var _this = possibleConstructorReturn$1(this, (HtmlMediaSource.__proto__ || Object.getPrototypeOf(HtmlMediaSource)).call(this));
47230
47231      var property = void 0;
47232      _this.nativeMediaSource_ = new window$1.MediaSource(); // delegate to the native MediaSource's methods by default
47233
47234      for (property in _this.nativeMediaSource_) {
47235        if (!(property in HtmlMediaSource.prototype) && typeof _this.nativeMediaSource_[property] === 'function') {
47236          _this[property] = _this.nativeMediaSource_[property].bind(_this.nativeMediaSource_);
47237        }
47238      } // emulate `duration` and `seekable` until seeking can be
47239      // handled uniformly for live streams
47240      // see https://github.com/w3c/media-source/issues/5
47241
47242
47243      _this.duration_ = NaN;
47244      Object.defineProperty(_this, 'duration', {
47245        get: function get$$1() {
47246          if (this.duration_ === Infinity) {
47247            return this.duration_;
47248          }
47249
47250          return this.nativeMediaSource_.duration;
47251        },
47252        set: function set$$1(duration) {
47253          this.duration_ = duration;
47254
47255          if (duration !== Infinity) {
47256            this.nativeMediaSource_.duration = duration;
47257            return;
47258          }
47259        }
47260      });
47261      Object.defineProperty(_this, 'seekable', {
47262        get: function get$$1() {
47263          if (this.duration_ === Infinity) {
47264            return videojs$1.createTimeRanges([[0, this.nativeMediaSource_.duration]]);
47265          }
47266
47267          return this.nativeMediaSource_.seekable;
47268        }
47269      });
47270      Object.defineProperty(_this, 'readyState', {
47271        get: function get$$1() {
47272          return this.nativeMediaSource_.readyState;
47273        }
47274      });
47275      Object.defineProperty(_this, 'activeSourceBuffers', {
47276        get: function get$$1() {
47277          return this.activeSourceBuffers_;
47278        }
47279      }); // the list of virtual and native SourceBuffers created by this
47280      // MediaSource
47281
47282      _this.sourceBuffers = [];
47283      _this.activeSourceBuffers_ = [];
47284      /**
47285       * update the list of active source buffers based upon various
47286       * imformation from HLS and video.js
47287       *
47288       * @private
47289       */
47290
47291      _this.updateActiveSourceBuffers_ = function () {
47292        // Retain the reference but empty the array
47293        _this.activeSourceBuffers_.length = 0; // If there is only one source buffer, then it will always be active and audio will
47294        // be disabled based on the codec of the source buffer
47295
47296        if (_this.sourceBuffers.length === 1) {
47297          var sourceBuffer = _this.sourceBuffers[0];
47298          sourceBuffer.appendAudioInitSegment_ = true;
47299          sourceBuffer.audioDisabled_ = !sourceBuffer.audioCodec_;
47300
47301          _this.activeSourceBuffers_.push(sourceBuffer);
47302
47303          return;
47304        } // There are 2 source buffers, a combined (possibly video only) source buffer and
47305        // and an audio only source buffer.
47306        // By default, the audio in the combined virtual source buffer is enabled
47307        // and the audio-only source buffer (if it exists) is disabled.
47308
47309
47310        var disableCombined = false;
47311        var disableAudioOnly = true; // TODO: maybe we can store the sourcebuffers on the track objects?
47312        // safari may do something like this
47313
47314        for (var i = 0; i < _this.player_.audioTracks().length; i++) {
47315          var track = _this.player_.audioTracks()[i];
47316
47317          if (track.enabled && track.kind !== 'main') {
47318            // The enabled track is an alternate audio track so disable the audio in
47319            // the combined source buffer and enable the audio-only source buffer.
47320            disableCombined = true;
47321            disableAudioOnly = false;
47322            break;
47323          }
47324        }
47325
47326        _this.sourceBuffers.forEach(function (sourceBuffer, index) {
47327          /* eslinst-disable */
47328          // TODO once codecs are required, we can switch to using the codecs to determine
47329          //      what stream is the video stream, rather than relying on videoTracks
47330
47331          /* eslinst-enable */
47332          sourceBuffer.appendAudioInitSegment_ = true;
47333
47334          if (sourceBuffer.videoCodec_ && sourceBuffer.audioCodec_) {
47335            // combined
47336            sourceBuffer.audioDisabled_ = disableCombined;
47337          } else if (sourceBuffer.videoCodec_ && !sourceBuffer.audioCodec_) {
47338            // If the "combined" source buffer is video only, then we do not want
47339            // disable the audio-only source buffer (this is mostly for demuxed
47340            // audio and video hls)
47341            sourceBuffer.audioDisabled_ = true;
47342            disableAudioOnly = false;
47343          } else if (!sourceBuffer.videoCodec_ && sourceBuffer.audioCodec_) {
47344            // audio only
47345            // In the case of audio only with alternate audio and disableAudioOnly is true
47346            // this means we want to disable the audio on the alternate audio sourcebuffer
47347            // but not the main "combined" source buffer. The "combined" source buffer is
47348            // always at index 0, so this ensures audio won't be disabled in both source
47349            // buffers.
47350            sourceBuffer.audioDisabled_ = index ? disableAudioOnly : !disableAudioOnly;
47351
47352            if (sourceBuffer.audioDisabled_) {
47353              return;
47354            }
47355          }
47356
47357          _this.activeSourceBuffers_.push(sourceBuffer);
47358        });
47359      };
47360
47361      _this.onPlayerMediachange_ = function () {
47362        _this.sourceBuffers.forEach(function (sourceBuffer) {
47363          sourceBuffer.appendAudioInitSegment_ = true;
47364        });
47365      };
47366
47367      _this.onHlsReset_ = function () {
47368        _this.sourceBuffers.forEach(function (sourceBuffer) {
47369          if (sourceBuffer.transmuxer_) {
47370            sourceBuffer.transmuxer_.postMessage({
47371              action: 'resetCaptions'
47372            });
47373          }
47374        });
47375      };
47376
47377      _this.onHlsSegmentTimeMapping_ = function (event) {
47378        _this.sourceBuffers.forEach(function (buffer) {
47379          return buffer.timeMapping_ = event.mapping;
47380        });
47381      }; // Re-emit MediaSource events on the polyfill
47382
47383
47384      ['sourceopen', 'sourceclose', 'sourceended'].forEach(function (eventName) {
47385        this.nativeMediaSource_.addEventListener(eventName, this.trigger.bind(this));
47386      }, _this); // capture the associated player when the MediaSource is
47387      // successfully attached
47388
47389      _this.on('sourceopen', function (event) {
47390        // Get the player this MediaSource is attached to
47391        var video = document.querySelector('[src="' + _this.url_ + '"]');
47392
47393        if (!video) {
47394          return;
47395        }
47396
47397        _this.player_ = videojs$1(video.parentNode);
47398
47399        if (!_this.player_) {
47400          return;
47401        } // hls-reset is fired by videojs.Hls on to the tech after the main SegmentLoader
47402        // resets its state and flushes the buffer
47403
47404
47405        _this.player_.tech_.on('hls-reset', _this.onHlsReset_); // hls-segment-time-mapping is fired by videojs.Hls on to the tech after the main
47406        // SegmentLoader inspects an MTS segment and has an accurate stream to display
47407        // time mapping
47408
47409
47410        _this.player_.tech_.on('hls-segment-time-mapping', _this.onHlsSegmentTimeMapping_);
47411
47412        if (_this.player_.audioTracks && _this.player_.audioTracks()) {
47413          _this.player_.audioTracks().on('change', _this.updateActiveSourceBuffers_);
47414
47415          _this.player_.audioTracks().on('addtrack', _this.updateActiveSourceBuffers_);
47416
47417          _this.player_.audioTracks().on('removetrack', _this.updateActiveSourceBuffers_);
47418        }
47419
47420        _this.player_.on('mediachange', _this.onPlayerMediachange_);
47421      });
47422
47423      _this.on('sourceended', function (event) {
47424        var duration = durationOfVideo(_this.duration);
47425
47426        for (var i = 0; i < _this.sourceBuffers.length; i++) {
47427          var sourcebuffer = _this.sourceBuffers[i];
47428          var cues = sourcebuffer.metadataTrack_ && sourcebuffer.metadataTrack_.cues;
47429
47430          if (cues && cues.length) {
47431            cues[cues.length - 1].endTime = duration;
47432          }
47433        }
47434      }); // explicitly terminate any WebWorkers that were created
47435      // by SourceHandlers
47436
47437
47438      _this.on('sourceclose', function (event) {
47439        this.sourceBuffers.forEach(function (sourceBuffer) {
47440          if (sourceBuffer.transmuxer_) {
47441            sourceBuffer.transmuxer_.terminate();
47442          }
47443        });
47444        this.sourceBuffers.length = 0;
47445
47446        if (!this.player_) {
47447          return;
47448        }
47449
47450        if (this.player_.audioTracks && this.player_.audioTracks()) {
47451          this.player_.audioTracks().off('change', this.updateActiveSourceBuffers_);
47452          this.player_.audioTracks().off('addtrack', this.updateActiveSourceBuffers_);
47453          this.player_.audioTracks().off('removetrack', this.updateActiveSourceBuffers_);
47454        } // We can only change this if the player hasn't been disposed of yet
47455        // because `off` eventually tries to use the el_ property. If it has
47456        // been disposed of, then don't worry about it because there are no
47457        // event handlers left to unbind anyway
47458
47459
47460        if (this.player_.el_) {
47461          this.player_.off('mediachange', this.onPlayerMediachange_);
47462        }
47463
47464        if (this.player_.tech_ && this.player_.tech_.el_) {
47465          this.player_.tech_.off('hls-reset', this.onHlsReset_);
47466          this.player_.tech_.off('hls-segment-time-mapping', this.onHlsSegmentTimeMapping_);
47467        }
47468      });
47469
47470      return _this;
47471    }
47472    /**
47473     * Add a range that that can now be seeked to.
47474     *
47475     * @param {Double} start where to start the addition
47476     * @param {Double} end where to end the addition
47477     * @private
47478     */
47479
47480
47481    createClass$1(HtmlMediaSource, [{
47482      key: 'addSeekableRange_',
47483      value: function addSeekableRange_(start, end) {
47484        var error = void 0;
47485
47486        if (this.duration !== Infinity) {
47487          error = new Error('MediaSource.addSeekableRange() can only be invoked ' + 'when the duration is Infinity');
47488          error.name = 'InvalidStateError';
47489          error.code = 11;
47490          throw error;
47491        }
47492
47493        if (end > this.nativeMediaSource_.duration || isNaN(this.nativeMediaSource_.duration)) {
47494          this.nativeMediaSource_.duration = end;
47495        }
47496      }
47497      /**
47498       * Add a source buffer to the media source.
47499       *
47500       * @link https://developer.mozilla.org/en-US/docs/Web/API/MediaSource/addSourceBuffer
47501       * @param {String} type the content-type of the content
47502       * @return {Object} the created source buffer
47503       */
47504
47505    }, {
47506      key: 'addSourceBuffer',
47507      value: function addSourceBuffer(type) {
47508        var buffer = void 0;
47509        var parsedType = parseContentType(type); // Create a VirtualSourceBuffer to transmux MPEG-2 transport
47510        // stream segments into fragmented MP4s
47511
47512        if (/^(video|audio)\/mp2t$/i.test(parsedType.type)) {
47513          var codecs = [];
47514
47515          if (parsedType.parameters && parsedType.parameters.codecs) {
47516            codecs = parsedType.parameters.codecs.split(',');
47517            codecs = translateLegacyCodecs(codecs);
47518            codecs = codecs.filter(function (codec) {
47519              return isAudioCodec(codec) || isVideoCodec(codec);
47520            });
47521          }
47522
47523          if (codecs.length === 0) {
47524            codecs = ['avc1.4d400d', 'mp4a.40.2'];
47525          }
47526
47527          buffer = new VirtualSourceBuffer(this, codecs);
47528
47529          if (this.sourceBuffers.length !== 0) {
47530            // If another VirtualSourceBuffer already exists, then we are creating a
47531            // SourceBuffer for an alternate audio track and therefore we know that
47532            // the source has both an audio and video track.
47533            // That means we should trigger the manual creation of the real
47534            // SourceBuffers instead of waiting for the transmuxer to return data
47535            this.sourceBuffers[0].createRealSourceBuffers_();
47536            buffer.createRealSourceBuffers_(); // Automatically disable the audio on the first source buffer if
47537            // a second source buffer is ever created
47538
47539            this.sourceBuffers[0].audioDisabled_ = true;
47540          }
47541        } else {
47542          // delegate to the native implementation
47543          buffer = this.nativeMediaSource_.addSourceBuffer(type);
47544        }
47545
47546        this.sourceBuffers.push(buffer);
47547        return buffer;
47548      }
47549    }]);
47550    return HtmlMediaSource;
47551  }(videojs$1.EventTarget);
47552  /**
47553   * @file videojs-contrib-media-sources.js
47554   */
47555
47556
47557  var urlCount = 0; // ------------
47558  // Media Source
47559  // ------------
47560  // store references to the media sources so they can be connected
47561  // to a video element (a swf object)
47562  // TODO: can we store this somewhere local to this module?
47563
47564  videojs$1.mediaSources = {};
47565  /**
47566   * Provide a method for a swf object to notify JS that a
47567   * media source is now open.
47568   *
47569   * @param {String} msObjectURL string referencing the MSE Object URL
47570   * @param {String} swfId the swf id
47571   */
47572
47573  var open = function open(msObjectURL, swfId) {
47574    var mediaSource = videojs$1.mediaSources[msObjectURL];
47575
47576    if (mediaSource) {
47577      mediaSource.trigger({
47578        type: 'sourceopen',
47579        swfId: swfId
47580      });
47581    } else {
47582      throw new Error('Media Source not found (Video.js)');
47583    }
47584  };
47585  /**
47586   * Check to see if the native MediaSource object exists and supports
47587   * an MP4 container with both H.264 video and AAC-LC audio.
47588   *
47589   * @return {Boolean} if  native media sources are supported
47590   */
47591
47592
47593  var supportsNativeMediaSources = function supportsNativeMediaSources() {
47594    return !!window$1.MediaSource && !!window$1.MediaSource.isTypeSupported && window$1.MediaSource.isTypeSupported('video/mp4;codecs="avc1.4d400d,mp4a.40.2"');
47595  };
47596  /**
47597   * An emulation of the MediaSource API so that we can support
47598   * native and non-native functionality. returns an instance of
47599   * HtmlMediaSource.
47600   *
47601   * @link https://developer.mozilla.org/en-US/docs/Web/API/MediaSource/MediaSource
47602   */
47603
47604
47605  var MediaSource = function MediaSource() {
47606    this.MediaSource = {
47607      open: open,
47608      supportsNativeMediaSources: supportsNativeMediaSources
47609    };
47610
47611    if (supportsNativeMediaSources()) {
47612      return new HtmlMediaSource();
47613    }
47614
47615    throw new Error('Cannot use create a virtual MediaSource for this video');
47616  };
47617
47618  MediaSource.open = open;
47619  MediaSource.supportsNativeMediaSources = supportsNativeMediaSources;
47620  /**
47621   * A wrapper around the native URL for our MSE object
47622   * implementation, this object is exposed under videojs.URL
47623   *
47624   * @link https://developer.mozilla.org/en-US/docs/Web/API/URL/URL
47625   */
47626
47627  var URL$1 = {
47628    /**
47629     * A wrapper around the native createObjectURL for our objects.
47630     * This function maps a native or emulated mediaSource to a blob
47631     * url so that it can be loaded into video.js
47632     *
47633     * @link https://developer.mozilla.org/en-US/docs/Web/API/URL/createObjectURL
47634     * @param {MediaSource} object the object to create a blob url to
47635     */
47636    createObjectURL: function createObjectURL(object) {
47637      var objectUrlPrefix = 'blob:vjs-media-source/';
47638      var url = void 0; // use the native MediaSource to generate an object URL
47639
47640      if (object instanceof HtmlMediaSource) {
47641        url = window$1.URL.createObjectURL(object.nativeMediaSource_);
47642        object.url_ = url;
47643        return url;
47644      } // if the object isn't an emulated MediaSource, delegate to the
47645      // native implementation
47646
47647
47648      if (!(object instanceof HtmlMediaSource)) {
47649        url = window$1.URL.createObjectURL(object);
47650        object.url_ = url;
47651        return url;
47652      } // build a URL that can be used to map back to the emulated
47653      // MediaSource
47654
47655
47656      url = objectUrlPrefix + urlCount;
47657      urlCount++; // setup the mapping back to object
47658
47659      videojs$1.mediaSources[url] = object;
47660      return url;
47661    }
47662  };
47663  videojs$1.MediaSource = MediaSource;
47664  videojs$1.URL = URL$1;
47665  var EventTarget$1$1 = videojs$1.EventTarget,
47666      mergeOptions$2 = videojs$1.mergeOptions;
47667  /**
47668   * Returns a new master manifest that is the result of merging an updated master manifest
47669   * into the original version.
47670   *
47671   * @param {Object} oldMaster
47672   *        The old parsed mpd object
47673   * @param {Object} newMaster
47674   *        The updated parsed mpd object
47675   * @return {Object}
47676   *         A new object representing the original master manifest with the updated media
47677   *         playlists merged in
47678   */
47679
47680  var updateMaster$1 = function updateMaster$$1(oldMaster, newMaster) {
47681    var noChanges = void 0;
47682    var update = mergeOptions$2(oldMaster, {
47683      // These are top level properties that can be updated
47684      duration: newMaster.duration,
47685      minimumUpdatePeriod: newMaster.minimumUpdatePeriod
47686    }); // First update the playlists in playlist list
47687
47688    for (var i = 0; i < newMaster.playlists.length; i++) {
47689      var playlistUpdate = updateMaster(update, newMaster.playlists[i]);
47690
47691      if (playlistUpdate) {
47692        update = playlistUpdate;
47693      } else {
47694        noChanges = true;
47695      }
47696    } // Then update media group playlists
47697
47698
47699    forEachMediaGroup(newMaster, function (properties, type, group, label) {
47700      if (properties.playlists && properties.playlists.length) {
47701        var uri = properties.playlists[0].uri;
47702
47703        var _playlistUpdate = updateMaster(update, properties.playlists[0]);
47704
47705        if (_playlistUpdate) {
47706          update = _playlistUpdate; // update the playlist reference within media groups
47707
47708          update.mediaGroups[type][group][label].playlists[0] = update.playlists[uri];
47709          noChanges = false;
47710        }
47711      }
47712    });
47713
47714    if (noChanges) {
47715      return null;
47716    }
47717
47718    return update;
47719  };
47720
47721  var generateSidxKey = function generateSidxKey(sidxInfo) {
47722    // should be non-inclusive
47723    var sidxByteRangeEnd = sidxInfo.byterange.offset + sidxInfo.byterange.length - 1;
47724    return sidxInfo.uri + '-' + sidxInfo.byterange.offset + '-' + sidxByteRangeEnd;
47725  }; // SIDX should be equivalent if the URI and byteranges of the SIDX match.
47726  // If the SIDXs have maps, the two maps should match,
47727  // both `a` and `b` missing SIDXs is considered matching.
47728  // If `a` or `b` but not both have a map, they aren't matching.
47729
47730
47731  var equivalentSidx = function equivalentSidx(a, b) {
47732    var neitherMap = Boolean(!a.map && !b.map);
47733    var equivalentMap = neitherMap || Boolean(a.map && b.map && a.map.byterange.offset === b.map.byterange.offset && a.map.byterange.length === b.map.byterange.length);
47734    return equivalentMap && a.uri === b.uri && a.byterange.offset === b.byterange.offset && a.byterange.length === b.byterange.length;
47735  }; // exported for testing
47736
47737
47738  var compareSidxEntry = function compareSidxEntry(playlists, oldSidxMapping) {
47739    var newSidxMapping = {};
47740
47741    for (var uri in playlists) {
47742      var playlist = playlists[uri];
47743      var currentSidxInfo = playlist.sidx;
47744
47745      if (currentSidxInfo) {
47746        var key = generateSidxKey(currentSidxInfo);
47747
47748        if (!oldSidxMapping[key]) {
47749          break;
47750        }
47751
47752        var savedSidxInfo = oldSidxMapping[key].sidxInfo;
47753
47754        if (equivalentSidx(savedSidxInfo, currentSidxInfo)) {
47755          newSidxMapping[key] = oldSidxMapping[key];
47756        }
47757      }
47758    }
47759
47760    return newSidxMapping;
47761  };
47762  /**
47763   *  A function that filters out changed items as they need to be requested separately.
47764   *
47765   *  The method is exported for testing
47766   *
47767   *  @param {Object} masterXml the mpd XML
47768   *  @param {string} srcUrl the mpd url
47769   *  @param {Date} clientOffset a time difference between server and client (passed through and not used)
47770   *  @param {Object} oldSidxMapping the SIDX to compare against
47771   */
47772
47773
47774  var filterChangedSidxMappings = function filterChangedSidxMappings(masterXml, srcUrl, clientOffset, oldSidxMapping) {
47775    // Don't pass current sidx mapping
47776    var master = parse(masterXml, {
47777      manifestUri: srcUrl,
47778      clientOffset: clientOffset
47779    });
47780    var videoSidx = compareSidxEntry(master.playlists, oldSidxMapping);
47781    var mediaGroupSidx = videoSidx;
47782    forEachMediaGroup(master, function (properties, mediaType, groupKey, labelKey) {
47783      if (properties.playlists && properties.playlists.length) {
47784        var playlists = properties.playlists;
47785        mediaGroupSidx = mergeOptions$2(mediaGroupSidx, compareSidxEntry(playlists, oldSidxMapping));
47786      }
47787    });
47788    return mediaGroupSidx;
47789  }; // exported for testing
47790
47791
47792  var requestSidx_ = function requestSidx_(sidxRange, playlist, xhr, options, finishProcessingFn) {
47793    var sidxInfo = {
47794      // resolve the segment URL relative to the playlist
47795      uri: resolveManifestRedirect(options.handleManifestRedirects, sidxRange.resolvedUri),
47796      // resolvedUri: sidxRange.resolvedUri,
47797      byterange: sidxRange.byterange,
47798      // the segment's playlist
47799      playlist: playlist
47800    };
47801    var sidxRequestOptions = videojs$1.mergeOptions(sidxInfo, {
47802      responseType: 'arraybuffer',
47803      headers: segmentXhrHeaders(sidxInfo)
47804    });
47805    return xhr(sidxRequestOptions, finishProcessingFn);
47806  };
47807
47808  var DashPlaylistLoader = function (_EventTarget) {
47809    inherits$1(DashPlaylistLoader, _EventTarget); // DashPlaylistLoader must accept either a src url or a playlist because subsequent
47810    // playlist loader setups from media groups will expect to be able to pass a playlist
47811    // (since there aren't external URLs to media playlists with DASH)
47812
47813    function DashPlaylistLoader(srcUrlOrPlaylist, hls) {
47814      var options = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
47815      var masterPlaylistLoader = arguments[3];
47816      classCallCheck$1(this, DashPlaylistLoader);
47817
47818      var _this = possibleConstructorReturn$1(this, (DashPlaylistLoader.__proto__ || Object.getPrototypeOf(DashPlaylistLoader)).call(this));
47819
47820      var _options$withCredenti = options.withCredentials,
47821          withCredentials = _options$withCredenti === undefined ? false : _options$withCredenti,
47822          _options$handleManife = options.handleManifestRedirects,
47823          handleManifestRedirects = _options$handleManife === undefined ? false : _options$handleManife;
47824      _this.hls_ = hls;
47825      _this.withCredentials = withCredentials;
47826      _this.handleManifestRedirects = handleManifestRedirects;
47827
47828      if (!srcUrlOrPlaylist) {
47829        throw new Error('A non-empty playlist URL or playlist is required');
47830      } // event naming?
47831
47832
47833      _this.on('minimumUpdatePeriod', function () {
47834        _this.refreshXml_();
47835      }); // live playlist staleness timeout
47836
47837
47838      _this.on('mediaupdatetimeout', function () {
47839        _this.refreshMedia_(_this.media().uri);
47840      });
47841
47842      _this.state = 'HAVE_NOTHING';
47843      _this.loadedPlaylists_ = {}; // initialize the loader state
47844      // The masterPlaylistLoader will be created with a string
47845
47846      if (typeof srcUrlOrPlaylist === 'string') {
47847        _this.srcUrl = srcUrlOrPlaylist; // TODO: reset sidxMapping between period changes
47848        // once multi-period is refactored
47849
47850        _this.sidxMapping_ = {};
47851        return possibleConstructorReturn$1(_this);
47852      }
47853
47854      _this.setupChildLoader(masterPlaylistLoader, srcUrlOrPlaylist);
47855
47856      return _this;
47857    }
47858
47859    createClass$1(DashPlaylistLoader, [{
47860      key: 'setupChildLoader',
47861      value: function setupChildLoader(masterPlaylistLoader, playlist) {
47862        this.masterPlaylistLoader_ = masterPlaylistLoader;
47863        this.childPlaylist_ = playlist;
47864      }
47865    }, {
47866      key: 'dispose',
47867      value: function dispose() {
47868        this.stopRequest();
47869        this.loadedPlaylists_ = {};
47870        window$1.clearTimeout(this.minimumUpdatePeriodTimeout_);
47871        window$1.clearTimeout(this.mediaRequest_);
47872        window$1.clearTimeout(this.mediaUpdateTimeout);
47873      }
47874    }, {
47875      key: 'hasPendingRequest',
47876      value: function hasPendingRequest() {
47877        return this.request || this.mediaRequest_;
47878      }
47879    }, {
47880      key: 'stopRequest',
47881      value: function stopRequest() {
47882        if (this.request) {
47883          var oldRequest = this.request;
47884          this.request = null;
47885          oldRequest.onreadystatechange = null;
47886          oldRequest.abort();
47887        }
47888      }
47889    }, {
47890      key: 'sidxRequestFinished_',
47891      value: function sidxRequestFinished_(playlist, master, startingState, doneFn) {
47892        var _this2 = this;
47893
47894        return function (err, request) {
47895          // disposed
47896          if (!_this2.request) {
47897            return;
47898          } // pending request is cleared
47899
47900
47901          _this2.request = null;
47902
47903          if (err) {
47904            _this2.error = {
47905              status: request.status,
47906              message: 'DASH playlist request error at URL: ' + playlist.uri,
47907              response: request.response,
47908              // MEDIA_ERR_NETWORK
47909              code: 2
47910            };
47911
47912            if (startingState) {
47913              _this2.state = startingState;
47914            }
47915
47916            _this2.trigger('error');
47917
47918            return doneFn(master, null);
47919          }
47920
47921          var bytes = new Uint8Array(request.response);
47922          var sidx = mp4Inspector.parseSidx(bytes.subarray(8));
47923          return doneFn(master, sidx);
47924        };
47925      }
47926    }, {
47927      key: 'media',
47928      value: function media(playlist) {
47929        var _this3 = this; // getter
47930
47931
47932        if (!playlist) {
47933          return this.media_;
47934        } // setter
47935
47936
47937        if (this.state === 'HAVE_NOTHING') {
47938          throw new Error('Cannot switch media playlist from ' + this.state);
47939        }
47940
47941        var startingState = this.state; // find the playlist object if the target playlist has been specified by URI
47942
47943        if (typeof playlist === 'string') {
47944          if (!this.master.playlists[playlist]) {
47945            throw new Error('Unknown playlist URI: ' + playlist);
47946          }
47947
47948          playlist = this.master.playlists[playlist];
47949        }
47950
47951        var mediaChange = !this.media_ || playlist.uri !== this.media_.uri; // switch to previously loaded playlists immediately
47952
47953        if (mediaChange && this.loadedPlaylists_[playlist.uri] && this.loadedPlaylists_[playlist.uri].endList) {
47954          this.state = 'HAVE_METADATA';
47955          this.media_ = playlist; // trigger media change if the active media has been updated
47956
47957          if (mediaChange) {
47958            this.trigger('mediachanging');
47959            this.trigger('mediachange');
47960          }
47961
47962          return;
47963        } // switching to the active playlist is a no-op
47964
47965
47966        if (!mediaChange) {
47967          return;
47968        } // switching from an already loaded playlist
47969
47970
47971        if (this.media_) {
47972          this.trigger('mediachanging');
47973        }
47974
47975        if (!playlist.sidx) {
47976          // Continue asynchronously if there is no sidx
47977          // wait one tick to allow haveMaster to run first on a child loader
47978          this.mediaRequest_ = window$1.setTimeout(this.haveMetadata.bind(this, {
47979            startingState: startingState,
47980            playlist: playlist
47981          }), 0); // exit early and don't do sidx work
47982
47983          return;
47984        } // we have sidx mappings
47985
47986
47987        var oldMaster = void 0;
47988        var sidxMapping = void 0; // sidxMapping is used when parsing the masterXml, so store
47989        // it on the masterPlaylistLoader
47990
47991        if (this.masterPlaylistLoader_) {
47992          oldMaster = this.masterPlaylistLoader_.master;
47993          sidxMapping = this.masterPlaylistLoader_.sidxMapping_;
47994        } else {
47995          oldMaster = this.master;
47996          sidxMapping = this.sidxMapping_;
47997        }
47998
47999        var sidxKey = generateSidxKey(playlist.sidx);
48000        sidxMapping[sidxKey] = {
48001          sidxInfo: playlist.sidx
48002        };
48003        this.request = requestSidx_(playlist.sidx, playlist, this.hls_.xhr, {
48004          handleManifestRedirects: this.handleManifestRedirects
48005        }, this.sidxRequestFinished_(playlist, oldMaster, startingState, function (newMaster, sidx) {
48006          if (!newMaster || !sidx) {
48007            throw new Error('failed to request sidx');
48008          } // update loader's sidxMapping with parsed sidx box
48009
48010
48011          sidxMapping[sidxKey].sidx = sidx; // everything is ready just continue to haveMetadata
48012
48013          _this3.haveMetadata({
48014            startingState: startingState,
48015            playlist: newMaster.playlists[playlist.uri]
48016          });
48017        }));
48018      }
48019    }, {
48020      key: 'haveMetadata',
48021      value: function haveMetadata(_ref) {
48022        var startingState = _ref.startingState,
48023            playlist = _ref.playlist;
48024        this.state = 'HAVE_METADATA';
48025        this.loadedPlaylists_[playlist.uri] = playlist;
48026        this.mediaRequest_ = null; // This will trigger loadedplaylist
48027
48028        this.refreshMedia_(playlist.uri); // fire loadedmetadata the first time a media playlist is loaded
48029        // to resolve setup of media groups
48030
48031        if (startingState === 'HAVE_MASTER') {
48032          this.trigger('loadedmetadata');
48033        } else {
48034          // trigger media change if the active media has been updated
48035          this.trigger('mediachange');
48036        }
48037      }
48038    }, {
48039      key: 'pause',
48040      value: function pause() {
48041        this.stopRequest();
48042        window$1.clearTimeout(this.mediaUpdateTimeout);
48043        window$1.clearTimeout(this.minimumUpdatePeriodTimeout_);
48044
48045        if (this.state === 'HAVE_NOTHING') {
48046          // If we pause the loader before any data has been retrieved, its as if we never
48047          // started, so reset to an unstarted state.
48048          this.started = false;
48049        }
48050      }
48051    }, {
48052      key: 'load',
48053      value: function load(isFinalRendition) {
48054        var _this4 = this;
48055
48056        window$1.clearTimeout(this.mediaUpdateTimeout);
48057        window$1.clearTimeout(this.minimumUpdatePeriodTimeout_);
48058        var media = this.media();
48059
48060        if (isFinalRendition) {
48061          var delay = media ? media.targetDuration / 2 * 1000 : 5 * 1000;
48062          this.mediaUpdateTimeout = window$1.setTimeout(function () {
48063            return _this4.load();
48064          }, delay);
48065          return;
48066        } // because the playlists are internal to the manifest, load should either load the
48067        // main manifest, or do nothing but trigger an event
48068
48069
48070        if (!this.started) {
48071          this.start();
48072          return;
48073        }
48074
48075        this.trigger('loadedplaylist');
48076      }
48077      /**
48078       * Parses the master xml string and updates playlist uri references
48079       *
48080       * @return {Object}
48081       *         The parsed mpd manifest object
48082       */
48083
48084    }, {
48085      key: 'parseMasterXml',
48086      value: function parseMasterXml() {
48087        var master = parse(this.masterXml_, {
48088          manifestUri: this.srcUrl,
48089          clientOffset: this.clientOffset_,
48090          sidxMapping: this.sidxMapping_
48091        });
48092        master.uri = this.srcUrl; // Set up phony URIs for the playlists since we won't have external URIs for DASH
48093        // but reference playlists by their URI throughout the project
48094        // TODO: Should we create the dummy uris in mpd-parser as well (leaning towards yes).
48095
48096        for (var i = 0; i < master.playlists.length; i++) {
48097          var phonyUri = 'placeholder-uri-' + i;
48098          master.playlists[i].uri = phonyUri; // set up by URI references
48099
48100          master.playlists[phonyUri] = master.playlists[i];
48101        } // set up phony URIs for the media group playlists since we won't have external
48102        // URIs for DASH but reference playlists by their URI throughout the project
48103
48104
vendor: 25,700 bytes, lines 48105-48875
48105        forEachMediaGroup(master, function (properties, mediaType, groupKey, labelKey) {
48106          if (properties.playlists && properties.playlists.length) {
48107            var _phonyUri = 'placeholder-uri-' + mediaType + '-' + groupKey + '-' + labelKey;
48108
48109            properties.playlists[0].uri = _phonyUri; // setup URI references
48110
48111            master.playlists[_phonyUri] = properties.playlists[0];
48112          }
48113        });
48114        setupMediaPlaylists(master);
48115        resolveMediaGroupUris(master);
48116        return master;
48117      }
48118    }, {
48119      key: 'start',
48120      value: function start() {
48121        var _this5 = this;
48122
48123        this.started = true; // We don't need to request the master manifest again
48124        // Call this asynchronously to match the xhr request behavior below
48125
48126        if (this.masterPlaylistLoader_) {
48127          this.mediaRequest_ = window$1.setTimeout(this.haveMaster_.bind(this), 0);
48128          return;
48129        } // request the specified URL
48130
48131
48132        this.request = this.hls_.xhr({
48133          uri: this.srcUrl,
48134          withCredentials: this.withCredentials
48135        }, function (error, req) {
48136          // disposed
48137          if (!_this5.request) {
48138            return;
48139          } // clear the loader's request reference
48140
48141
48142          _this5.request = null;
48143
48144          if (error) {
48145            _this5.error = {
48146              status: req.status,
48147              message: 'DASH playlist request error at URL: ' + _this5.srcUrl,
48148              responseText: req.responseText,
48149              // MEDIA_ERR_NETWORK
48150              code: 2
48151            };
48152
48153            if (_this5.state === 'HAVE_NOTHING') {
48154              _this5.started = false;
48155            }
48156
48157            return _this5.trigger('error');
48158          }
48159
48160          _this5.masterXml_ = req.responseText;
48161
48162          if (req.responseHeaders && req.responseHeaders.date) {
48163            _this5.masterLoaded_ = Date.parse(req.responseHeaders.date);
48164          } else {
48165            _this5.masterLoaded_ = Date.now();
48166          }
48167
48168          _this5.srcUrl = resolveManifestRedirect(_this5.handleManifestRedirects, _this5.srcUrl, req);
48169
48170          _this5.syncClientServerClock_(_this5.onClientServerClockSync_.bind(_this5));
48171        });
48172      }
48173      /**
48174       * Parses the master xml for UTCTiming node to sync the client clock to the server
48175       * clock. If the UTCTiming node requires a HEAD or GET request, that request is made.
48176       *
48177       * @param {Function} done
48178       *        Function to call when clock sync has completed
48179       */
48180
48181    }, {
48182      key: 'syncClientServerClock_',
48183      value: function syncClientServerClock_(done) {
48184        var _this6 = this;
48185
48186        var utcTiming = parseUTCTiming(this.masterXml_); // No UTCTiming element found in the mpd. Use Date header from mpd request as the
48187        // server clock
48188
48189        if (utcTiming === null) {
48190          this.clientOffset_ = this.masterLoaded_ - Date.now();
48191          return done();
48192        }
48193
48194        if (utcTiming.method === 'DIRECT') {
48195          this.clientOffset_ = utcTiming.value - Date.now();
48196          return done();
48197        }
48198
48199        this.request = this.hls_.xhr({
48200          uri: resolveUrl$1(this.srcUrl, utcTiming.value),
48201          method: utcTiming.method,
48202          withCredentials: this.withCredentials
48203        }, function (error, req) {
48204          // disposed
48205          if (!_this6.request) {
48206            return;
48207          }
48208
48209          if (error) {
48210            // sync request failed, fall back to using date header from mpd
48211            // TODO: log warning
48212            _this6.clientOffset_ = _this6.masterLoaded_ - Date.now();
48213            return done();
48214          }
48215
48216          var serverTime = void 0;
48217
48218          if (utcTiming.method === 'HEAD') {
48219            if (!req.responseHeaders || !req.responseHeaders.date) {
48220              // expected date header not preset, fall back to using date header from mpd
48221              // TODO: log warning
48222              serverTime = _this6.masterLoaded_;
48223            } else {
48224              serverTime = Date.parse(req.responseHeaders.date);
48225            }
48226          } else {
48227            serverTime = Date.parse(req.responseText);
48228          }
48229
48230          _this6.clientOffset_ = serverTime - Date.now();
48231          done();
48232        });
48233      }
48234    }, {
48235      key: 'haveMaster_',
48236      value: function haveMaster_() {
48237        this.state = 'HAVE_MASTER'; // clear media request
48238
48239        this.mediaRequest_ = null;
48240
48241        if (!this.masterPlaylistLoader_) {
48242          this.master = this.parseMasterXml(); // We have the master playlist at this point, so
48243          // trigger this to allow MasterPlaylistController
48244          // to make an initial playlist selection
48245
48246          this.trigger('loadedplaylist');
48247        } else if (!this.media_) {
48248          // no media playlist was specifically selected so select
48249          // the one the child playlist loader was created with
48250          this.media(this.childPlaylist_);
48251        }
48252      }
48253      /**
48254       * Handler for after client/server clock synchronization has happened. Sets up
48255       * xml refresh timer if specificed by the manifest.
48256       */
48257
48258    }, {
48259      key: 'onClientServerClockSync_',
48260      value: function onClientServerClockSync_() {
48261        var _this7 = this;
48262
48263        this.haveMaster_();
48264
48265        if (!this.hasPendingRequest() && !this.media_) {
48266          this.media(this.master.playlists[0]);
48267        } // TODO: minimumUpdatePeriod can have a value of 0. Currently the manifest will not
48268        // be refreshed when this is the case. The inter-op guide says that when the
48269        // minimumUpdatePeriod is 0, the manifest should outline all currently available
48270        // segments, but future segments may require an update. I think a good solution
48271        // would be to update the manifest at the same rate that the media playlists
48272        // are "refreshed", i.e. every targetDuration.
48273
48274
48275        if (this.master && this.master.minimumUpdatePeriod) {
48276          this.minimumUpdatePeriodTimeout_ = window$1.setTimeout(function () {
48277            _this7.trigger('minimumUpdatePeriod');
48278          }, this.master.minimumUpdatePeriod);
48279        }
48280      }
48281      /**
48282       * Sends request to refresh the master xml and updates the parsed master manifest
48283       * TODO: Does the client offset need to be recalculated when the xml is refreshed?
48284       */
48285
48286    }, {
48287      key: 'refreshXml_',
48288      value: function refreshXml_() {
48289        var _this8 = this; // The srcUrl here *may* need to pass through handleManifestsRedirects when
48290        // sidx is implemented
48291
48292
48293        this.request = this.hls_.xhr({
48294          uri: this.srcUrl,
48295          withCredentials: this.withCredentials
48296        }, function (error, req) {
48297          // disposed
48298          if (!_this8.request) {
48299            return;
48300          } // clear the loader's request reference
48301
48302
48303          _this8.request = null;
48304
48305          if (error) {
48306            _this8.error = {
48307              status: req.status,
48308              message: 'DASH playlist request error at URL: ' + _this8.srcUrl,
48309              responseText: req.responseText,
48310              // MEDIA_ERR_NETWORK
48311              code: 2
48312            };
48313
48314            if (_this8.state === 'HAVE_NOTHING') {
48315              _this8.started = false;
48316            }
48317
48318            return _this8.trigger('error');
48319          }
48320
48321          _this8.masterXml_ = req.responseText; // This will filter out updated sidx info from the mapping
48322
48323          _this8.sidxMapping_ = filterChangedSidxMappings(_this8.masterXml_, _this8.srcUrl, _this8.clientOffset_, _this8.sidxMapping_);
48324
48325          var master = _this8.parseMasterXml();
48326
48327          var updatedMaster = updateMaster$1(_this8.master, master);
48328
48329          var currentSidxInfo = _this8.media().sidx;
48330
48331          if (updatedMaster) {
48332            if (currentSidxInfo) {
48333              var sidxKey = generateSidxKey(currentSidxInfo); // the sidx was updated, so the previous mapping was removed
48334
48335              if (!_this8.sidxMapping_[sidxKey]) {
48336                var playlist = _this8.media();
48337
48338                _this8.request = requestSidx_(playlist.sidx, playlist, _this8.hls_.xhr, {
48339                  handleManifestRedirects: _this8.handleManifestRedirects
48340                }, _this8.sidxRequestFinished_(playlist, master, _this8.state, function (newMaster, sidx) {
48341                  if (!newMaster || !sidx) {
48342                    throw new Error('failed to request sidx on minimumUpdatePeriod');
48343                  } // update loader's sidxMapping with parsed sidx box
48344
48345
48346                  _this8.sidxMapping_[sidxKey].sidx = sidx;
48347                  _this8.minimumUpdatePeriodTimeout_ = window$1.setTimeout(function () {
48348                    _this8.trigger('minimumUpdatePeriod');
48349                  }, _this8.master.minimumUpdatePeriod); // TODO: do we need to reload the current playlist?
48350
48351                  _this8.refreshMedia_(_this8.media().uri);
48352
48353                  return;
48354                }));
48355              }
48356            } else {
48357              _this8.master = updatedMaster;
48358            }
48359          }
48360
48361          _this8.minimumUpdatePeriodTimeout_ = window$1.setTimeout(function () {
48362            _this8.trigger('minimumUpdatePeriod');
48363          }, _this8.master.minimumUpdatePeriod);
48364        });
48365      }
48366      /**
48367       * Refreshes the media playlist by re-parsing the master xml and updating playlist
48368       * references. If this is an alternate loader, the updated parsed manifest is retrieved
48369       * from the master loader.
48370       */
48371
48372    }, {
48373      key: 'refreshMedia_',
48374      value: function refreshMedia_(mediaUri) {
48375        var _this9 = this;
48376
48377        if (!mediaUri) {
48378          throw new Error('refreshMedia_ must take a media uri');
48379        }
48380
48381        var oldMaster = void 0;
48382        var newMaster = void 0;
48383
48384        if (this.masterPlaylistLoader_) {
48385          oldMaster = this.masterPlaylistLoader_.master;
48386          newMaster = this.masterPlaylistLoader_.parseMasterXml();
48387        } else {
48388          oldMaster = this.master;
48389          newMaster = this.parseMasterXml();
48390        }
48391
48392        var updatedMaster = updateMaster$1(oldMaster, newMaster);
48393
48394        if (updatedMaster) {
48395          if (this.masterPlaylistLoader_) {
48396            this.masterPlaylistLoader_.master = updatedMaster;
48397          } else {
48398            this.master = updatedMaster;
48399          }
48400
48401          this.media_ = updatedMaster.playlists[mediaUri];
48402        } else {
48403          this.media_ = newMaster.playlists[mediaUri];
48404          this.trigger('playlistunchanged');
48405        }
48406
48407        if (!this.media().endList) {
48408          this.mediaUpdateTimeout = window$1.setTimeout(function () {
48409            _this9.trigger('mediaupdatetimeout');
48410          }, refreshDelay(this.media(), !!updatedMaster));
48411        }
48412
48413        this.trigger('loadedplaylist');
48414      }
48415    }]);
48416    return DashPlaylistLoader;
48417  }(EventTarget$1$1);
48418
48419  var logger = function logger(source) {
48420    if (videojs$1.log.debug) {
48421      return videojs$1.log.debug.bind(videojs$1, 'VHS:', source + ' >');
48422    }
48423
48424    return function () {};
48425  };
48426
48427  function noop$1() {}
48428  /**
48429   * @file source-updater.js
48430   */
48431
48432  /**
48433   * A queue of callbacks to be serialized and applied when a
48434   * MediaSource and its associated SourceBuffers are not in the
48435   * updating state. It is used by the segment loader to update the
48436   * underlying SourceBuffers when new data is loaded, for instance.
48437   *
48438   * @class SourceUpdater
48439   * @param {MediaSource} mediaSource the MediaSource to create the
48440   * SourceBuffer from
48441   * @param {String} mimeType the desired MIME type of the underlying
48442   * SourceBuffer
48443   * @param {Object} sourceBufferEmitter an event emitter that fires when a source buffer is
48444   * added to the media source
48445   */
48446
48447
48448  var SourceUpdater = function () {
48449    function SourceUpdater(mediaSource, mimeType, type, sourceBufferEmitter) {
48450      classCallCheck$1(this, SourceUpdater);
48451      this.callbacks_ = [];
48452      this.pendingCallback_ = null;
48453      this.timestampOffset_ = 0;
48454      this.mediaSource = mediaSource;
48455      this.processedAppend_ = false;
48456      this.type_ = type;
48457      this.mimeType_ = mimeType;
48458      this.logger_ = logger('SourceUpdater[' + type + '][' + mimeType + ']');
48459
48460      if (mediaSource.readyState === 'closed') {
48461        mediaSource.addEventListener('sourceopen', this.createSourceBuffer_.bind(this, mimeType, sourceBufferEmitter));
48462      } else {
48463        this.createSourceBuffer_(mimeType, sourceBufferEmitter);
48464      }
48465    }
48466
48467    createClass$1(SourceUpdater, [{
48468      key: 'createSourceBuffer_',
48469      value: function createSourceBuffer_(mimeType, sourceBufferEmitter) {
48470        var _this = this;
48471
48472        this.sourceBuffer_ = this.mediaSource.addSourceBuffer(mimeType);
48473        this.logger_('created SourceBuffer');
48474
48475        if (sourceBufferEmitter) {
48476          sourceBufferEmitter.trigger('sourcebufferadded');
48477
48478          if (this.mediaSource.sourceBuffers.length < 2) {
48479            // There's another source buffer we must wait for before we can start updating
48480            // our own (or else we can get into a bad state, i.e., appending video/audio data
48481            // before the other video/audio source buffer is available and leading to a video
48482            // or audio only buffer).
48483            sourceBufferEmitter.on('sourcebufferadded', function () {
48484              _this.start_();
48485            });
48486            return;
48487          }
48488        }
48489
48490        this.start_();
48491      }
48492    }, {
48493      key: 'start_',
48494      value: function start_() {
48495        var _this2 = this;
48496
48497        this.started_ = true; // run completion handlers and process callbacks as updateend
48498        // events fire
48499
48500        this.onUpdateendCallback_ = function () {
48501          var pendingCallback = _this2.pendingCallback_;
48502          _this2.pendingCallback_ = null;
48503          _this2.sourceBuffer_.removing = false;
48504
48505          _this2.logger_('buffered [' + printableRange(_this2.buffered()) + ']');
48506
48507          if (pendingCallback) {
48508            pendingCallback();
48509          }
48510
48511          _this2.runCallback_();
48512        };
48513
48514        this.sourceBuffer_.addEventListener('updateend', this.onUpdateendCallback_);
48515        this.runCallback_();
48516      }
48517      /**
48518       * Aborts the current segment and resets the segment parser.
48519       *
48520       * @param {Function} done function to call when done
48521       * @see http://w3c.github.io/media-source/#widl-SourceBuffer-abort-void
48522       */
48523
48524    }, {
48525      key: 'abort',
48526      value: function abort(done) {
48527        var _this3 = this;
48528
48529        if (this.processedAppend_) {
48530          this.queueCallback_(function () {
48531            _this3.sourceBuffer_.abort();
48532          }, done);
48533        }
48534      }
48535      /**
48536       * Queue an update to append an ArrayBuffer.
48537       *
48538       * @param {ArrayBuffer} bytes
48539       * @param {Function} done the function to call when done
48540       * @see http://www.w3.org/TR/media-source/#widl-SourceBuffer-appendBuffer-void-ArrayBuffer-data
48541       */
48542
48543    }, {
48544      key: 'appendBuffer',
48545      value: function appendBuffer(config, done) {
48546        var _this4 = this;
48547
48548        this.processedAppend_ = true;
48549        this.queueCallback_(function () {
48550          if (config.videoSegmentTimingInfoCallback) {
48551            _this4.sourceBuffer_.addEventListener('videoSegmentTimingInfo', config.videoSegmentTimingInfoCallback);
48552          }
48553
48554          _this4.sourceBuffer_.appendBuffer(config.bytes);
48555        }, function () {
48556          if (config.videoSegmentTimingInfoCallback) {
48557            _this4.sourceBuffer_.removeEventListener('videoSegmentTimingInfo', config.videoSegmentTimingInfoCallback);
48558          }
48559
48560          done();
48561        });
48562      }
48563      /**
48564       * Indicates what TimeRanges are buffered in the managed SourceBuffer.
48565       *
48566       * @see http://www.w3.org/TR/media-source/#widl-SourceBuffer-buffered
48567       */
48568
48569    }, {
48570      key: 'buffered',
48571      value: function buffered() {
48572        if (!this.sourceBuffer_) {
48573          return videojs$1.createTimeRanges();
48574        }
48575
48576        return this.sourceBuffer_.buffered;
48577      }
48578      /**
48579       * Queue an update to remove a time range from the buffer.
48580       *
48581       * @param {Number} start where to start the removal
48582       * @param {Number} end where to end the removal
48583       * @param {Function} [done=noop] optional callback to be executed when the remove
48584       * operation is complete
48585       * @see http://www.w3.org/TR/media-source/#widl-SourceBuffer-remove-void-double-start-unrestricted-double-end
48586       */
48587
48588    }, {
48589      key: 'remove',
48590      value: function remove(start, end) {
48591        var _this5 = this;
48592
48593        var done = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : noop$1;
48594
48595        if (this.processedAppend_) {
48596          this.queueCallback_(function () {
48597            _this5.logger_('remove [' + start + ' => ' + end + ']');
48598
48599            _this5.sourceBuffer_.removing = true;
48600
48601            _this5.sourceBuffer_.remove(start, end);
48602          }, done);
48603        }
48604      }
48605      /**
48606       * Whether the underlying sourceBuffer is updating or not
48607       *
48608       * @return {Boolean} the updating status of the SourceBuffer
48609       */
48610
48611    }, {
48612      key: 'updating',
48613      value: function updating() {
48614        // we are updating if the sourcebuffer is updating or
48615        return !this.sourceBuffer_ || this.sourceBuffer_.updating || // if we have a pending callback that is not our internal noop
48616        !!this.pendingCallback_ && this.pendingCallback_ !== noop$1;
48617      }
48618      /**
48619       * Set/get the timestampoffset on the SourceBuffer
48620       *
48621       * @return {Number} the timestamp offset
48622       */
48623
48624    }, {
48625      key: 'timestampOffset',
48626      value: function timestampOffset(offset) {
48627        var _this6 = this;
48628
48629        if (typeof offset !== 'undefined') {
48630          this.queueCallback_(function () {
48631            _this6.sourceBuffer_.timestampOffset = offset;
48632
48633            _this6.runCallback_();
48634          });
48635          this.timestampOffset_ = offset;
48636        }
48637
48638        return this.timestampOffset_;
48639      }
48640      /**
48641       * Queue a callback to run
48642       */
48643
48644    }, {
48645      key: 'queueCallback_',
48646      value: function queueCallback_(callback, done) {
48647        this.callbacks_.push([callback.bind(this), done]);
48648        this.runCallback_();
48649      }
48650      /**
48651       * Run a queued callback
48652       */
48653
48654    }, {
48655      key: 'runCallback_',
48656      value: function runCallback_() {
48657        var callbacks = void 0;
48658
48659        if (!this.updating() && this.callbacks_.length && this.started_) {
48660          callbacks = this.callbacks_.shift();
48661          this.pendingCallback_ = callbacks[1];
48662          callbacks[0]();
48663        }
48664      }
48665      /**
48666       * dispose of the source updater and the underlying sourceBuffer
48667       */
48668
48669    }, {
48670      key: 'dispose',
48671      value: function dispose() {
48672        var _this7 = this;
48673
48674        var disposeFn = function disposeFn() {
48675          if (_this7.sourceBuffer_ && _this7.mediaSource.readyState === 'open') {
48676            _this7.sourceBuffer_.abort();
48677          }
48678
48679          _this7.sourceBuffer_.removeEventListener('updateend', disposeFn);
48680        };
48681
48682        this.sourceBuffer_.removeEventListener('updateend', this.onUpdateendCallback_);
48683
48684        if (this.sourceBuffer_.removing) {
48685          this.sourceBuffer_.addEventListener('updateend', disposeFn);
48686        } else {
48687          disposeFn();
48688        }
48689      }
48690    }]);
48691    return SourceUpdater;
48692  }();
48693
48694  var Config = {
48695    GOAL_BUFFER_LENGTH: 30,
48696    MAX_GOAL_BUFFER_LENGTH: 60,
48697    GOAL_BUFFER_LENGTH_RATE: 1,
48698    // 0.5 MB/s
48699    INITIAL_BANDWIDTH: 4194304,
48700    // A fudge factor to apply to advertised playlist bitrates to account for
48701    // temporary flucations in client bandwidth
48702    BANDWIDTH_VARIANCE: 1.2,
48703    // How much of the buffer must be filled before we consider upswitching
48704    BUFFER_LOW_WATER_LINE: 0,
48705    MAX_BUFFER_LOW_WATER_LINE: 30,
48706    BUFFER_LOW_WATER_LINE_RATE: 1
48707  };
48708  var REQUEST_ERRORS = {
48709    FAILURE: 2,
48710    TIMEOUT: -101,
48711    ABORTED: -102
48712  };
48713  /**
48714   * Abort all requests
48715   *
48716   * @param {Object} activeXhrs - an object that tracks all XHR requests
48717   */
48718
48719  var abortAll = function abortAll(activeXhrs) {
48720    activeXhrs.forEach(function (xhr) {
48721      xhr.abort();
48722    });
48723  };
48724  /**
48725   * Gather important bandwidth stats once a request has completed
48726   *
48727   * @param {Object} request - the XHR request from which to gather stats
48728   */
48729
48730
48731  var getRequestStats = function getRequestStats(request) {
48732    return {
48733      bandwidth: request.bandwidth,
48734      bytesReceived: request.bytesReceived || 0,
48735      roundTripTime: request.roundTripTime || 0
48736    };
48737  };
48738  /**
48739   * If possible gather bandwidth stats as a request is in
48740   * progress
48741   *
48742   * @param {Event} progressEvent - an event object from an XHR's progress event
48743   */
48744
48745
48746  var getProgressStats = function getProgressStats(progressEvent) {
48747    var request = progressEvent.target;
48748    var roundTripTime = Date.now() - request.requestTime;
48749    var stats = {
48750      bandwidth: Infinity,
48751      bytesReceived: 0,
48752      roundTripTime: roundTripTime || 0
48753    };
48754    stats.bytesReceived = progressEvent.loaded; // This can result in Infinity if stats.roundTripTime is 0 but that is ok
48755    // because we should only use bandwidth stats on progress to determine when
48756    // abort a request early due to insufficient bandwidth
48757
48758    stats.bandwidth = Math.floor(stats.bytesReceived / stats.roundTripTime * 8 * 1000);
48759    return stats;
48760  };
48761  /**
48762   * Handle all error conditions in one place and return an object
48763   * with all the information
48764   *
48765   * @param {Error|null} error - if non-null signals an error occured with the XHR
48766   * @param {Object} request -  the XHR request that possibly generated the error
48767   */
48768
48769
48770  var handleErrors = function handleErrors(error, request) {
48771    if (request.timedout) {
48772      return {
48773        status: request.status,
48774        message: 'HLS request timed-out at URL: ' + request.uri,
48775        code: REQUEST_ERRORS.TIMEOUT,
48776        xhr: request
48777      };
48778    }
48779
48780    if (request.aborted) {
48781      return {
48782        status: request.status,
48783        message: 'HLS request aborted at URL: ' + request.uri,
48784        code: REQUEST_ERRORS.ABORTED,
48785        xhr: request
48786      };
48787    }
48788
48789    if (error) {
48790      return {
48791        status: request.status,
48792        message: 'HLS request errored at URL: ' + request.uri,
48793        code: REQUEST_ERRORS.FAILURE,
48794        xhr: request
48795      };
48796    }
48797
48798    return null;
48799  };
48800  /**
48801   * Handle responses for key data and convert the key data to the correct format
48802   * for the decryption step later
48803   *
48804   * @param {Object} segment - a simplified copy of the segmentInfo object
48805   *                           from SegmentLoader
48806   * @param {Function} finishProcessingFn - a callback to execute to continue processing
48807   *                                        this request
48808   */
48809
48810
48811  var handleKeyResponse = function handleKeyResponse(segment, finishProcessingFn) {
48812    return function (error, request) {
48813      var response = request.response;
48814      var errorObj = handleErrors(error, request);
48815
48816      if (errorObj) {
48817        return finishProcessingFn(errorObj, segment);
48818      }
48819
48820      if (response.byteLength !== 16) {
48821        return finishProcessingFn({
48822          status: request.status,
48823          message: 'Invalid HLS key at URL: ' + request.uri,
48824          code: REQUEST_ERRORS.FAILURE,
48825          xhr: request
48826        }, segment);
48827      }
48828
48829      var view = new DataView(response);
48830      segment.key.bytes = new Uint32Array([view.getUint32(0), view.getUint32(4), view.getUint32(8), view.getUint32(12)]);
48831      return finishProcessingFn(null, segment);
48832    };
48833  };
48834  /**
48835   * Handle init-segment responses
48836   *
48837   * @param {Object} segment - a simplified copy of the segmentInfo object
48838   *                           from SegmentLoader
48839   * @param {Function} finishProcessingFn - a callback to execute to continue processing
48840   *                                        this request
48841   */
48842
48843
48844  var handleInitSegmentResponse = function handleInitSegmentResponse(segment, captionParser, finishProcessingFn) {
48845    return function (error, request) {
48846      var response = request.response;
48847      var errorObj = handleErrors(error, request);
48848
48849      if (errorObj) {
48850        return finishProcessingFn(errorObj, segment);
48851      } // stop processing if received empty content
48852
48853
48854      if (response.byteLength === 0) {
48855        return finishProcessingFn({
48856          status: request.status,
48857          message: 'Empty HLS segment content at URL: ' + request.uri,
48858          code: REQUEST_ERRORS.FAILURE,
48859          xhr: request
48860        }, segment);
48861      }
48862
48863      segment.map.bytes = new Uint8Array(request.response); // Initialize CaptionParser if it hasn't been yet
48864
48865      if (captionParser && !captionParser.isInitialized()) {
48866        captionParser.init();
48867      }
48868
48869      segment.map.timescales = probe.timescale(segment.map.bytes);
48870      segment.map.videoTrackIds = probe.videoTrackIds(segment.map.bytes);
48871      return finishProcessingFn(null, segment);
48872    };
48873  };
48874  /**
48875   * Response handler for segment-requests being sure to set the correct
48876   * property depending on whether the segment is encryped or not
48877   * Also records and keeps track of stats that are used for ABR purposes
48878   *
48879   * @param {Object} segment - a simplified copy of the segmentInfo object
48880   *                           from SegmentLoader
48881   * @param {Function} finishProcessingFn - a callback to execute to continue processing
48882   *                                        this request
48883   */
48884
48885
48886  var handleSegmentResponse = function handleSegmentResponse(segment, captionParser, finishProcessingFn) {
48887    return function (error, request) {
48888      var response = request.response;
48889      var errorObj = handleErrors(error, request);
48890      var parsed = void 0;
48891
48892      if (errorObj) {
48893        return finishProcessingFn(errorObj, segment);
48894      } // stop processing if received empty content
48895
48896
48897      if (response.byteLength === 0) {
48898        return finishProcessingFn({
48899          status: request.status,
48900          message: 'Empty HLS segment content at URL: ' + request.uri,
48901          code: REQUEST_ERRORS.FAILURE,
48902          xhr: request
48903        }, segment);
48904      }
48905
48906      segment.stats = getRequestStats(request);
48907
48908      if (segment.key) {
48909        segment.encryptedBytes = new Uint8Array(request.response);
48910      } else {
48911        segment.bytes = new Uint8Array(request.response);
48912      } // This is likely an FMP4 and has the init segment.
48913      // Run through the CaptionParser in case there are captions.
48914
48915
48916      if (captionParser && segment.map && segment.map.bytes) {
48917        // Initialize CaptionParser if it hasn't been yet
48918        if (!captionParser.isInitialized()) {
48919          captionParser.init();
48920        }
48921
48922        parsed = captionParser.parse(segment.bytes, segment.map.videoTrackIds, segment.map.timescales);
48923
48924        if (parsed && parsed.captions) {
48925          segment.captionStreams = parsed.captionStreams;
48926          segment.fmp4Captions = parsed.captions;
48927        }
48928      }
48929
48930      return finishProcessingFn(null, segment);
48931    };
48932  };
48933  /**
48934   * Decrypt the segment via the decryption web worker
48935   *
48936   * @param {WebWorker} decrypter - a WebWorker interface to AES-128 decryption routines
48937   * @param {Object} segment - a simplified copy of the segmentInfo object
48938   *                           from SegmentLoader
48939   * @param {Function} doneFn - a callback that is executed after decryption has completed
48940   */
48941
48942
48943  var decryptSegment = function decryptSegment(decrypter, segment, doneFn) {
48944    var decryptionHandler = function decryptionHandler(event) {
48945      if (event.data.source === segment.requestId) {
48946        decrypter.removeEventListener('message', decryptionHandler);
48947        var decrypted = event.data.decrypted;
48948        segment.bytes = new Uint8Array(decrypted.bytes, decrypted.byteOffset, decrypted.byteLength);
48949        return doneFn(null, segment);
48950      }
48951    };
48952
48953    decrypter.addEventListener('message', decryptionHandler);
48954    var keyBytes = void 0;
48955
48956    if (segment.key.bytes.slice) {
48957      keyBytes = segment.key.bytes.slice();
48958    } else {
48959      keyBytes = new Uint32Array(Array.prototype.slice.call(segment.key.bytes));
48960    } // this is an encrypted segment
48961    // incrementally decrypt the segment
48962
48963
48964    decrypter.postMessage(createTransferableMessage({
48965      source: segment.requestId,
48966      encrypted: segment.encryptedBytes,
48967      key: keyBytes,
48968      iv: segment.key.iv
48969    }), [segment.encryptedBytes.buffer, keyBytes.buffer]);
48970  };
48971  /**
48972   * This function waits for all XHRs to finish (with either success or failure)
48973   * before continueing processing via it's callback. The function gathers errors
48974   * from each request into a single errors array so that the error status for
48975   * each request can be examined later.
48976   *
48977   * @param {Object} activeXhrs - an object that tracks all XHR requests
48978   * @param {WebWorker} decrypter - a WebWorker interface to AES-128 decryption routines
48979   * @param {Function} doneFn - a callback that is executed after all resources have been
48980   *                            downloaded and any decryption completed
48981   */
48982
48983
48984  var waitForCompletion = function waitForCompletion(activeXhrs, decrypter, doneFn) {
48985    var count = 0;
48986    var didError = false;
48987    return function (error, segment) {
48988      if (didError) {
48989        return;
48990      }
48991
48992      if (error) {
48993        didError = true; // If there are errors, we have to abort any outstanding requests
48994
48995        abortAll(activeXhrs); // Even though the requests above are aborted, and in theory we could wait until we
48996        // handle the aborted events from those requests, there are some cases where we may
48997        // never get an aborted event. For instance, if the network connection is lost and
vendor: 9,438 bytes, lines 48998-49258
48998        // there were two requests, the first may have triggered an error immediately, while
48999        // the second request remains unsent. In that case, the aborted algorithm will not
49000        // trigger an abort: see https://xhr.spec.whatwg.org/#the-abort()-method
49001        //
49002        // We also can't rely on the ready state of the XHR, since the request that
49003        // triggered the connection error may also show as a ready state of 0 (unsent).
49004        // Therefore, we have to finish this group of requests immediately after the first
49005        // seen error.
49006
49007        return doneFn(error, segment);
49008      }
49009
49010      count += 1;
49011
49012      if (count === activeXhrs.length) {
49013        // Keep track of when *all* of the requests have completed
49014        segment.endOfAllRequests = Date.now();
49015
49016        if (segment.encryptedBytes) {
49017          return decryptSegment(decrypter, segment, doneFn);
49018        } // Otherwise, everything is ready just continue
49019
49020
49021        return doneFn(null, segment);
49022      }
49023    };
49024  };
49025  /**
49026   * Simple progress event callback handler that gathers some stats before
49027   * executing a provided callback with the `segment` object
49028   *
49029   * @param {Object} segment - a simplified copy of the segmentInfo object
49030   *                           from SegmentLoader
49031   * @param {Function} progressFn - a callback that is executed each time a progress event
49032   *                                is received
49033   * @param {Event} event - the progress event object from XMLHttpRequest
49034   */
49035
49036
49037  var handleProgress = function handleProgress(segment, progressFn) {
49038    return function (event) {
49039      segment.stats = videojs$1.mergeOptions(segment.stats, getProgressStats(event)); // record the time that we receive the first byte of data
49040
49041      if (!segment.stats.firstBytesReceivedAt && segment.stats.bytesReceived) {
49042        segment.stats.firstBytesReceivedAt = Date.now();
49043      }
49044
49045      return progressFn(event, segment);
49046    };
49047  };
49048  /**
49049   * Load all resources and does any processing necessary for a media-segment
49050   *
49051   * Features:
49052   *   decrypts the media-segment if it has a key uri and an iv
49053   *   aborts *all* requests if *any* one request fails
49054   *
49055   * The segment object, at minimum, has the following format:
49056   * {
49057   *   resolvedUri: String,
49058   *   [byterange]: {
49059   *     offset: Number,
49060   *     length: Number
49061   *   },
49062   *   [key]: {
49063   *     resolvedUri: String
49064   *     [byterange]: {
49065   *       offset: Number,
49066   *       length: Number
49067   *     },
49068   *     iv: {
49069   *       bytes: Uint32Array
49070   *     }
49071   *   },
49072   *   [map]: {
49073   *     resolvedUri: String,
49074   *     [byterange]: {
49075   *       offset: Number,
49076   *       length: Number
49077   *     },
49078   *     [bytes]: Uint8Array
49079   *   }
49080   * }
49081   * ...where [name] denotes optional properties
49082   *
49083   * @param {Function} xhr - an instance of the xhr wrapper in xhr.js
49084   * @param {Object} xhrOptions - the base options to provide to all xhr requests
49085   * @param {WebWorker} decryptionWorker - a WebWorker interface to AES-128
49086   *                                       decryption routines
49087   * @param {Object} segment - a simplified copy of the segmentInfo object
49088   *                           from SegmentLoader
49089   * @param {Function} progressFn - a callback that receives progress events from the main
49090   *                                segment's xhr request
49091   * @param {Function} doneFn - a callback that is executed only once all requests have
49092   *                            succeeded or failed
49093   * @returns {Function} a function that, when invoked, immediately aborts all
49094   *                     outstanding requests
49095   */
49096
49097
49098  var mediaSegmentRequest = function mediaSegmentRequest(xhr, xhrOptions, decryptionWorker, captionParser, segment, progressFn, doneFn) {
49099    var activeXhrs = [];
49100    var finishProcessingFn = waitForCompletion(activeXhrs, decryptionWorker, doneFn); // optionally, request the decryption key
49101
49102    if (segment.key && !segment.key.bytes) {
49103      var keyRequestOptions = videojs$1.mergeOptions(xhrOptions, {
49104        uri: segment.key.resolvedUri,
49105        responseType: 'arraybuffer'
49106      });
49107      var keyRequestCallback = handleKeyResponse(segment, finishProcessingFn);
49108      var keyXhr = xhr(keyRequestOptions, keyRequestCallback);
49109      activeXhrs.push(keyXhr);
49110    } // optionally, request the associated media init segment
49111
49112
49113    if (segment.map && !segment.map.bytes) {
49114      var initSegmentOptions = videojs$1.mergeOptions(xhrOptions, {
49115        uri: segment.map.resolvedUri,
49116        responseType: 'arraybuffer',
49117        headers: segmentXhrHeaders(segment.map)
49118      });
49119      var initSegmentRequestCallback = handleInitSegmentResponse(segment, captionParser, finishProcessingFn);
49120      var initSegmentXhr = xhr(initSegmentOptions, initSegmentRequestCallback);
49121      activeXhrs.push(initSegmentXhr);
49122    }
49123
49124    var segmentRequestOptions = videojs$1.mergeOptions(xhrOptions, {
49125      uri: segment.resolvedUri,
49126      responseType: 'arraybuffer',
49127      headers: segmentXhrHeaders(segment)
49128    });
49129    var segmentRequestCallback = handleSegmentResponse(segment, captionParser, finishProcessingFn);
49130    var segmentXhr = xhr(segmentRequestOptions, segmentRequestCallback);
49131    segmentXhr.addEventListener('progress', handleProgress(segment, progressFn));
49132    activeXhrs.push(segmentXhr);
49133    return function () {
49134      return abortAll(activeXhrs);
49135    };
49136  }; // Utilities
49137
49138  /**
49139   * Returns the CSS value for the specified property on an element
49140   * using `getComputedStyle`. Firefox has a long-standing issue where
49141   * getComputedStyle() may return null when running in an iframe with
49142   * `display: none`.
49143   *
49144   * @see https://bugzilla.mozilla.org/show_bug.cgi?id=548397
49145   * @param {HTMLElement} el the htmlelement to work on
49146   * @param {string} the proprety to get the style for
49147   */
49148
49149
49150  var safeGetComputedStyle = function safeGetComputedStyle(el, property) {
49151    var result = void 0;
49152
49153    if (!el) {
49154      return '';
49155    }
49156
49157    result = window$1.getComputedStyle(el);
49158
49159    if (!result) {
49160      return '';
49161    }
49162
49163    return result[property];
49164  };
49165  /**
49166   * Resuable stable sort function
49167   *
49168   * @param {Playlists} array
49169   * @param {Function} sortFn Different comparators
49170   * @function stableSort
49171   */
49172
49173
49174  var stableSort = function stableSort(array, sortFn) {
49175    var newArray = array.slice();
49176    array.sort(function (left, right) {
49177      var cmp = sortFn(left, right);
49178
49179      if (cmp === 0) {
49180        return newArray.indexOf(left) - newArray.indexOf(right);
49181      }
49182
49183      return cmp;
49184    });
49185  };
49186  /**
49187   * A comparator function to sort two playlist object by bandwidth.
49188   *
49189   * @param {Object} left a media playlist object
49190   * @param {Object} right a media playlist object
49191   * @return {Number} Greater than zero if the bandwidth attribute of
49192   * left is greater than the corresponding attribute of right. Less
49193   * than zero if the bandwidth of right is greater than left and
49194   * exactly zero if the two are equal.
49195   */
49196
49197
49198  var comparePlaylistBandwidth = function comparePlaylistBandwidth(left, right) {
49199    var leftBandwidth = void 0;
49200    var rightBandwidth = void 0;
49201
49202    if (left.attributes.BANDWIDTH) {
49203      leftBandwidth = left.attributes.BANDWIDTH;
49204    }
49205
49206    leftBandwidth = leftBandwidth || window$1.Number.MAX_VALUE;
49207
49208    if (right.attributes.BANDWIDTH) {
49209      rightBandwidth = right.attributes.BANDWIDTH;
49210    }
49211
49212    rightBandwidth = rightBandwidth || window$1.Number.MAX_VALUE;
49213    return leftBandwidth - rightBandwidth;
49214  };
49215  /**
49216   * A comparator function to sort two playlist object by resolution (width).
49217   * @param {Object} left a media playlist object
49218   * @param {Object} right a media playlist object
49219   * @return {Number} Greater than zero if the resolution.width attribute of
49220   * left is greater than the corresponding attribute of right. Less
49221   * than zero if the resolution.width of right is greater than left and
49222   * exactly zero if the two are equal.
49223   */
49224
49225
49226  var comparePlaylistResolution = function comparePlaylistResolution(left, right) {
49227    var leftWidth = void 0;
49228    var rightWidth = void 0;
49229
49230    if (left.attributes.RESOLUTION && left.attributes.RESOLUTION.width) {
49231      leftWidth = left.attributes.RESOLUTION.width;
49232    }
49233
49234    leftWidth = leftWidth || window$1.Number.MAX_VALUE;
49235
49236    if (right.attributes.RESOLUTION && right.attributes.RESOLUTION.width) {
49237      rightWidth = right.attributes.RESOLUTION.width;
49238    }
49239
49240    rightWidth = rightWidth || window$1.Number.MAX_VALUE; // NOTE - Fallback to bandwidth sort as appropriate in cases where multiple renditions
49241    // have the same media dimensions/ resolution
49242
49243    if (leftWidth === rightWidth && left.attributes.BANDWIDTH && right.attributes.BANDWIDTH) {
49244      return left.attributes.BANDWIDTH - right.attributes.BANDWIDTH;
49245    }
49246
49247    return leftWidth - rightWidth;
49248  };
49249  /**
49250   * Chooses the appropriate media playlist based on bandwidth and player size
49251   *
49252   * @param {Object} master
49253   *        Object representation of the master manifest
49254   * @param {Number} playerBandwidth
49255   *        Current calculated bandwidth of the player
49256   * @param {Number} playerWidth
49257   *        Current width of the player element
49258   * @param {Number}
49258 playerHeight
49259   *        Current height of the player element
49260   * @param {Boolean} limitRenditionByPlayerDimensions
49261   *        True if the player width and height should be used during the selection, false otherwise
49262   * @return {Playlist} the highest bitrate playlist less than the
49263   * currently detected bandwidth, accounting for some amount of
49264   * bandwidth variance
49265   */
49266
49267
49268  var simpleSelector = function simpleSelector(master, playerBandwidth, playerWidth, playerHeight, limitRenditionByPlayerDimensions) {
49269    // convert the playlists to an intermediary representation to make comparisons easier
49270    var sortedPlaylistReps = master.playlists.map(function (playlist) {
49271      var width = void 0;
49272      var height = void 0;
49273      var bandwidth = void 0;
49274      width = playlist.attributes.RESOLUTION && playlist.attributes.RESOLUTION.width;
49275      height = playlist.attributes.RESOLUTION && playlist.attributes.RESOLUTION.height;
49276      bandwidth = playlist.attributes.BANDWIDTH;
49277      bandwidth = bandwidth || window$1.Number.MAX_VALUE;
49278      return {
49279        bandwidth: bandwidth,
49280        width: width,
49281        height: height,
49282        playlist: playlist
49283      };
49284    });
49285    stableSort(sortedPlaylistReps, function (left, right) {
49286      return left.bandwidth - right.bandwidth;
49287    }); // filter out any playlists that have been excluded due to
49288    // incompatible configurations
49289
49290    sortedPlaylistReps = sortedPlaylistReps.filter(function (rep) {
49291      return !Playlist.isIncompatible(rep.playlist);
49292    }); // filter out any playlists that have been disabled manually through the representations
49293    // api or blacklisted temporarily due to playback errors.
49294
49295    var enabledPlaylistReps = sortedPlaylistReps.filter(function (rep) {
49296      return Playlist.isEnabled(rep.playlist);
49297    });
49298
49299    if (!enabledPlaylistReps.length) {
49300      // if there are no enabled playlists, then they have all been blacklisted or disabled
49301      // by the user through the representations api. In this case, ignore blacklisting and
49302      // fallback to what the user wants by using playlists the user has not disabled.
49303      enabledPlaylistReps = sortedPlaylistReps.filter(function (rep) {
49304        return !Playlist.isDisabled(rep.playlist);
49305      });
49306    } // filter out any variant that has greater effective bitrate
49307    // than the current estimated bandwidth
49308
49309
49310    var bandwidthPlaylistReps = enabledPlaylistReps.filter(function (rep) {
49311      return rep.bandwidth * Config.BANDWIDTH_VARIANCE < playerBandwidth;
49312    });
49313    var highestRemainingBandwidthRep = bandwidthPlaylistReps[bandwidthPlaylistReps.length - 1]; // get all of the renditions with the same (highest) bandwidth
49314    // and then taking the very first element
49315
49316    var bandwidthBestRep = bandwidthPlaylistReps.filter(function (rep) {
49317      return rep.bandwidth === highestRemainingBandwidthRep.bandwidth;
49318    })[0]; // if we're not going to limit renditions by player size, make an early decision.
49319
49320    if (limitRenditionByPlayerDimensions === false) {
49321      var _chosenRep = bandwidthBestRep || enabledPlaylistReps[0] || sortedPlaylistReps[0];
49322
49323      return _chosenRep ? _chosenRep.playlist : null;
49324    } // filter out playlists without resolution information
49325
49326
49327    var haveResolution = bandwidthPlaylistReps.filter(function (rep) {
49328      return rep.width && rep.height;
49329    }); // sort variants by resolution
49330
49331    stableSort(haveResolution, function (left, right) {
49332      return left.width - right.width;
49333    }); // if we have the exact resolution as the player use it
49334
49335    var resolutionBestRepList = haveResolution.filter(function (rep) {
49336      return rep.width === playerWidth && rep.height === playerHeight;
49337    });
49338    highestRemainingBandwidthRep = resolutionBestRepList[resolutionBestRepList.length - 1]; // ensure that we pick the highest bandwidth variant that have exact resolution
49339
49340    var resolutionBestRep = resolutionBestRepList.filter(function (rep) {
49341      return rep.bandwidth === highestRemainingBandwidthRep.bandwidth;
49342    })[0];
49343    var resolutionPlusOneList = void 0;
49344    var resolutionPlusOneSmallest = void 0;
49345    var resolutionPlusOneRep = void 0; // find the smallest variant that is larger than the player
49346    // if there is no match of exact resolution
49347
49348    if (!resolutionBestRep) {
49349      resolutionPlusOneList = haveResolution.filter(function (rep) {
49350        return rep.width > playerWidth || rep.height >
vendor: 131,030 bytes, lines 49350-52891
49350 playerHeight;
49351      }); // find all the variants have the same smallest resolution
49352
49353      resolutionPlusOneSmallest = resolutionPlusOneList.filter(function (rep) {
49354        return rep.width === resolutionPlusOneList[0].width && rep.height === resolutionPlusOneList[0].height;
49355      }); // ensure that we also pick the highest bandwidth variant that
49356      // is just-larger-than the video player
49357
49358      highestRemainingBandwidthRep = resolutionPlusOneSmallest[resolutionPlusOneSmallest.length - 1];
49359      resolutionPlusOneRep = resolutionPlusOneSmallest.filter(function (rep) {
49360        return rep.bandwidth === highestRemainingBandwidthRep.bandwidth;
49361      })[0];
49362    } // fallback chain of variants
49363
49364
49365    var chosenRep = resolutionPlusOneRep || resolutionBestRep || bandwidthBestRep || enabledPlaylistReps[0] || sortedPlaylistReps[0];
49366    return chosenRep ? chosenRep.playlist : null;
49367  }; // Playlist Selectors
49368
49369  /**
49370   * Chooses the appropriate media playlist based on the most recent
49371   * bandwidth estimate and the player size.
49372   *
49373   * Expects to be called within the context of an instance of HlsHandler
49374   *
49375   * @return {Playlist} the highest bitrate playlist less than the
49376   * currently detected bandwidth, accounting for some amount of
49377   * bandwidth variance
49378   */
49379
49380
49381  var lastBandwidthSelector = function lastBandwidthSelector() {
49382    return simpleSelector(this.playlists.master, this.systemBandwidth, parseInt(safeGetComputedStyle(this.tech_.el(), 'width'), 10), parseInt(safeGetComputedStyle(this.tech_.el(), 'height'), 10), this.limitRenditionByPlayerDimensions);
49383  };
49384  /**
49385   * Chooses the appropriate media playlist based on the potential to rebuffer
49386   *
49387   * @param {Object} settings
49388   *        Object of information required to use this selector
49389   * @param {Object} settings.master
49390   *        Object representation of the master manifest
49391   * @param {Number} settings.currentTime
49392   *        The current time of the player
49393   * @param {Number} settings.bandwidth
49394   *        Current measured bandwidth
49395   * @param {Number} settings.duration
49396   *        Duration of the media
49397   * @param {Number} settings.segmentDuration
49398   *        Segment duration to be used in round trip time calculations
49399   * @param {Number} settings.timeUntilRebuffer
49400   *        Time left in seconds until the player has to rebuffer
49401   * @param {Number} settings.currentTimeline
49402   *        The current timeline segments are being loaded from
49403   * @param {SyncController} settings.syncController
49404   *        SyncController for determining if we have a sync point for a given playlist
49405   * @return {Object|null}
49406   *         {Object} return.playlist
49407   *         The highest bandwidth playlist with the least amount of rebuffering
49408   *         {Number} return.rebufferingImpact
49409   *         The amount of time in seconds switching to this playlist will rebuffer. A
49410   *         negative value means that switching will cause zero rebuffering.
49411   */
49412
49413
49414  var minRebufferMaxBandwidthSelector = function minRebufferMaxBandwidthSelector(settings) {
49415    var master = settings.master,
49416        currentTime = settings.currentTime,
49417        bandwidth = settings.bandwidth,
49418        duration$$1 = settings.duration,
49419        segmentDuration = settings.segmentDuration,
49420        timeUntilRebuffer = settings.timeUntilRebuffer,
49421        currentTimeline = settings.currentTimeline,
49422        syncController = settings.syncController; // filter out any playlists that have been excluded due to
49423    // incompatible configurations
49424
49425    var compatiblePlaylists = master.playlists.filter(function (playlist) {
49426      return !Playlist.isIncompatible(playlist);
49427    }); // filter out any playlists that have been disabled manually through the representations
49428    // api or blacklisted temporarily due to playback errors.
49429
49430    var enabledPlaylists = compatiblePlaylists.filter(Playlist.isEnabled);
49431
49432    if (!enabledPlaylists.length) {
49433      // if there are no enabled playlists, then they have all been blacklisted or disabled
49434      // by the user through the representations api. In this case, ignore blacklisting and
49435      // fallback to what the user wants by using playlists the user has not disabled.
49436      enabledPlaylists = compatiblePlaylists.filter(function (playlist) {
49437        return !Playlist.isDisabled(playlist);
49438      });
49439    }
49440
49441    var bandwidthPlaylists = enabledPlaylists.filter(Playlist.hasAttribute.bind(null, 'BANDWIDTH'));
49442    var rebufferingEstimates = bandwidthPlaylists.map(function (playlist) {
49443      var syncPoint = syncController.getSyncPoint(playlist, duration$$1, currentTimeline, currentTime); // If there is no sync point for this playlist, switching to it will require a
49444      // sync request first. This will double the request time
49445
49446      var numRequests = syncPoint ? 1 : 2;
49447      var requestTimeEstimate = Playlist.estimateSegmentRequestTime(segmentDuration, bandwidth, playlist);
49448      var rebufferingImpact = requestTimeEstimate * numRequests - timeUntilRebuffer;
49449      return {
49450        playlist: playlist,
49451        rebufferingImpact: rebufferingImpact
49452      };
49453    });
49454    var noRebufferingPlaylists = rebufferingEstimates.filter(function (estimate) {
49455      return estimate.rebufferingImpact <= 0;
49456    }); // Sort by bandwidth DESC
49457
49458    stableSort(noRebufferingPlaylists, function (a, b) {
49459      return comparePlaylistBandwidth(b.playlist, a.playlist);
49460    });
49461
49462    if (noRebufferingPlaylists.length) {
49463      return noRebufferingPlaylists[0];
49464    }
49465
49466    stableSort(rebufferingEstimates, function (a, b) {
49467      return a.rebufferingImpact - b.rebufferingImpact;
49468    });
49469    return rebufferingEstimates[0] || null;
49470  };
49471  /**
49472   * Chooses the appropriate media playlist, which in this case is the lowest bitrate
49473   * one with video.  If no renditions with video exist, return the lowest audio rendition.
49474   *
49475   * Expects to be called within the context of an instance of HlsHandler
49476   *
49477   * @return {Object|null}
49478   *         {Object} return.playlist
49479   *         The lowest bitrate playlist that contains a video codec.  If no such rendition
49480   *         exists pick the lowest audio rendition.
49481   */
49482
49483
49484  var lowestBitrateCompatibleVariantSelector = function lowestBitrateCompatibleVariantSelector() {
49485    // filter out any playlists that have been excluded due to
49486    // incompatible configurations or playback errors
49487    var playlists = this.playlists.master.playlists.filter(Playlist.isEnabled); // Sort ascending by bitrate
49488
49489    stableSort(playlists, function (a, b) {
49490      return comparePlaylistBandwidth(a, b);
49491    }); // Parse and assume that playlists with no video codec have no video
49492    // (this is not necessarily true, although it is generally true).
49493    //
49494    // If an entire manifest has no valid videos everything will get filtered
49495    // out.
49496
49497    var playlistsWithVideo = playlists.filter(function (playlist) {
49498      return parseCodecs(playlist.attributes.CODECS).videoCodec;
49499    });
49500    return playlistsWithVideo[0] || null;
49501  };
49502  /**
49503   * Create captions text tracks on video.js if they do not exist
49504   *
49505   * @param {Object} inbandTextTracks a reference to current inbandTextTracks
49506   * @param {Object} tech the video.js tech
49507   * @param {Object} captionStreams the caption streams to create
49508   * @private
49509   */
49510
49511
49512  var createCaptionsTrackIfNotExists = function createCaptionsTrackIfNotExists(inbandTextTracks, tech, captionStreams) {
49513    for (var trackId in captionStreams) {
49514      if (!inbandTextTracks[trackId]) {
49515        tech.trigger({
49516          type: 'usage',
49517          name: 'hls-608'
49518        });
49519        var track = tech.textTracks().getTrackById(trackId);
49520
49521        if (track) {
49522          // Resuse an existing track with a CC# id because this was
49523          // very likely created by videojs-contrib-hls from information
49524          // in the m3u8 for us to use
49525          inbandTextTracks[trackId] = track;
49526        } else {
49527          // Otherwise, create a track with the default `CC#` label and
49528          // without a language
49529          inbandTextTracks[trackId] = tech.addRemoteTextTrack({
49530            kind: 'captions',
49531            id: trackId,
49532            label: trackId
49533          }, false).track;
49534        }
49535      }
49536    }
49537  };
49538
49539  var addCaptionData = function addCaptionData(_ref) {
49540    var inbandTextTracks = _ref.inbandTextTracks,
49541        captionArray = _ref.captionArray,
49542        timestampOffset = _ref.timestampOffset;
49543
49544    if (!captionArray) {
49545      return;
49546    }
49547
49548    var Cue = window.WebKitDataCue || window.VTTCue;
49549    captionArray.forEach(function (caption) {
49550      var track = caption.stream;
49551      var startTime = caption.startTime;
49552      var endTime = caption.endTime;
49553
49554      if (!inbandTextTracks[track]) {
49555        return;
49556      }
49557
49558      startTime += timestampOffset;
49559      endTime += timestampOffset;
49560      inbandTextTracks[track].addCue(new Cue(startTime, endTime, caption.text));
49561    });
49562  };
49563  /**
49564   * @file segment-loader.js
49565   */
49566  // in ms
49567
49568
49569  var CHECK_BUFFER_DELAY = 500;
49570  /**
49571   * Determines if we should call endOfStream on the media source based
49572   * on the state of the buffer or if appened segment was the final
49573   * segment in the playlist.
49574   *
49575   * @param {Object} playlist a media playlist object
49576   * @param {Object} mediaSource the MediaSource object
49577   * @param {Number} segmentIndex the index of segment we last appended
49578   * @returns {Boolean} do we need to call endOfStream on the MediaSource
49579   */
49580
49581  var detectEndOfStream = function detectEndOfStream(playlist, mediaSource, segmentIndex) {
49582    if (!playlist || !mediaSource) {
49583      return false;
49584    }
49585
49586    var segments = playlist.segments; // determine a few boolean values to help make the branch below easier
49587    // to read
49588
49589    var appendedLastSegment = segmentIndex === segments.length; // if we've buffered to the end of the video, we need to call endOfStream
49590    // so that MediaSources can trigger the `ended` event when it runs out of
49591    // buffered data instead of waiting for me
49592
49593    return playlist.endList && mediaSource.readyState === 'open' && appendedLastSegment;
49594  };
49595
49596  var finite = function finite(num) {
49597    return typeof num === 'number' && isFinite(num);
49598  };
49599
49600  var illegalMediaSwitch = function illegalMediaSwitch(loaderType, startingMedia, newSegmentMedia) {
49601    // Although these checks should most likely cover non 'main' types, for now it narrows
49602    // the scope of our checks.
49603    if (loaderType !== 'main' || !startingMedia || !newSegmentMedia) {
49604      return null;
49605    }
49606
49607    if (!newSegmentMedia.containsAudio && !newSegmentMedia.containsVideo) {
49608      return 'Neither audio nor video found in segment.';
49609    }
49610
49611    if (startingMedia.containsVideo && !newSegmentMedia.containsVideo) {
49612      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.';
49613    }
49614
49615    if (!startingMedia.containsVideo && newSegmentMedia.containsVideo) {
49616      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.';
49617    }
49618
49619    return null;
49620  };
49621  /**
49622   * Calculates a time value that is safe to remove from the back buffer without interupting
49623   * playback.
49624   *
49625   * @param {TimeRange} seekable
49626   *        The current seekable range
49627   * @param {Number} currentTime
49628   *        The current time of the player
49629   * @param {Number} targetDuration
49630   *        The target duration of the current playlist
49631   * @return {Number}
49632   *         Time that is safe to remove from the back buffer without interupting playback
49633   */
49634
49635
49636  var safeBackBufferTrimTime = function safeBackBufferTrimTime(seekable$$1, currentTime, targetDuration) {
49637    var removeToTime = void 0;
49638
49639    if (seekable$$1.length && seekable$$1.start(0) > 0 && seekable$$1.start(0) < currentTime) {
49640      // If we have a seekable range use that as the limit for what can be removed safely
49641      removeToTime = seekable$$1.start(0);
49642    } else {
49643      // otherwise remove anything older than 30 seconds before the current play head
49644      removeToTime = currentTime - 30;
49645    } // Don't allow removing from the buffer within target duration of current time
49646    // to avoid the possibility of removing the GOP currently being played which could
49647    // cause playback stalls.
49648
49649
49650    return Math.min(removeToTime, currentTime - targetDuration);
49651  };
49652
49653  var segmentInfoString = function segmentInfoString(segmentInfo) {
49654    var _segmentInfo$segment = segmentInfo.segment,
49655        start = _segmentInfo$segment.start,
49656        end = _segmentInfo$segment.end,
49657        _segmentInfo$playlist = segmentInfo.playlist,
49658        seq = _segmentInfo$playlist.mediaSequence,
49659        id = _segmentInfo$playlist.id,
49660        _segmentInfo$playlist2 = _segmentInfo$playlist.segments,
49661        segments = _segmentInfo$playlist2 === undefined ? [] : _segmentInfo$playlist2,
49662        index = segmentInfo.mediaIndex,
49663        timeline = segmentInfo.timeline;
49664    return ['appending [' + index + '] of [' + seq + ', ' + (seq + segments.length) + '] from playlist [' + id + ']', '[' + start + ' => ' + end + '] in timeline [' + timeline + ']'].join(' ');
49665  };
49666  /**
49667   * An object that manages segment loading and appending.
49668   *
49669   * @class SegmentLoader
49670   * @param {Object} options required and optional options
49671   * @extends videojs.EventTarget
49672   */
49673
49674
49675  var SegmentLoader = function (_videojs$EventTarget) {
49676    inherits$1(SegmentLoader, _videojs$EventTarget);
49677
49678    function SegmentLoader(settings) {
49679      classCallCheck$1(this, SegmentLoader); // check pre-conditions
49680
49681      var _this = possibleConstructorReturn$1(this, (SegmentLoader.__proto__ || Object.getPrototypeOf(SegmentLoader)).call(this));
49682
49683      if (!settings) {
49684        throw new TypeError('Initialization settings are required');
49685      }
49686
49687      if (typeof settings.currentTime !== 'function') {
49688        throw new TypeError('No currentTime getter specified');
49689      }
49690
49691      if (!settings.mediaSource) {
49692        throw new TypeError('No MediaSource specified');
49693      } // public properties
49694
49695
49696      _this.bandwidth = settings.bandwidth;
49697      _this.throughput = {
49698        rate: 0,
49699        count: 0
49700      };
49701      _this.roundTrip = NaN;
49702
49703      _this.resetStats_();
49704
49705      _this.mediaIndex = null; // private settings
49706
49707      _this.hasPlayed_ = settings.hasPlayed;
49708      _this.currentTime_ = settings.currentTime;
49709      _this.seekable_ = settings.seekable;
49710      _this.seeking_ = settings.seeking;
49711      _this.duration_ = settings.duration;
49712      _this.mediaSource_ = settings.mediaSource;
49713      _this.hls_ = settings.hls;
49714      _this.loaderType_ = settings.loaderType;
49715      _this.startingMedia_ = void 0;
49716      _this.segmentMetadataTrack_ = settings.segmentMetadataTrack;
49717      _this.goalBufferLength_ = settings.goalBufferLength;
49718      _this.sourceType_ = settings.sourceType;
49719      _this.inbandTextTracks_ = settings.inbandTextTracks;
49720      _this.state_ = 'INIT'; // private instance variables
49721
49722      _this.checkBufferTimeout_ = null;
49723      _this.error_ = void 0;
49724      _this.currentTimeline_ = -1;
49725      _this.pendingSegment_ = null;
49726      _this.mimeType_ = null;
49727      _this.sourceUpdater_ = null;
49728      _this.xhrOptions_ = null; // Fragmented mp4 playback
49729
49730      _this.activeInitSegmentId_ = null;
49731      _this.initSegments_ = {}; // HLSe playback
49732
49733      _this.cacheEncryptionKeys_ = settings.cacheEncryptionKeys;
49734      _this.keyCache_ = {}; // Fmp4 CaptionParser
49735
49736      if (_this.loaderType_ === 'main') {
49737        _this.captionParser_ = new captionParser();
49738      } else {
49739        _this.captionParser_ = null;
49740      }
49741
49742      _this.decrypter_ = settings.decrypter; // Manages the tracking and generation of sync-points, mappings
49743      // between a time in the display time and a segment index within
49744      // a playlist
49745
49746      _this.syncController_ = settings.syncController;
49747      _this.syncPoint_ = {
49748        segmentIndex: 0,
49749        time: 0
49750      };
49751
49752      _this.syncController_.on('syncinfoupdate', function () {
49753        return _this.trigger('syncinfoupdate');
49754      });
49755
49756      _this.mediaSource_.addEventListener('sourceopen', function () {
49757        return _this.ended_ = false;
49758      }); // ...for determining the fetch location
49759
49760
49761      _this.fetchAtBuffer_ = false;
49762      _this.logger_ = logger('SegmentLoader[' + _this.loaderType_ + ']');
49763      Object.defineProperty(_this, 'state', {
49764        get: function get$$1() {
49765          return this.state_;
49766        },
49767        set: function set$$1(newState) {
49768          if (newState !== this.state_) {
49769            this.logger_(this.state_ + ' -> ' + newState);
49770            this.state_ = newState;
49771          }
49772        }
49773      });
49774      return _this;
49775    }
49776    /**
49777     * reset all of our media stats
49778     *
49779     * @private
49780     */
49781
49782
49783    createClass$1(SegmentLoader, [{
49784      key: 'resetStats_',
49785      value: function resetStats_() {
49786        this.mediaBytesTransferred = 0;
49787        this.mediaRequests = 0;
49788        this.mediaRequestsAborted = 0;
49789        this.mediaRequestsTimedout = 0;
49790        this.mediaRequestsErrored = 0;
49791        this.mediaTransferDuration = 0;
49792        this.mediaSecondsLoaded = 0;
49793      }
49794      /**
49795       * dispose of the SegmentLoader and reset to the default state
49796       */
49797
49798    }, {
49799      key: 'dispose',
49800      value: function dispose() {
49801        this.state = 'DISPOSED';
49802        this.pause();
49803        this.abort_();
49804
49805        if (this.sourceUpdater_) {
49806          this.sourceUpdater_.dispose();
49807        }
49808
49809        this.resetStats_();
49810
49811        if (this.captionParser_) {
49812          this.captionParser_.reset();
49813        }
49814      }
49815      /**
49816       * abort anything that is currently doing on with the SegmentLoader
49817       * and reset to a default state
49818       */
49819
49820    }, {
49821      key: 'abort',
49822      value: function abort() {
49823        if (this.state !== 'WAITING') {
49824          if (this.pendingSegment_) {
49825            this.pendingSegment_ = null;
49826          }
49827
49828          return;
49829        }
49830
49831        this.abort_(); // We aborted the requests we were waiting on, so reset the loader's state to READY
49832        // since we are no longer "waiting" on any requests. XHR callback is not always run
49833        // when the request is aborted. This will prevent the loader from being stuck in the
49834        // WAITING state indefinitely.
49835
49836        this.state = 'READY'; // don't wait for buffer check timeouts to begin fetching the
49837        // next segment
49838
49839        if (!this.paused()) {
49840          this.monitorBuffer_();
49841        }
49842      }
49843      /**
49844       * abort all pending xhr requests and null any pending segements
49845       *
49846       * @private
49847       */
49848
49849    }, {
49850      key: 'abort_',
49851      value: function abort_() {
49852        if (this.pendingSegment_) {
49853          this.pendingSegment_.abortRequests();
49854        } // clear out the segment being processed
49855
49856
49857        this.pendingSegment_ = null;
49858      }
49859      /**
49860       * set an error on the segment loader and null out any pending segements
49861       *
49862       * @param {Error} error the error to set on the SegmentLoader
49863       * @return {Error} the error that was set or that is currently set
49864       */
49865
49866    }, {
49867      key: 'error',
49868      value: function error(_error) {
49869        if (typeof _error !== 'undefined') {
49870          this.error_ = _error;
49871        }
49872
49873        this.pendingSegment_ = null;
49874        return this.error_;
49875      }
49876    }, {
49877      key: 'endOfStream',
49878      value: function endOfStream() {
49879        this.ended_ = true;
49880        this.pause();
49881        this.trigger('ended');
49882      }
49883      /**
49884       * Indicates which time ranges are buffered
49885       *
49886       * @return {TimeRange}
49887       *         TimeRange object representing the current buffered ranges
49888       */
49889
49890    }, {
49891      key: 'buffered_',
49892      value: function buffered_() {
49893        if (!this.sourceUpdater_) {
49894          return videojs$1.createTimeRanges();
49895        }
49896
49897        return this.sourceUpdater_.buffered();
49898      }
49899      /**
49900       * Gets and sets init segment for the provided map
49901       *
49902       * @param {Object} map
49903       *        The map object representing the init segment to get or set
49904       * @param {Boolean=} set
49905       *        If true, the init segment for the provided map should be saved
49906       * @return {Object}
49907       *         map object for desired init segment
49908       */
49909
49910    }, {
49911      key: 'initSegment',
49912      value: function initSegment(map) {
49913        var set$$1 = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false;
49914
49915        if (!map) {
49916          return null;
49917        }
49918
49919        var id = initSegmentId(map);
49920        var storedMap = this.initSegments_[id];
49921
49922        if (set$$1 && !storedMap && map.bytes) {
49923          this.initSegments_[id] = storedMap = {
49924            resolvedUri: map.resolvedUri,
49925            byterange: map.byterange,
49926            bytes: map.bytes,
49927            timescales: map.timescales,
49928            videoTrackIds: map.videoTrackIds
49929          };
49930        }
49931
49932        return storedMap || map;
49933      }
49934      /**
49935       * Gets and sets key for the provided key
49936       *
49937       * @param {Object} key
49938       *        The key object representing the key to get or set
49939       * @param {Boolean=} set
49940       *        If true, the key for the provided key should be saved
49941       * @return {Object}
49942       *         Key object for desired key
49943       */
49944
49945    }, {
49946      key: 'segmentKey',
49947      value: function segmentKey(key) {
49948        var set$$1 = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false;
49949
49950        if (!key) {
49951          return null;
49952        }
49953
49954        var id = segmentKeyId(key);
49955        var storedKey = this.keyCache_[id]; // TODO: We should use the HTTP Expires header to invalidate our cache per
49956        // https://tools.ietf.org/html/draft-pantos-http-live-streaming-23#section-6.2.3
49957
49958        if (this.cacheEncryptionKeys_ && set$$1 && !storedKey && key.bytes) {
49959          this.keyCache_[id] = storedKey = {
49960            resolvedUri: key.resolvedUri,
49961            bytes: key.bytes
49962          };
49963        }
49964
49965        var result = {
49966          resolvedUri: (storedKey || key).resolvedUri
49967        };
49968
49969        if (storedKey) {
49970          result.bytes = storedKey.bytes;
49971        }
49972
49973        return result;
49974      }
49975      /**
49976       * Returns true if all configuration required for loading is present, otherwise false.
49977       *
49978       * @return {Boolean} True if the all configuration is ready for loading
49979       * @private
49980       */
49981
49982    }, {
49983      key: 'couldBeginLoading_',
49984      value: function couldBeginLoading_() {
49985        return this.playlist_ && ( // the source updater is created when init_ is called, so either having a
49986        // source updater or being in the INIT state with a mimeType is enough
49987        // to say we have all the needed configuration to start loading.
49988        this.sourceUpdater_ || this.mimeType_ && this.state === 'INIT') && !this.paused();
49989      }
49990      /**
49991       * load a playlist and start to fill the buffer
49992       */
49993
49994    }, {
49995      key: 'load',
49996      value: function load() {
49997        // un-pause
49998        this.monitorBuffer_(); // if we don't have a playlist yet, keep waiting for one to be
49999        // specified
50000
50001        if (!this.playlist_) {
50002          return;
50003        } // not sure if this is the best place for this
50004
50005
50006        this.syncController_.setDateTimeMapping(this.playlist_); // if all the configuration is ready, initialize and begin loading
50007
50008        if (this.state === 'INIT' && this.couldBeginLoading_()) {
50009          return this.init_();
50010        } // if we're in the middle of processing a segment already, don't
50011        // kick off an additional segment request
50012
50013
50014        if (!this.couldBeginLoading_() || this.state !== 'READY' && this.state !== 'INIT') {
50015          return;
50016        }
50017
50018        this.state = 'READY';
50019      }
50020      /**
50021       * Once all the starting parameters have been specified, begin
50022       * operation. This method should only be invoked from the INIT
50023       * state.
50024       *
50025       * @private
50026       */
50027
50028    }, {
50029      key: 'init_',
50030      value: function init_() {
50031        this.state = 'READY';
50032        this.sourceUpdater_ = new SourceUpdater(this.mediaSource_, this.mimeType_, this.loaderType_, this.sourceBufferEmitter_);
50033        this.resetEverything();
50034        return this.monitorBuffer_();
50035      }
50036      /**
50037       * set a playlist on the segment loader
50038       *
50039       * @param {PlaylistLoader} media the playlist to set on the segment loader
50040       */
50041
50042    }, {
50043      key: 'playlist',
50044      value: function playlist(newPlaylist) {
50045        var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
50046
50047        if (!newPlaylist) {
50048          return;
50049        }
50050
50051        var oldPlaylist = this.playlist_;
50052        var segmentInfo = this.pendingSegment_;
50053        this.playlist_ = newPlaylist;
50054        this.xhrOptions_ = options; // when we haven't started playing yet, the start of a live playlist
50055        // is always our zero-time so force a sync update each time the playlist
50056        // is refreshed from the server
50057
50058        if (!this.hasPlayed_()) {
50059          newPlaylist.syncInfo = {
50060            mediaSequence: newPlaylist.mediaSequence,
50061            time: 0
50062          };
50063        }
50064
50065        var oldId = null;
50066
50067        if (oldPlaylist) {
50068          if (oldPlaylist.id) {
50069            oldId = oldPlaylist.id;
50070          } else if (oldPlaylist.uri) {
50071            oldId = oldPlaylist.uri;
50072          }
50073        }
50074
50075        this.logger_('playlist update [' + oldId + ' => ' + (newPlaylist.id || newPlaylist.uri) + ']'); // in VOD, this is always a rendition switch (or we updated our syncInfo above)
50076        // in LIVE, we always want to update with new playlists (including refreshes)
50077
50078        this.trigger('syncinfoupdate'); // if we were unpaused but waiting for a playlist, start
50079        // buffering now
50080
50081        if (this.state === 'INIT' && this.couldBeginLoading_()) {
50082          return this.init_();
50083        }
50084
50085        if (!oldPlaylist || oldPlaylist.uri !== newPlaylist.uri) {
50086          if (this.mediaIndex !== null) {
50087            // we must "resync" the segment loader when we switch renditions and
50088            // the segment loader is already synced to the previous rendition
50089            this.resyncLoader();
50090          } // the rest of this function depends on `oldPlaylist` being defined
50091
50092
50093          return;
50094        } // we reloaded the same playlist so we are in a live scenario
50095        // and we will likely need to adjust the mediaIndex
50096
50097
50098        var mediaSequenceDiff = newPlaylist.mediaSequence - oldPlaylist.mediaSequence;
50099        this.logger_('live window shift [' + mediaSequenceDiff + ']'); // update the mediaIndex on the SegmentLoader
50100        // this is important because we can abort a request and this value must be
50101        // equal to the last appended mediaIndex
50102
50103        if (this.mediaIndex !== null) {
50104          this.mediaIndex -= mediaSequenceDiff;
50105        } // update the mediaIndex on the SegmentInfo object
50106        // this is important because we will update this.mediaIndex with this value
50107        // in `handleUpdateEnd_` after the segment has been successfully appended
50108
50109
50110        if (segmentInfo) {
50111          segmentInfo.mediaIndex -= mediaSequenceDiff; // we need to update the referenced segment so that timing information is
50112          // saved for the new playlist's segment, however, if the segment fell off the
50113          // playlist, we can leave the old reference and just lose the timing info
50114
50115          if (segmentInfo.mediaIndex >= 0) {
50116            segmentInfo.segment = newPlaylist.segments[segmentInfo.mediaIndex];
50117          }
50118        }
50119
50120        this.syncController_.saveExpiredSegmentInfo(oldPlaylist, newPlaylist);
50121      }
50122      /**
50123       * Prevent the loader from fetching additional segments. If there
50124       * is a segment request outstanding, it will finish processing
50125       * before the loader halts. A segment loader can be unpaused by
50126       * calling load().
50127       */
50128
50129    }, {
50130      key: 'pause',
50131      value: function pause() {
50132        if (this.checkBufferTimeout_) {
50133          window$1.clearTimeout(this.checkBufferTimeout_);
50134          this.checkBufferTimeout_ = null;
50135        }
50136      }
50137      /**
50138       * Returns whether the segment loader is fetching additional
50139       * segments when given the opportunity. This property can be
50140       * modified through calls to pause() and load().
50141       */
50142
50143    }, {
50144      key: 'paused',
50145      value: function paused() {
50146        return this.checkBufferTimeout_ === null;
50147      }
50148      /**
50149       * create/set the following mimetype on the SourceBuffer through a
50150       * SourceUpdater
50151       *
50152       * @param {String} mimeType the mime type string to use
50153       * @param {Object} sourceBufferEmitter an event emitter that fires when a source buffer
50154       * is added to the media source
50155       */
50156
50157    }, {
50158      key: 'mimeType',
50159      value: function mimeType(_mimeType, sourceBufferEmitter) {
50160        if (this.mimeType_) {
50161          return;
50162        }
50163
50164        this.mimeType_ = _mimeType;
50165        this.sourceBufferEmitter_ = sourceBufferEmitter; // if we were unpaused but waiting for a sourceUpdater, start
50166        // buffering now
50167
50168        if (this.state === 'INIT' && this.couldBeginLoading_()) {
50169          this.init_();
50170        }
50171      }
50172      /**
50173       * Delete all the buffered data and reset the SegmentLoader
50174       * @param {Function} [done] an optional callback to be executed when the remove
50175       * operation is complete
50176       */
50177
50178    }, {
50179      key: 'resetEverything',
50180      value: function resetEverything(done) {
50181        this.ended_ = false;
50182        this.resetLoader();
50183        this.remove(0, this.duration_(), done); // clears fmp4 captions
50184
50185        if (this.captionParser_) {
50186          this.captionParser_.clearAllCaptions();
50187        }
50188
50189        this.trigger('reseteverything');
50190      }
50191      /**
50192       * Force the SegmentLoader to resync and start loading around the currentTime instead
50193       * of starting at the end of the buffer
50194       *
50195       * Useful for fast quality changes
50196       */
50197
50198    }, {
50199      key: 'resetLoader',
50200      value: function resetLoader() {
50201        this.fetchAtBuffer_ = false;
50202        this.resyncLoader();
50203      }
50204      /**
50205       * Force the SegmentLoader to restart synchronization and make a conservative guess
50206       * before returning to the simple walk-forward method
50207       */
50208
50209    }, {
50210      key: 'resyncLoader',
50211      value: function resyncLoader() {
50212        this.mediaIndex = null;
50213        this.syncPoint_ = null;
50214        this.abort();
50215      }
50216      /**
50217       * Remove any data in the source buffer between start and end times
50218       * @param {Number} start - the start time of the region to remove from the buffer
50219       * @param {Number} end - the end time of the region to remove from the buffer
50220       * @param {Function} [done] - an optional callback to be executed when the remove
50221       * operation is complete
50222       */
50223
50224    }, {
50225      key: 'remove',
50226      value: function remove(start, end, done) {
50227        if (this.sourceUpdater_) {
50228          this.sourceUpdater_.remove(start, end, done);
50229        }
50230
50231        removeCuesFromTrack(start, end, this.segmentMetadataTrack_);
50232
50233        if (this.inbandTextTracks_) {
50234          for (var id in this.inbandTextTracks_) {
50235            removeCuesFromTrack(start, end, this.inbandTextTracks_[id]);
50236          }
50237        }
50238      }
50239      /**
50240       * (re-)schedule monitorBufferTick_ to run as soon as possible
50241       *
50242       * @private
50243       */
50244
50245    }, {
50246      key: 'monitorBuffer_',
50247      value: function monitorBuffer_() {
50248        if (this.checkBufferTimeout_) {
50249          window$1.clearTimeout(this.checkBufferTimeout_);
50250        }
50251
50252        this.checkBufferTimeout_ = window$1.setTimeout(this.monitorBufferTick_.bind(this), 1);
50253      }
50254      /**
50255       * As long as the SegmentLoader is in the READY state, periodically
50256       * invoke fillBuffer_().
50257       *
50258       * @private
50259       */
50260
50261    }, {
50262      key: 'monitorBufferTick_',
50263      value: function monitorBufferTick_() {
50264        if (this.state === 'READY') {
50265          this.fillBuffer_();
50266        }
50267
50268        if (this.checkBufferTimeout_) {
50269          window$1.clearTimeout(this.checkBufferTimeout_);
50270        }
50271
50272        this.checkBufferTimeout_ = window$1.setTimeout(this.monitorBufferTick_.bind(this), CHECK_BUFFER_DELAY);
50273      }
50274      /**
50275       * fill the buffer with segements unless the sourceBuffers are
50276       * currently updating
50277       *
50278       * Note: this function should only ever be called by monitorBuffer_
50279       * and never directly
50280       *
50281       * @private
50282       */
50283
50284    }, {
50285      key: 'fillBuffer_',
50286      value: function fillBuffer_() {
50287        if (this.sourceUpdater_.updating()) {
50288          return;
50289        }
50290
50291        if (!this.syncPoint_) {
50292          this.syncPoint_ = this.syncController_.getSyncPoint(this.playlist_, this.duration_(), this.currentTimeline_, this.currentTime_());
50293        } // see if we need to begin loading immediately
50294
50295
50296        var segmentInfo = this.checkBuffer_(this.buffered_(), this.playlist_, this.mediaIndex, this.hasPlayed_(), this.currentTime_(), this.syncPoint_);
50297
50298        if (!segmentInfo) {
50299          return;
50300        }
50301
50302        if (this.isEndOfStream_(segmentInfo.mediaIndex)) {
50303          this.endOfStream();
50304          return;
50305        }
50306
50307        if (segmentInfo.mediaIndex === this.playlist_.segments.length - 1 && this.mediaSource_.readyState === 'ended' && !this.seeking_()) {
50308          return;
50309        } // We will need to change timestampOffset of the sourceBuffer if:
50310        // - The segment.timeline !== this.currentTimeline
50311        //   (we are crossing a discontinuity somehow)
50312        // - The "timestampOffset" for the start of this segment is less than
50313        //   the currently set timestampOffset
50314        // Also, clear captions if we are crossing a discontinuity boundary
50315        // Previously, we changed the timestampOffset if the start of this segment
50316        // is less than the currently set timestampOffset but this isn't wanted
50317        // as it can produce bad behavior, especially around long running
50318        // live streams
50319
50320
50321        if (segmentInfo.timeline !== this.currentTimeline_) {
50322          this.syncController_.reset();
50323          segmentInfo.timestampOffset = segmentInfo.startOfSegment;
50324
50325          if (this.captionParser_) {
50326            this.captionParser_.clearAllCaptions();
50327          }
50328        }
50329
50330        this.loadSegment_(segmentInfo);
50331      }
50332      /**
50333       * Determines if this segment loader is at the end of it's stream.
50334       *
50335       * @param {Number} mediaIndex the index of segment we last appended
50336       * @param {Object} [playlist=this.playlist_] a media playlist object
50337       * @returns {Boolean} true if at end of stream, false otherwise.
50338       */
50339
50340    }, {
50341      key: 'isEndOfStream_',
50342      value: function isEndOfStream_(mediaIndex) {
50343        var playlist = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : this.playlist_;
50344        return detectEndOfStream(playlist, this.mediaSource_, mediaIndex) && !this.sourceUpdater_.updating();
50345      }
50346      /**
50347       * Determines what segment request should be made, given current playback
50348       * state.
50349       *
50350       * @param {TimeRanges} buffered - the state of the buffer
50351       * @param {Object} playlist - the playlist object to fetch segments from
50352       * @param {Number} mediaIndex - the previous mediaIndex fetched or null
50353       * @param {Boolean} hasPlayed - a flag indicating whether we have played or not
50354       * @param {Number} currentTime - the playback position in seconds
50355       * @param {Object} syncPoint - a segment info object that describes the
50356       * @returns {Object} a segment request object that describes the segment to load
50357       */
50358
50359    }, {
50360      key: 'checkBuffer_',
50361      value: function checkBuffer_(buffered, playlist, mediaIndex, hasPlayed, currentTime, syncPoint) {
50362        var lastBufferedEnd = 0;
50363        var startOfSegment = void 0;
50364
50365        if (buffered.length) {
50366          lastBufferedEnd = buffered.end(buffered.length - 1);
50367        }
50368
50369        var bufferedTime = Math.max(0, lastBufferedEnd - currentTime);
50370
50371        if (!playlist.segments.length) {
50372          return null;
50373        } // if there is plenty of content buffered, and the video has
50374        // been played before relax for awhile
50375
50376
50377        if (bufferedTime >= this.goalBufferLength_()) {
50378          return null;
50379        } // if the video has not yet played once, and we already have
50380        // one segment downloaded do nothing
50381
50382
50383        if (!hasPlayed && bufferedTime >= 1) {
50384          return null;
50385        } // When the syncPoint is null, there is no way of determining a good
50386        // conservative segment index to fetch from
50387        // The best thing to do here is to get the kind of sync-point data by
50388        // making a request
50389
50390
50391        if (syncPoint === null) {
50392          mediaIndex = this.getSyncSegmentCandidate_(playlist);
50393          return this.generateSegmentInfo_(playlist, mediaIndex, null, true);
50394        } // Under normal playback conditions fetching is a simple walk forward
50395
50396
50397        if (mediaIndex !== null) {
50398          var segment = playlist.segments[mediaIndex];
50399          startOfSegment = lastBufferedEnd;
50400          return this.generateSegmentInfo_(playlist, mediaIndex + 1, startOfSegment, false);
50401        } // There is a sync-point but the lack of a mediaIndex indicates that
50402        // we need to make a good conservative guess about which segment to
50403        // fetch
50404
50405
50406        if (this.fetchAtBuffer_) {
50407          // Find the segment containing the end of the buffer
50408          var mediaSourceInfo = Playlist.getMediaInfoForTime(playlist, lastBufferedEnd, syncPoint.segmentIndex, syncPoint.time);
50409          mediaIndex = mediaSourceInfo.mediaIndex;
50410          startOfSegment = mediaSourceInfo.startTime;
50411        } else {
50412          // Find the segment containing currentTime
50413          var _mediaSourceInfo = Playlist.getMediaInfoForTime(playlist, currentTime, syncPoint.segmentIndex, syncPoint.time);
50414
50415          mediaIndex = _mediaSourceInfo.mediaIndex;
50416          startOfSegment = _mediaSourceInfo.startTime;
50417        }
50418
50419        return this.generateSegmentInfo_(playlist, mediaIndex, startOfSegment, false);
50420      }
50421      /**
50422       * The segment loader has no recourse except to fetch a segment in the
50423       * current playlist and use the internal timestamps in that segment to
50424       * generate a syncPoint. This function returns a good candidate index
50425       * for that process.
50426       *
50427       * @param {Object} playlist - the playlist object to look for a
50428       * @returns {Number} An index of a segment from the playlist to load
50429       */
50430
50431    }, {
50432      key: 'getSyncSegmentCandidate_',
50433      value: function getSyncSegmentCandidate_(playlist) {
50434        var _this2 = this;
50435
50436        if (this.currentTimeline_ === -1) {
50437          return 0;
50438        }
50439
50440        var segmentIndexArray = playlist.segments.map(function (s, i) {
50441          return {
50442            timeline: s.timeline,
50443            segmentIndex: i
50444          };
50445        }).filter(function (s) {
50446          return s.timeline === _this2.currentTimeline_;
50447        });
50448
50449        if (segmentIndexArray.length) {
50450          return segmentIndexArray[Math.min(segmentIndexArray.length - 1, 1)].segmentIndex;
50451        }
50452
50453        return Math.max(playlist.segments.length - 1, 0);
50454      }
50455    }, {
50456      key: 'generateSegmentInfo_',
50457      value: function generateSegmentInfo_(playlist, mediaIndex, startOfSegment, isSyncRequest) {
50458        if (mediaIndex < 0 || mediaIndex >= playlist.segments.length) {
50459          return null;
50460        }
50461
50462        var segment = playlist.segments[mediaIndex];
50463        return {
50464          requestId: 'segment-loader-' + Math.random(),
50465          // resolve the segment URL relative to the playlist
50466          uri: segment.resolvedUri,
50467          // the segment's mediaIndex at the time it was requested
50468          mediaIndex: mediaIndex,
50469          // whether or not to update the SegmentLoader's state with this
50470          // segment's mediaIndex
50471          isSyncRequest: isSyncRequest,
50472          startOfSegment: startOfSegment,
50473          // the segment's playlist
50474          playlist: playlist,
50475          // unencrypted bytes of the segment
50476          bytes: null,
50477          // when a key is defined for this segment, the encrypted bytes
50478          encryptedBytes: null,
50479          // The target timestampOffset for this segment when we append it
50480          // to the source buffer
50481          timestampOffset: null,
50482          // The timeline that the segment is in
50483          timeline: segment.timeline,
50484          // The expected duration of the segment in seconds
50485          duration: segment.duration,
50486          // retain the segment in case the playlist updates while doing an async process
50487          segment: segment
50488        };
50489      }
50490      /**
50491       * Determines if the network has enough bandwidth to complete the current segment
50492       * request in a timely manner. If not, the request will be aborted early and bandwidth
50493       * updated to trigger a playlist switch.
50494       *
50495       * @param {Object} stats
50496       *        Object containing stats about the request timing and size
50497       * @return {Boolean} True if the request was aborted, false otherwise
50498       * @private
50499       */
50500
50501    }, {
50502      key: 'abortRequestEarly_',
50503      value: function abortRequestEarly_(stats) {
50504        if (this.hls_.tech_.paused() || // Don't abort if the current playlist is on the lowestEnabledRendition
50505        // TODO: Replace using timeout with a boolean indicating whether this playlist is
50506        //       the lowestEnabledRendition.
50507        !this.xhrOptions_.timeout || // Don't abort if we have no bandwidth information to estimate segment sizes
50508        !this.playlist_.attributes.BANDWIDTH) {
50509          return false;
50510        } // Wait at least 1 second since the first byte of data has been received before
50511        // using the calculated bandwidth from the progress event to allow the bitrate
50512        // to stabilize
50513
50514
50515        if (Date.now() - (stats.firstBytesReceivedAt || Date.now()) < 1000) {
50516          return false;
50517        }
50518
50519        var currentTime = this.currentTime_();
50520        var measuredBandwidth = stats.bandwidth;
50521        var segmentDuration = this.pendingSegment_.duration;
50522        var requestTimeRemaining = Playlist.estimateSegmentRequestTime(segmentDuration, measuredBandwidth, this.playlist_, stats.bytesReceived); // Subtract 1 from the timeUntilRebuffer so we still consider an early abort
50523        // if we are only left with less than 1 second when the request completes.
50524        // A negative timeUntilRebuffering indicates we are already rebuffering
50525
50526        var timeUntilRebuffer$$1 = timeUntilRebuffer(this.buffered_(), currentTime, this.hls_.tech_.playbackRate()) - 1; // Only consider aborting early if the estimated time to finish the download
50527        // is larger than the estimated time until the player runs out of forward buffer
50528
50529        if (requestTimeRemaining <= timeUntilRebuffer$$1) {
50530          return false;
50531        }
50532
50533        var switchCandidate = minRebufferMaxBandwidthSelector({
50534          master: this.hls_.playlists.master,
50535          currentTime: currentTime,
50536          bandwidth: measuredBandwidth,
50537          duration: this.duration_(),
50538          segmentDuration: segmentDuration,
50539          timeUntilRebuffer: timeUntilRebuffer$$1,
50540          currentTimeline: this.currentTimeline_,
50541          syncController: this.syncController_
50542        });
50543
50544        if (!switchCandidate) {
50545          return;
50546        }
50547
50548        var rebufferingImpact = requestTimeRemaining - timeUntilRebuffer$$1;
50549        var timeSavedBySwitching = rebufferingImpact - switchCandidate.rebufferingImpact;
50550        var minimumTimeSaving = 0.5; // If we are already rebuffering, increase the amount of variance we add to the
50551        // potential round trip time of the new request so that we are not too aggressive
50552        // with switching to a playlist that might save us a fraction of a second.
50553
50554        if (timeUntilRebuffer$$1 <= TIME_FUDGE_FACTOR) {
50555          minimumTimeSaving = 1;
50556        }
50557
50558        if (!switchCandidate.playlist || switchCandidate.playlist.uri === this.playlist_.uri || timeSavedBySwitching < minimumTimeSaving) {
50559          return false;
50560        } // set the bandwidth to that of the desired playlist being sure to scale by
50561        // BANDWIDTH_VARIANCE and add one so the playlist selector does not exclude it
50562        // don't trigger a bandwidthupdate as the bandwidth is artifial
50563
50564
50565        this.bandwidth = switchCandidate.playlist.attributes.BANDWIDTH * Config.BANDWIDTH_VARIANCE + 1;
50566        this.abort();
50567        this.trigger('earlyabort');
50568        return true;
50569      }
50570      /**
50571       * XHR `progress` event handler
50572       *
50573       * @param {Event}
50574       *        The XHR `progress` event
50575       * @param {Object} simpleSegment
50576       *        A simplified segment object copy
50577       * @private
50578       */
50579
50580    }, {
50581      key: 'handleProgress_',
50582      value: function handleProgress_(event, simpleSegment) {
50583        if (!this.pendingSegment_ || simpleSegment.requestId !== this.pendingSegment_.requestId || this.abortRequestEarly_(simpleSegment.stats)) {
50584          return;
50585        }
50586
50587        this.trigger('progress');
50588      }
50589      /**
50590       * load a specific segment from a request into the buffer
50591       *
50592       * @private
50593       */
50594
50595    }, {
50596      key: 'loadSegment_',
50597      value: function loadSegment_(segmentInfo) {
50598        this.state = 'WAITING';
50599        this.pendingSegment_ = segmentInfo;
50600        this.trimBackBuffer_(segmentInfo);
50601        segmentInfo.abortRequests = mediaSegmentRequest(this.hls_.xhr, this.xhrOptions_, this.decrypter_, this.captionParser_, this.createSimplifiedSegmentObj_(segmentInfo), // progress callback
50602        this.handleProgress_.bind(this), this.segmentRequestFinished_.bind(this));
50603      }
50604      /**
50605       * trim the back buffer so that we don't have too much data
50606       * in the source buffer
50607       *
50608       * @private
50609       *
50610       * @param {Object} segmentInfo - the current segment
50611       */
50612
50613    }, {
50614      key: 'trimBackBuffer_',
50615      value: function trimBackBuffer_(segmentInfo) {
50616        var removeToTime = safeBackBufferTrimTime(this.seekable_(), this.currentTime_(), this.playlist_.targetDuration || 10); // Chrome has a hard limit of 150MB of
50617        // buffer and a very conservative "garbage collector"
50618        // We manually clear out the old buffer to ensure
50619        // we don't trigger the QuotaExceeded error
50620        // on the source buffer during subsequent appends
50621
50622        if (removeToTime > 0) {
50623          this.remove(0, removeToTime);
50624        }
50625      }
50626      /**
50627       * created a simplified copy of the segment object with just the
50628       * information necessary to perform the XHR and decryption
50629       *
50630       * @private
50631       *
50632       * @param {Object} segmentInfo - the current segment
50633       * @returns {Object} a simplified segment object copy
50634       */
50635
50636    }, {
50637      key: 'createSimplifiedSegmentObj_',
50638      value: function createSimplifiedSegmentObj_(segmentInfo) {
50639        var segment = segmentInfo.segment;
50640        var simpleSegment = {
50641          resolvedUri: segment.resolvedUri,
50642          byterange: segment.byterange,
50643          requestId: segmentInfo.requestId
50644        };
50645
50646        if (segment.key) {
50647          // if the media sequence is greater than 2^32, the IV will be incorrect
50648          // assuming 10s segments, that would be about 1300 years
50649          var iv = segment.key.iv || new Uint32Array([0, 0, 0, segmentInfo.mediaIndex + segmentInfo.playlist.mediaSequence]);
50650          simpleSegment.key = this.segmentKey(segment.key);
50651          simpleSegment.key.iv = iv;
50652        }
50653
50654        if (segment.map) {
50655          simpleSegment.map = this.initSegment(segment.map);
50656        }
50657
50658        return simpleSegment;
50659      }
50660      /**
50661       * Handle the callback from the segmentRequest function and set the
50662       * associated SegmentLoader state and errors if necessary
50663       *
50664       * @private
50665       */
50666
50667    }, {
50668      key: 'segmentRequestFinished_',
50669      value: function segmentRequestFinished_(error, simpleSegment) {
50670        // every request counts as a media request even if it has been aborted
50671        // or canceled due to a timeout
50672        this.mediaRequests += 1;
50673
50674        if (simpleSegment.stats) {
50675          this.mediaBytesTransferred += simpleSegment.stats.bytesReceived;
50676          this.mediaTransferDuration += simpleSegment.stats.roundTripTime;
50677        } // The request was aborted and the SegmentLoader has already been reset
50678
50679
50680        if (!this.pendingSegment_) {
50681          this.mediaRequestsAborted += 1;
50682          return;
50683        } // the request was aborted and the SegmentLoader has already started
50684        // another request. this can happen when the timeout for an aborted
50685        // request triggers due to a limitation in the XHR library
50686        // do not count this as any sort of request or we risk double-counting
50687
50688
50689        if (simpleSegment.requestId !== this.pendingSegment_.requestId) {
50690          return;
50691        } // an error occurred from the active pendingSegment_ so reset everything
50692
50693
50694        if (error) {
50695          this.pendingSegment_ = null;
50696          this.state = 'READY'; // the requests were aborted just record the aborted stat and exit
50697          // this is not a true error condition and nothing corrective needs
50698          // to be done
50699
50700          if (error.code === REQUEST_ERRORS.ABORTED) {
50701            this.mediaRequestsAborted += 1;
50702            return;
50703          }
50704
50705          this.pause(); // the error is really just that at least one of the requests timed-out
50706          // set the bandwidth to a very low value and trigger an ABR switch to
50707          // take emergency action
50708
50709          if (error.code === REQUEST_ERRORS.TIMEOUT) {
50710            this.mediaRequestsTimedout += 1;
50711            this.bandwidth = 1;
50712            this.roundTrip = NaN;
50713            this.trigger('bandwidthupdate');
50714            return;
50715          } // if control-flow has arrived here, then the error is real
50716          // emit an error event to blacklist the current playlist
50717
50718
50719          this.mediaRequestsErrored += 1;
50720          this.error(error);
50721          this.trigger('error');
50722          return;
50723        } // the response was a success so set any bandwidth stats the request
50724        // generated for ABR purposes
50725
50726
50727        this.bandwidth = simpleSegment.stats.bandwidth;
50728        this.roundTrip = simpleSegment.stats.roundTripTime; // if this request included an initialization segment, save that data
50729        // to the initSegment cache
50730
50731        if (simpleSegment.map) {
50732          simpleSegment.map = this.initSegment(simpleSegment.map, true);
50733        } // if this request included a segment key, save that data in the cache
50734
50735
50736        if (simpleSegment.key) {
50737          this.segmentKey(simpleSegment.key, true);
50738        }
50739
50740        this.processSegmentResponse_(simpleSegment);
50741      }
50742      /**
50743       * Move any important data from the simplified segment object
50744       * back to the real segment object for future phases
50745       *
50746       * @private
50747       */
50748
50749    }, {
50750      key: 'processSegmentResponse_',
50751      value: function processSegmentResponse_(simpleSegment) {
50752        var segmentInfo = this.pendingSegment_;
50753        segmentInfo.bytes = simpleSegment.bytes;
50754
50755        if (simpleSegment.map) {
50756          segmentInfo.segment.map.bytes = simpleSegment.map.bytes;
50757        }
50758
50759        segmentInfo.endOfAllRequests = simpleSegment.endOfAllRequests; // This has fmp4 captions, add them to text tracks
50760
50761        if (simpleSegment.fmp4Captions) {
50762          createCaptionsTrackIfNotExists(this.inbandTextTracks_, this.hls_.tech_, simpleSegment.captionStreams);
50763          addCaptionData({
50764            inbandTextTracks: this.inbandTextTracks_,
50765            captionArray: simpleSegment.fmp4Captions,
50766            // fmp4s will not have a timestamp offset
50767            timestampOffset: 0
50768          }); // Reset stored captions since we added parsed
50769          // captions to a text track at this point
50770
50771          if (this.captionParser_) {
50772            this.captionParser_.clearParsedCaptions();
50773          }
50774        }
50775
50776        this.handleSegment_();
50777      }
50778      /**
50779       * append a decrypted segement to the SourceBuffer through a SourceUpdater
50780       *
50781       * @private
50782       */
50783
50784    }, {
50785      key: 'handleSegment_',
50786      value: function handleSegment_() {
50787        var _this3 = this;
50788
50789        if (!this.pendingSegment_) {
50790          this.state = 'READY';
50791          return;
50792        }
50793
50794        var segmentInfo = this.pendingSegment_;
50795        var segment = segmentInfo.segment;
50796        var timingInfo = this.syncController_.probeSegmentInfo(segmentInfo); // When we have our first timing info, determine what media types this loader is
50797        // dealing with. Although we're maintaining extra state, it helps to preserve the
50798        // separation of segment loader from the actual source buffers.
50799
50800        if (typeof this.startingMedia_ === 'undefined' && timingInfo && ( // Guard against cases where we're not getting timing info at all until we are
50801        // certain that all streams will provide it.
50802        timingInfo.containsAudio || timingInfo.containsVideo)) {
50803          this.startingMedia_ = {
50804            containsAudio: timingInfo.containsAudio,
50805            containsVideo: timingInfo.containsVideo
50806          };
50807        }
50808
50809        var illegalMediaSwitchError = illegalMediaSwitch(this.loaderType_, this.startingMedia_, timingInfo);
50810
50811        if (illegalMediaSwitchError) {
50812          this.error({
50813            message: illegalMediaSwitchError,
50814            blacklistDuration: Infinity
50815          });
50816          this.trigger('error');
50817          return;
50818        }
50819
50820        if (segmentInfo.isSyncRequest) {
50821          this.trigger('syncinfoupdate');
50822          this.pendingSegment_ = null;
50823          this.state = 'READY';
50824          return;
50825        }
50826
50827        if (segmentInfo.timestampOffset !== null && segmentInfo.timestampOffset !== this.sourceUpdater_.timestampOffset()) {
50828          // Subtract any difference between the PTS and DTS times of the first frame
50829          // from the timeStampOffset (which currently equals the buffered.end) to prevent
50830          // creating any gaps in the buffer
50831          if (timingInfo && timingInfo.segmentTimestampInfo) {
50832            var ptsStartTime = timingInfo.segmentTimestampInfo[0].ptsTime;
50833            var dtsStartTime = timingInfo.segmentTimestampInfo[0].dtsTime;
50834            segmentInfo.timestampOffset -= ptsStartTime - dtsStartTime;
50835          }
50836
50837          this.sourceUpdater_.timestampOffset(segmentInfo.timestampOffset); // fired when a timestamp offset is set in HLS (can also identify discontinuities)
50838
50839          this.trigger('timestampoffset');
50840        }
50841
50842        var timelineMapping = this.syncController_.mappingForTimeline(segmentInfo.timeline);
50843
50844        if (timelineMapping !== null) {
50845          this.trigger({
50846            type: 'segmenttimemapping',
50847            mapping: timelineMapping
50848          });
50849        }
50850
50851        this.state = 'APPENDING'; // if the media initialization segment is changing, append it
50852        // before the content segment
50853
50854        if (segment.map) {
50855          var initId = initSegmentId(segment.map);
50856
50857          if (!this.activeInitSegmentId_ || this.activeInitSegmentId_ !== initId) {
50858            var initSegment = this.initSegment(segment.map);
50859            this.sourceUpdater_.appendBuffer({
50860              bytes: initSegment.bytes
50861            }, function () {
50862              _this3.activeInitSegmentId_ = initId;
50863            });
50864          }
50865        }
50866
50867        segmentInfo.byteLength = segmentInfo.bytes.byteLength;
50868
50869        if (typeof segment.start === 'number' && typeof segment.end === 'number') {
50870          this.mediaSecondsLoaded += segment.end - segment.start;
50871        } else {
50872          this.mediaSecondsLoaded += segment.duration;
50873        }
50874
50875        this.logger_(segmentInfoString(segmentInfo));
50876        this.sourceUpdater_.appendBuffer({
50877          bytes: segmentInfo.bytes,
50878          videoSegmentTimingInfoCallback: this.handleVideoSegmentTimingInfo_.bind(this, segmentInfo.requestId)
50879        }, this.handleUpdateEnd_.bind(this));
50880      }
50881    }, {
50882      key: 'handleVideoSegmentTimingInfo_',
50883      value: function handleVideoSegmentTimingInfo_(requestId, event) {
50884        if (!this.pendingSegment_ || requestId !== this.pendingSegment_.requestId) {
50885          return;
50886        }
50887
50888        var segment = this.pendingSegment_.segment;
50889
50890        if (!segment.videoTimingInfo) {
50891          segment.videoTimingInfo = {};
50892        }
50893
50894        segment.videoTimingInfo.transmuxerPrependedSeconds = event.videoSegmentTimingInfo.prependedContentDuration || 0;
50895        segment.videoTimingInfo.transmuxedPresentationStart = event.videoSegmentTimingInfo.start.presentation;
50896        segment.videoTimingInfo.transmuxedPresentationEnd = event.videoSegmentTimingInfo.end.presentation; // mainly used as a reference for debugging
50897
50898        segment.videoTimingInfo.baseMediaDecodeTime = event.videoSegmentTimingInfo.baseMediaDecodeTime;
50899      }
50900      /**
50901       * callback to run when appendBuffer is finished. detects if we are
50902       * in a good state to do things with the data we got, or if we need
50903       * to wait for more
50904       *
50905       * @private
50906       */
50907
50908    }, {
50909      key: 'handleUpdateEnd_',
50910      value: function handleUpdateEnd_() {
50911        if (!this.pendingSegment_) {
50912          this.state = 'READY';
50913
50914          if (!this.paused()) {
50915            this.monitorBuffer_();
50916          }
50917
50918          return;
50919        }
50920
50921        var segmentInfo = this.pendingSegment_;
50922        var segment = segmentInfo.segment;
50923        var isWalkingForward = this.mediaIndex !== null;
50924        this.pendingSegment_ = null;
50925        this.recordThroughput_(segmentInfo);
50926        this.addSegmentMetadataCue_(segmentInfo);
50927        this.state = 'READY';
50928        this.mediaIndex = segmentInfo.mediaIndex;
50929        this.fetchAtBuffer_ = true;
50930        this.currentTimeline_ = segmentInfo.timeline; // We must update the syncinfo to recalculate the seekable range before
50931        // the following conditional otherwise it may consider this a bad "guess"
50932        // and attempt to resync when the post-update seekable window and live
50933        // point would mean that this was the perfect segment to fetch
50934
50935        this.trigger('syncinfoupdate'); // If we previously appended a segment that ends more than 3 targetDurations before
50936        // the currentTime_ that means that our conservative guess was too conservative.
50937        // In that case, reset the loader state so that we try to use any information gained
50938        // from the previous request to create a new, more accurate, sync-point.
50939
50940        if (segment.end && this.currentTime_() - segment.end > segmentInfo.playlist.targetDuration * 3) {
50941          this.resetEverything();
50942          return;
50943        } // Don't do a rendition switch unless we have enough time to get a sync segment
50944        // and conservatively guess
50945
50946
50947        if (isWalkingForward) {
50948          this.trigger('bandwidthupdate');
50949        }
50950
50951        this.trigger('progress'); // any time an update finishes and the last segment is in the
50952        // buffer, end the stream. this ensures the "ended" event will
50953        // fire if playback reaches that point.
50954
50955        if (this.isEndOfStream_(segmentInfo.mediaIndex + 1, segmentInfo.playlist)) {
50956          this.endOfStream();
50957        }
50958
50959        if (!this.paused()) {
50960          this.monitorBuffer_();
50961        }
50962      }
50963      /**
50964       * Records the current throughput of the decrypt, transmux, and append
50965       * portion of the semgment pipeline. `throughput.rate` is a the cumulative
50966       * moving average of the throughput. `throughput.count` is the number of
50967       * data points in the average.
50968       *
50969       * @private
50970       * @param {Object} segmentInfo the object returned by loadSegment
50971       */
50972
50973    }, {
50974      key: 'recordThroughput_',
50975      value: function recordThroughput_(segmentInfo) {
50976        var rate = this.throughput.rate; // Add one to the time to ensure that we don't accidentally attempt to divide
50977        // by zero in the case where the throughput is ridiculously high
50978
50979        var segmentProcessingTime = Date.now() - segmentInfo.endOfAllRequests + 1; // Multiply by 8000 to convert from bytes/millisecond to bits/second
50980
50981        var segmentProcessingThroughput = Math.floor(segmentInfo.byteLength / segmentProcessingTime * 8 * 1000); // This is just a cumulative moving average calculation:
50982        //   newAvg = oldAvg + (sample - oldAvg) / (sampleCount + 1)
50983
50984        this.throughput.rate += (segmentProcessingThroughput - rate) / ++this.throughput.count;
50985      }
50986      /**
50987       * Adds a cue to the segment-metadata track with some metadata information about the
50988       * segment
50989       *
50990       * @private
50991       * @param {Object} segmentInfo
50992       *        the object returned by loadSegment
50993       * @method addSegmentMetadataCue_
50994       */
50995
50996    }, {
50997      key: 'addSegmentMetadataCue_',
50998      value: function addSegmentMetadataCue_(segmentInfo) {
50999        if (!this.segmentMetadataTrack_) {
51000          return;
51001        }
51002
51003        var segment = segmentInfo.segment;
51004        var start = segment.start;
51005        var end = segment.end; // Do not try adding the cue if the start and end times are invalid.
51006
51007        if (!finite(start) || !finite(end)) {
51008          return;
51009        }
51010
51011        removeCuesFromTrack(start, end, this.segmentMetadataTrack_);
51012        var Cue = window$1.WebKitDataCue || window$1.VTTCue;
51013        var value = {
51014          custom: segment.custom,
51015          dateTimeObject: segment.dateTimeObject,
51016          dateTimeString: segment.dateTimeString,
51017          bandwidth: segmentInfo.playlist.attributes.BANDWIDTH,
51018          resolution: segmentInfo.playlist.attributes.RESOLUTION,
51019          codecs: segmentInfo.playlist.attributes.CODECS,
51020          byteLength: segmentInfo.byteLength,
51021          uri: segmentInfo.uri,
51022          timeline: segmentInfo.timeline,
51023          playlist: segmentInfo.playlist.uri,
51024          start: start,
51025          end: end
51026        };
51027        var data = JSON.stringify(value);
51028        var cue = new Cue(start, end, data); // Attach the metadata to the value property of the cue to keep consistency between
51029        // the differences of WebKitDataCue in safari and VTTCue in other browsers
51030
51031        cue.value = value;
51032        this.segmentMetadataTrack_.addCue(cue);
51033      }
51034    }]);
51035    return SegmentLoader;
51036  }(videojs$1.EventTarget);
51037
51038  var uint8ToUtf8 = function uint8ToUtf8(uintArray) {
51039    return decodeURIComponent(escape(String.fromCharCode.apply(null, uintArray)));
51040  };
51041  /**
51042   * @file vtt-segment-loader.js
51043   */
51044
51045
51046  var VTT_LINE_TERMINATORS = new Uint8Array('\n\n'.split('').map(function (_char2) {
51047    return _char2.charCodeAt(0);
51048  }));
51049  /**
51050   * An object that manages segment loading and appending.
51051   *
51052   * @class VTTSegmentLoader
51053   * @param {Object} options required and optional options
51054   * @extends videojs.EventTarget
51055   */
51056
51057  var VTTSegmentLoader = function (_SegmentLoader) {
51058    inherits$1(VTTSegmentLoader, _SegmentLoader);
51059
51060    function VTTSegmentLoader(settings) {
51061      var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
51062      classCallCheck$1(this, VTTSegmentLoader); // SegmentLoader requires a MediaSource be specified or it will throw an error;
51063      // however, VTTSegmentLoader has no need of a media source, so delete the reference
51064
51065      var _this = possibleConstructorReturn$1(this, (VTTSegmentLoader.__proto__ || Object.getPrototypeOf(VTTSegmentLoader)).call(this, settings, options));
51066
51067      _this.mediaSource_ = null;
51068      _this.subtitlesTrack_ = null;
51069      return _this;
51070    }
51071    /**
51072     * Indicates which time ranges are buffered
51073     *
51074     * @return {TimeRange}
51075     *         TimeRange object representing the current buffered ranges
51076     */
51077
51078
51079    createClass$1(VTTSegmentLoader, [{
51080      key: 'buffered_',
51081      value: function buffered_() {
51082        if (!this.subtitlesTrack_ || !this.subtitlesTrack_.cues.length) {
51083          return videojs$1.createTimeRanges();
51084        }
51085
51086        var cues = this.subtitlesTrack_.cues;
51087        var start = cues[0].startTime;
51088        var end = cues[cues.length - 1].startTime;
51089        return videojs$1.createTimeRanges([[start, end]]);
51090      }
51091      /**
51092       * Gets and sets init segment for the provided map
51093       *
51094       * @param {Object} map
51095       *        The map object representing the init segment to get or set
51096       * @param {Boolean=} set
51097       *        If true, the init segment for the provided map should be saved
51098       * @return {Object}
51099       *         map object for desired init segment
51100       */
51101
51102    }, {
51103      key: 'initSegment',
51104      value: function initSegment(map) {
51105        var set$$1 = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false;
51106
51107        if (!map) {
51108          return null;
51109        }
51110
51111        var id = initSegmentId(map);
51112        var storedMap = this.initSegments_[id];
51113
51114        if (set$$1 && !storedMap && map.bytes) {
51115          // append WebVTT line terminators to the media initialization segment if it exists
51116          // to follow the WebVTT spec (https://w3c.github.io/webvtt/#file-structure) that
51117          // requires two or more WebVTT line terminators between the WebVTT header and the
51118          // rest of the file
51119          var combinedByteLength = VTT_LINE_TERMINATORS.byteLength + map.bytes.byteLength;
51120          var combinedSegment = new Uint8Array(combinedByteLength);
51121          combinedSegment.set(map.bytes);
51122          combinedSegment.set(VTT_LINE_TERMINATORS, map.bytes.byteLength);
51123          this.initSegments_[id] = storedMap = {
51124            resolvedUri: map.resolvedUri,
51125            byterange: map.byterange,
51126            bytes: combinedSegment
51127          };
51128        }
51129
51130        return storedMap || map;
51131      }
51132      /**
51133       * Returns true if all configuration required for loading is present, otherwise false.
51134       *
51135       * @return {Boolean} True if the all configuration is ready for loading
51136       * @private
51137       */
51138
51139    }, {
51140      key: 'couldBeginLoading_',
51141      value: function couldBeginLoading_() {
51142        return this.playlist_ && this.subtitlesTrack_ && !this.paused();
51143      }
51144      /**
51145       * Once all the starting parameters have been specified, begin
51146       * operation. This method should only be invoked from the INIT
51147       * state.
51148       *
51149       * @private
51150       */
51151
51152    }, {
51153      key: 'init_',
51154      value: function init_() {
51155        this.state = 'READY';
51156        this.resetEverything();
51157        return this.monitorBuffer_();
51158      }
51159      /**
51160       * Set a subtitle track on the segment loader to add subtitles to
51161       *
51162       * @param {TextTrack=} track
51163       *        The text track to add loaded subtitles to
51164       * @return {TextTrack}
51165       *        Returns the subtitles track
51166       */
51167
51168    }, {
51169      key: 'track',
51170      value: function track(_track) {
51171        if (typeof _track === 'undefined') {
51172          return this.subtitlesTrack_;
51173        }
51174
51175        this.subtitlesTrack_ = _track; // if we were unpaused but waiting for a sourceUpdater, start
51176        // buffering now
51177
51178        if (this.state === 'INIT' && this.couldBeginLoading_()) {
51179          this.init_();
51180        }
51181
51182        return this.subtitlesTrack_;
51183      }
51184      /**
51185       * Remove any data in the source buffer between start and end times
51186       * @param {Number} start - the start time of the region to remove from the buffer
51187       * @param {Number} end - the end time of the region to remove from the buffer
51188       */
51189
51190    }, {
51191      key: 'remove',
51192      value: function remove(start, end) {
51193        removeCuesFromTrack(start, end, this.subtitlesTrack_);
51194      }
51195      /**
51196       * fill the buffer with segements unless the sourceBuffers are
51197       * currently updating
51198       *
51199       * Note: this function should only ever be called by monitorBuffer_
51200       * and never directly
51201       *
51202       * @private
51203       */
51204
51205    }, {
51206      key: 'fillBuffer_',
51207      value: function fillBuffer_() {
51208        var _this2 = this;
51209
51210        if (!this.syncPoint_) {
51211          this.syncPoint_ = this.syncController_.getSyncPoint(this.playlist_, this.duration_(), this.currentTimeline_, this.currentTime_());
51212        } // see if we need to begin loading immediately
51213
51214
51215        var segmentInfo = this.checkBuffer_(this.buffered_(), this.playlist_, this.mediaIndex, this.hasPlayed_(), this.currentTime_(), this.syncPoint_);
51216        segmentInfo = this.skipEmptySegments_(segmentInfo);
51217
51218        if (!segmentInfo) {
51219          return;
51220        }
51221
51222        if (this.syncController_.timestampOffsetForTimeline(segmentInfo.timeline) === null) {
51223          // We don't have the timestamp offset that we need to sync subtitles.
51224          // Rerun on a timestamp offset or user interaction.
51225          var checkTimestampOffset = function checkTimestampOffset() {
51226            _this2.state = 'READY';
51227
51228            if (!_this2.paused()) {
51229              // if not paused, queue a buffer check as soon as possible
51230              _this2.monitorBuffer_();
51231            }
51232          };
51233
51234          this.syncController_.one('timestampoffset', checkTimestampOffset);
51235          this.state = 'WAITING_ON_TIMELINE';
51236          return;
51237        }
51238
51239        this.loadSegment_(segmentInfo);
51240      }
51241      /**
51242       * Prevents the segment loader from requesting segments we know contain no subtitles
51243       * by walking forward until we find the next segment that we don't know whether it is
51244       * empty or not.
51245       *
51246       * @param {Object} segmentInfo
51247       *        a segment info object that describes the current segment
51248       * @return {Object}
51249       *         a segment info object that describes the current segment
51250       */
51251
51252    }, {
51253      key: 'skipEmptySegments_',
51254      value: function skipEmptySegments_(segmentInfo) {
51255        while (segmentInfo && segmentInfo.segment.empty) {
51256          segmentInfo = this.generateSegmentInfo_(segmentInfo.playlist, segmentInfo.mediaIndex + 1, segmentInfo.startOfSegment + segmentInfo.duration, segmentInfo.isSyncRequest);
51257        }
51258
51259        return segmentInfo;
51260      }
51261      /**
51262       * append a decrypted segement to the SourceBuffer through a SourceUpdater
51263       *
51264       * @private
51265       */
51266
51267    }, {
51268      key: 'handleSegment_',
51269      value: function handleSegment_() {
51270        var _this3 = this;
51271
51272        if (!this.pendingSegment_ || !this.subtitlesTrack_) {
51273          this.state = 'READY';
51274          return;
51275        }
51276
51277        this.state = 'APPENDING';
51278        var segmentInfo = this.pendingSegment_;
51279        var segment = segmentInfo.segment; // Make sure that vttjs has loaded, otherwise, wait till it finished loading
51280
51281        if (typeof window$1.WebVTT !== 'function' && this.subtitlesTrack_ && this.subtitlesTrack_.tech_) {
51282          var loadHandler = void 0;
51283
51284          var errorHandler = function errorHandler() {
51285            _this3.subtitlesTrack_.tech_.off('vttjsloaded', loadHandler);
51286
51287            _this3.error({
51288              message: 'Error loading vtt.js'
51289            });
51290
51291            _this3.state = 'READY';
51292
51293            _this3.pause();
51294
51295            _this3.trigger('error');
51296          };
51297
51298          loadHandler = function loadHandler() {
51299            _this3.subtitlesTrack_.tech_.off('vttjserror', errorHandler);
51300
51301            _this3.handleSegment_();
51302          };
51303
51304          this.state = 'WAITING_ON_VTTJS';
51305          this.subtitlesTrack_.tech_.one('vttjsloaded', loadHandler);
51306          this.subtitlesTrack_.tech_.one('vttjserror', errorHandler);
51307          return;
51308        }
51309
51310        segment.requested = true;
51311
51312        try {
51313          this.parseVTTCues_(segmentInfo);
51314        } catch (e) {
51315          this.error({
51316            message: e.message
51317          });
51318          this.state = 'READY';
51319          this.pause();
51320          return this.trigger('error');
51321        }
51322
51323        this.updateTimeMapping_(segmentInfo, this.syncController_.timelines[segmentInfo.timeline], this.playlist_);
51324
51325        if (segmentInfo.isSyncRequest) {
51326          this.trigger('syncinfoupdate');
51327          this.pendingSegment_ = null;
51328          this.state = 'READY';
51329          return;
51330        }
51331
51332        segmentInfo.byteLength = segmentInfo.bytes.byteLength;
51333        this.mediaSecondsLoaded += segment.duration;
51334
51335        if (segmentInfo.cues.length) {
51336          // remove any overlapping cues to prevent doubling
51337          this.remove(segmentInfo.cues[0].endTime, segmentInfo.cues[segmentInfo.cues.length - 1].endTime);
51338        }
51339
51340        segmentInfo.cues.forEach(function (cue) {
51341          _this3.subtitlesTrack_.addCue(cue);
51342        });
51343        this.handleUpdateEnd_();
51344      }
51345      /**
51346       * Uses the WebVTT parser to parse the segment response
51347       *
51348       * @param {Object} segmentInfo
51349       *        a segment info object that describes the current segment
51350       * @private
51351       */
51352
51353    }, {
51354      key: 'parseVTTCues_',
51355      value: function parseVTTCues_(segmentInfo) {
51356        var decoder = void 0;
51357        var decodeBytesToString = false;
51358
51359        if (typeof window$1.TextDecoder === 'function') {
51360          decoder = new window$1.TextDecoder('utf8');
51361        } else {
51362          decoder = window$1.WebVTT.StringDecoder();
51363          decodeBytesToString = true;
51364        }
51365
51366        var parser = new window$1.WebVTT.Parser(window$1, window$1.vttjs, decoder);
51367        segmentInfo.cues = [];
51368        segmentInfo.timestampmap = {
51369          MPEGTS: 0,
51370          LOCAL: 0
51371        };
51372        parser.oncue = segmentInfo.cues.push.bind(segmentInfo.cues);
51373
51374        parser.ontimestampmap = function (map) {
51375          return segmentInfo.timestampmap = map;
51376        };
51377
51378        parser.onparsingerror = function (error) {
51379          videojs$1.log.warn('Error encountered when parsing cues: ' + error.message);
51380        };
51381
51382        if (segmentInfo.segment.map) {
51383          var mapData = segmentInfo.segment.map.bytes;
51384
51385          if (decodeBytesToString) {
51386            mapData = uint8ToUtf8(mapData);
51387          }
51388
51389          parser.parse(mapData);
51390        }
51391
51392        var segmentData = segmentInfo.bytes;
51393
51394        if (decodeBytesToString) {
51395          segmentData = uint8ToUtf8(segmentData);
51396        }
51397
51398        parser.parse(segmentData);
51399        parser.flush();
51400      }
51401      /**
51402       * Updates the start and end times of any cues parsed by the WebVTT parser using
51403       * the information parsed from the X-TIMESTAMP-MAP header and a TS to media time mapping
51404       * from the SyncController
51405       *
51406       * @param {Object} segmentInfo
51407       *        a segment info object that describes the current segment
51408       * @param {Object} mappingObj
51409       *        object containing a mapping from TS to media time
51410       * @param {Object} playlist
51411       *        the playlist object containing the segment
51412       * @private
51413       */
51414
51415    }, {
51416      key: 'updateTimeMapping_',
51417      value: function updateTimeMapping_(segmentInfo, mappingObj, playlist) {
51418        var segment = segmentInfo.segment;
51419
51420        if (!mappingObj) {
51421          // If the sync controller does not have a mapping of TS to Media Time for the
51422          // timeline, then we don't have enough information to update the cue
51423          // start/end times
51424          return;
51425        }
51426
51427        if (!segmentInfo.cues.length) {
51428          // If there are no cues, we also do not have enough information to figure out
51429          // segment timing. Mark that the segment contains no cues so we don't re-request
51430          // an empty segment.
51431          segment.empty = true;
51432          return;
51433        }
51434
51435        var timestampmap = segmentInfo.timestampmap;
51436        var diff = timestampmap.MPEGTS / 90000 - timestampmap.LOCAL + mappingObj.mapping;
51437        segmentInfo.cues.forEach(function (cue) {
51438          // First convert cue time to TS time using the timestamp-map provided within the vtt
51439          cue.startTime += diff;
51440          cue.endTime += diff;
51441        });
51442
51443        if (!playlist.syncInfo) {
51444          var firstStart = segmentInfo.cues[0].startTime;
51445          var lastStart = segmentInfo.cues[segmentInfo.cues.length - 1].startTime;
51446          playlist.syncInfo = {
51447            mediaSequence: playlist.mediaSequence + segmentInfo.mediaIndex,
51448            time: Math.min(firstStart, lastStart - segment.duration)
51449          };
51450        }
51451      }
51452    }]);
51453    return VTTSegmentLoader;
51454  }(SegmentLoader);
51455  /**
51456   * @file ad-cue-tags.js
51457   */
51458
51459  /**
51460   * Searches for an ad cue that overlaps with the given mediaTime
51461   */
51462
51463
51464  var findAdCue = function findAdCue(track, mediaTime) {
51465    var cues = track.cues;
51466
51467    for (var i = 0; i < cues.length; i++) {
51468      var cue = cues[i];
51469
51470      if (mediaTime >= cue.adStartTime && mediaTime <= cue.adEndTime) {
51471        return cue;
51472      }
51473    }
51474
51475    return null;
51476  };
51477
51478  var updateAdCues = function updateAdCues(media, track) {
51479    var offset = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 0;
51480
51481    if (!media.segments) {
51482      return;
51483    }
51484
51485    var mediaTime = offset;
51486    var cue = void 0;
51487
51488    for (var i = 0; i < media.segments.length; i++) {
51489      var segment = media.segments[i];
51490
51491      if (!cue) {
51492        // Since the cues will span for at least the segment duration, adding a fudge
51493        // factor of half segment duration will prevent duplicate cues from being
51494        // created when timing info is not exact (e.g. cue start time initialized
51495        // at 10.006677, but next call mediaTime is 10.003332 )
51496        cue = findAdCue(track, mediaTime + segment.duration / 2);
51497      }
51498
51499      if (cue) {
51500        if ('cueIn' in segment) {
51501          // Found a CUE-IN so end the cue
51502          cue.endTime = mediaTime;
51503          cue.adEndTime = mediaTime;
51504          mediaTime += segment.duration;
51505          cue = null;
51506          continue;
51507        }
51508
51509        if (mediaTime < cue.endTime) {
51510          // Already processed this mediaTime for this cue
51511          mediaTime += segment.duration;
51512          continue;
51513        } // otherwise extend cue until a CUE-IN is found
51514
51515
51516        cue.endTime += segment.duration;
51517      } else {
51518        if ('cueOut' in segment) {
51519          cue = new window$1.VTTCue(mediaTime, mediaTime + segment.duration, segment.cueOut);
51520          cue.adStartTime = mediaTime; // Assumes tag format to be
51521          // #EXT-X-CUE-OUT:30
51522
51523          cue.adEndTime = mediaTime + parseFloat(segment.cueOut);
51524          track.addCue(cue);
51525        }
51526
51527        if ('cueOutCont' in segment) {
51528          // Entered into the middle of an ad cue
51529          var adOffset = void 0;
51530          var adTotal = void 0; // Assumes tag formate to be
51531          // #EXT-X-CUE-OUT-CONT:10/30
51532
51533          var _segment$cueOutCont$s = segment.cueOutCont.split('/').map(parseFloat);
51534
51535          var _segment$cueOutCont$s2 = slicedToArray(_segment$cueOutCont$s, 2);
51536
51537          adOffset = _segment$cueOutCont$s2[0];
51538          adTotal = _segment$cueOutCont$s2[1];
51539          cue = new window$1.VTTCue(mediaTime, mediaTime + segment.duration, '');
51540          cue.adStartTime = mediaTime - adOffset;
51541          cue.adEndTime = cue.adStartTime + adTotal;
51542          track.addCue(cue);
51543        }
51544      }
51545
51546      mediaTime += segment.duration;
51547    }
51548  };
51549  /**
51550   * @file sync-controller.js
51551   */
51552
51553
51554  var tsprobe = tsInspector.inspect;
51555  var syncPointStrategies = [// Stategy "VOD": Handle the VOD-case where the sync-point is *always*
51556  //                the equivalence display-time 0 === segment-index 0
51557  {
51558    name: 'VOD',
51559    run: function run(syncController, playlist, duration$$1, currentTimeline, currentTime) {
51560      if (duration$$1 !== Infinity) {
51561        var syncPoint = {
51562          time: 0,
51563          segmentIndex: 0
51564        };
51565        return syncPoint;
51566      }
51567
51568      return null;
51569    }
51570  }, // Stategy "ProgramDateTime": We have a program-date-time tag in this playlist
51571  {
51572    name: 'ProgramDateTime',
51573    run: function run(syncController, playlist, duration$$1, currentTimeline, currentTime) {
51574      if (!syncController.datetimeToDisplayTime) {
51575        return null;
51576      }
51577
51578      var segments = playlist.segments || [];
51579      var syncPoint = null;
51580      var lastDistance = null;
51581      currentTime = currentTime || 0;
51582
51583      for (var i = 0; i < segments.length; i++) {
51584        var segment = segments[i];
51585
51586        if (segment.dateTimeObject) {
51587          var segmentTime = segment.dateTimeObject.getTime() / 1000;
51588          var segmentStart = segmentTime + syncController.datetimeToDisplayTime;
51589          var distance = Math.abs(currentTime - segmentStart); // Once the distance begins to increase, or if distance is 0, we have passed
51590          // currentTime and can stop looking for better candidates
51591
51592          if (lastDistance !== null && (distance === 0 || lastDistance < distance)) {
51593            break;
51594          }
51595
51596          lastDistance = distance;
51597          syncPoint = {
51598            time: segmentStart,
51599            segmentIndex: i
51600          };
51601        }
51602      }
51603
51604      return syncPoint;
51605    }
51606  }, // Stategy "Segment": We have a known time mapping for a timeline and a
51607  //                    segment in the current timeline with timing data
51608  {
51609    name: 'Segment',
51610    run: function run(syncController, playlist, duration$$1, currentTimeline, currentTime) {
51611      var segments = playlist.segments || [];
51612      var syncPoint = null;
51613      var lastDistance = null;
51614      currentTime = currentTime || 0;
51615
51616      for (var i = 0; i < segments.length; i++) {
51617        var segment = segments[i];
51618
51619        if (segment.timeline === currentTimeline && typeof segment.start !== 'undefined') {
51620          var distance = Math.abs(currentTime - segment.start); // Once the distance begins to increase, we have passed
51621          // currentTime and can stop looking for better candidates
51622
51623          if (lastDistance !== null && lastDistance < distance) {
51624            break;
51625          }
51626
51627          if (!syncPoint || lastDistance === null || lastDistance >= distance) {
51628            lastDistance = distance;
51629            syncPoint = {
51630              time: segment.start,
51631              segmentIndex: i
51632            };
51633          }
51634        }
51635      }
51636
51637      return syncPoint;
51638    }
51639  }, // Stategy "Discontinuity": We have a discontinuity with a known
51640  //                          display-time
51641  {
51642    name: 'Discontinuity',
51643    run: function run(syncController, playlist, duration$$1, currentTimeline, currentTime) {
51644      var syncPoint = null;
51645      currentTime = currentTime || 0;
51646
51647      if (playlist.discontinuityStarts && playlist.discontinuityStarts.length) {
51648        var lastDistance = null;
51649
51650        for (var i = 0; i < playlist.discontinuityStarts.length; i++) {
51651          var segmentIndex = playlist.discontinuityStarts[i];
51652          var discontinuity = playlist.discontinuitySequence + i + 1;
51653          var discontinuitySync = syncController.discontinuities[discontinuity];
51654
51655          if (discontinuitySync) {
51656            var distance = Math.abs(currentTime - discontinuitySync.time); // Once the distance begins to increase, we have passed
51657            // currentTime and can stop looking for better candidates
51658
51659            if (lastDistance !== null && lastDistance < distance) {
51660              break;
51661            }
51662
51663            if (!syncPoint || lastDistance === null || lastDistance >= distance) {
51664              lastDistance = distance;
51665              syncPoint = {
51666                time: discontinuitySync.time,
51667                segmentIndex: segmentIndex
51668              };
51669            }
51670          }
51671        }
51672      }
51673
51674      return syncPoint;
51675    }
51676  }, // Stategy "Playlist": We have a playlist with a known mapping of
51677  //                     segment index to display time
51678  {
51679    name: 'Playlist',
51680    run: function run(syncController, playlist, duration$$1, currentTimeline, currentTime) {
51681      if (playlist.syncInfo) {
51682        var syncPoint = {
51683          time: playlist.syncInfo.time,
51684          segmentIndex: playlist.syncInfo.mediaSequence - playlist.mediaSequence
51685        };
51686        return syncPoint;
51687      }
51688
51689      return null;
51690    }
51691  }];
51692
51693  var SyncController = function (_videojs$EventTarget) {
51694    inherits$1(SyncController, _videojs$EventTarget);
51695
51696    function SyncController() {
51697      classCallCheck$1(this, SyncController); // Segment Loader state variables...
51698      // ...for synching across variants
51699
51700      var _this = possibleConstructorReturn$1(this, (SyncController.__proto__ || Object.getPrototypeOf(SyncController)).call(this));
51701
51702      _this.inspectCache_ = undefined; // ...for synching across variants
51703
51704      _this.timelines = [];
51705      _this.discontinuities = [];
51706      _this.datetimeToDisplayTime = null;
51707      _this.logger_ = logger('SyncController');
51708      return _this;
51709    }
51710    /**
51711     * Find a sync-point for the playlist specified
51712     *
51713     * A sync-point is defined as a known mapping from display-time to
51714     * a segment-index in the current playlist.
51715     *
51716     * @param {Playlist} playlist
51717     *        The playlist that needs a sync-point
51718     * @param {Number} duration
51719     *        Duration of the MediaSource (Infinite if playing a live source)
51720     * @param {Number} currentTimeline
51721     *        The last timeline from which a segment was loaded
51722     * @returns {Object}
51723     *          A sync-point object
51724     */
51725
51726
51727    createClass$1(SyncController, [{
51728      key: 'getSyncPoint',
51729      value: function getSyncPoint(playlist, duration$$1, currentTimeline, currentTime) {
51730        var syncPoints = this.runStrategies_(playlist, duration$$1, currentTimeline, currentTime);
51731
51732        if (!syncPoints.length) {
51733          // Signal that we need to attempt to get a sync-point manually
51734          // by fetching a segment in the playlist and constructing
51735          // a sync-point from that information
51736          return null;
51737        } // Now find the sync-point that is closest to the currentTime because
51738        // that should result in the most accurate guess about which segment
51739        // to fetch
51740
51741
51742        return this.selectSyncPoint_(syncPoints, {
51743          key: 'time',
51744          value: currentTime
51745        });
51746      }
51747      /**
51748       * Calculate the amount of time that has expired off the playlist during playback
51749       *
51750       * @param {Playlist} playlist
51751       *        Playlist object to calculate expired from
51752       * @param {Number} duration
51753       *        Duration of the MediaSource (Infinity if playling a live source)
51754       * @returns {Number|null}
51755       *          The amount of time that has expired off the playlist during playback. Null
51756       *          if no sync-points for the playlist can be found.
51757       */
51758
51759    }, {
51760      key: 'getExpiredTime',
51761      value: function getExpiredTime(playlist, duration$$1) {
51762        if (!playlist || !playlist.segments) {
51763          return null;
51764        }
51765
51766        var syncPoints = this.runStrategies_(playlist, duration$$1, playlist.discontinuitySequence, 0); // Without sync-points, there is not enough information to determine the expired time
51767
51768        if (!syncPoints.length) {
51769          return null;
51770        }
51771
51772        var syncPoint = this.selectSyncPoint_(syncPoints, {
51773          key: 'segmentIndex',
51774          value: 0
51775        }); // If the sync-point is beyond the start of the playlist, we want to subtract the
51776        // duration from index 0 to syncPoint.segmentIndex instead of adding.
51777
51778        if (syncPoint.segmentIndex > 0) {
51779          syncPoint.time *= -1;
51780        }
51781
51782        return Math.abs(syncPoint.time + sumDurations(playlist, syncPoint.segmentIndex, 0));
51783      }
51784      /**
51785       * Runs each sync-point strategy and returns a list of sync-points returned by the
51786       * strategies
51787       *
51788       * @private
51789       * @param {Playlist} playlist
51790       *        The playlist that needs a sync-point
51791       * @param {Number} duration
51792       *        Duration of the MediaSource (Infinity if playing a live source)
51793       * @param {Number} currentTimeline
51794       *        The last timeline from which a segment was loaded
51795       * @returns {Array}
51796       *          A list of sync-point objects
51797       */
51798
51799    }, {
51800      key: 'runStrategies_',
51801      value: function runStrategies_(playlist, duration$$1, currentTimeline, currentTime) {
51802        var syncPoints = []; // Try to find a sync-point in by utilizing various strategies...
51803
51804        for (var i = 0; i < syncPointStrategies.length; i++) {
51805          var strategy = syncPointStrategies[i];
51806          var syncPoint = strategy.run(this, playlist, duration$$1, currentTimeline, currentTime);
51807
51808          if (syncPoint) {
51809            syncPoint.strategy = strategy.name;
51810            syncPoints.push({
51811              strategy: strategy.name,
51812              syncPoint: syncPoint
51813            });
51814          }
51815        }
51816
51817        return syncPoints;
51818      }
51819      /**
51820       * Selects the sync-point nearest the specified target
51821       *
51822       * @private
51823       * @param {Array} syncPoints
51824       *        List of sync-points to select from
51825       * @param {Object} target
51826       *        Object specifying the property and value we are targeting
51827       * @param {String} target.key
51828       *        Specifies the property to target. Must be either 'time' or 'segmentIndex'
51829       * @param {Number} target.value
51830       *        The value to target for the specified key.
51831       * @returns {Object}
51832       *          The sync-point nearest the target
51833       */
51834
51835    }, {
51836      key: 'selectSyncPoint_',
51837      value: function selectSyncPoint_(syncPoints, target) {
51838        var bestSyncPoint = syncPoints[0].syncPoint;
51839        var bestDistance = Math.abs(syncPoints[0].syncPoint[target.key] - target.value);
51840        var bestStrategy = syncPoints[0].strategy;
51841
51842        for (var i = 1; i < syncPoints.length; i++) {
51843          var newDistance = Math.abs(syncPoints[i].syncPoint[target.key] - target.value);
51844
51845          if (newDistance < bestDistance) {
51846            bestDistance = newDistance;
51847            bestSyncPoint = syncPoints[i].syncPoint;
51848            bestStrategy = syncPoints[i].strategy;
51849          }
51850        }
51851
51852        this.logger_('syncPoint for [' + target.key + ': ' + target.value + '] chosen with strategy' + (' [' + bestStrategy + ']: [time:' + bestSyncPoint.time + ',') + (' segmentIndex:' + bestSyncPoint.segmentIndex + ']'));
51853        return bestSyncPoint;
51854      }
51855      /**
51856       * Save any meta-data present on the segments when segments leave
51857       * the live window to the playlist to allow for synchronization at the
51858       * playlist level later.
51859       *
51860       * @param {Playlist} oldPlaylist - The previous active playlist
51861       * @param {Playlist} newPlaylist - The updated and most current playlist
51862       */
51863
51864    }, {
51865      key: 'saveExpiredSegmentInfo',
51866      value: function saveExpiredSegmentInfo(oldPlaylist, newPlaylist) {
51867        var mediaSequenceDiff = newPlaylist.mediaSequence - oldPlaylist.mediaSequence; // When a segment expires from the playlist and it has a start time
51868        // save that information as a possible sync-point reference in future
51869
51870        for (var i = mediaSequenceDiff - 1; i >= 0; i--) {
51871          var lastRemovedSegment = oldPlaylist.segments[i];
51872
51873          if (lastRemovedSegment && typeof lastRemovedSegment.start !== 'undefined') {
51874            newPlaylist.syncInfo = {
51875              mediaSequence: oldPlaylist.mediaSequence + i,
51876              time: lastRemovedSegment.start
51877            };
51878            this.logger_('playlist refresh sync: [time:' + newPlaylist.syncInfo.time + ',' + (' mediaSequence: ' + newPlaylist.syncInfo.mediaSequence + ']'));
51879            this.trigger('syncinfoupdate');
51880            break;
51881          }
51882        }
51883      }
51884      /**
51885       * Save the mapping from playlist's ProgramDateTime to display. This should
51886       * only ever happen once at the start of playback.
51887       *
51888       * @param {Playlist} playlist - The currently active playlist
51889       */
51890
51891    }, {
51892      key: 'setDateTimeMapping',
51893      value: function setDateTimeMapping(playlist) {
51894        if (!this.datetimeToDisplayTime && playlist.segments && playlist.segments.length && playlist.segments[0].dateTimeObject) {
51895          var playlistTimestamp = playlist.segments[0].dateTimeObject.getTime() / 1000;
51896          this.datetimeToDisplayTime = -playlistTimestamp;
51897        }
51898      }
51899      /**
51900       * Reset the state of the inspection cache when we do a rendition
51901       * switch
51902       */
51903
51904    }, {
51905      key: 'reset',
51906      value: function reset() {
51907        this.inspectCache_ = undefined;
51908      }
51909      /**
51910       * Probe or inspect a fmp4 or an mpeg2-ts segment to determine the start
51911       * and end of the segment in it's internal "media time". Used to generate
51912       * mappings from that internal "media time" to the display time that is
51913       * shown on the player.
51914       *
51915       * @param {SegmentInfo} segmentInfo - The current active request information
51916       */
51917
51918    }, {
51919      key: 'probeSegmentInfo',
51920      value: function probeSegmentInfo(segmentInfo) {
51921        var segment = segmentInfo.segment;
51922        var playlist = segmentInfo.playlist;
51923        var timingInfo = void 0;
51924
51925        if (segment.map) {
51926          timingInfo = this.probeMp4Segment_(segmentInfo);
51927        } else {
51928          timingInfo = this.probeTsSegment_(segmentInfo);
51929        }
51930
51931        if (timingInfo) {
51932          if (this.calculateSegmentTimeMapping_(segmentInfo, timingInfo)) {
51933            this.saveDiscontinuitySyncInfo_(segmentInfo); // If the playlist does not have sync information yet, record that information
51934            // now with segment timing information
51935
51936            if (!playlist.syncInfo) {
51937              playlist.syncInfo = {
51938                mediaSequence: playlist.mediaSequence + segmentInfo.mediaIndex,
51939                time: segment.start
51940              };
51941            }
51942          }
51943        }
51944
51945        return timingInfo;
51946      }
51947      /**
51948       * Probe an fmp4 or an mpeg2-ts segment to determine the start of the segment
51949       * in it's internal "media time".
51950       *
51951       * @private
51952       * @param {SegmentInfo} segmentInfo - The current active request information
51953       * @return {object} The start and end time of the current segment in "media time"
51954       */
51955
51956    }, {
51957      key: 'probeMp4Segment_',
51958      value: function probeMp4Segment_(segmentInfo) {
51959        var segment = segmentInfo.segment;
51960        var timescales = probe.timescale(segment.map.bytes);
51961        var startTime = probe.startTime(timescales, segmentInfo.bytes);
51962
51963        if (segmentInfo.timestampOffset !== null) {
51964          segmentInfo.timestampOffset -= startTime;
51965        }
51966
51967        return {
51968          start: startTime,
51969          end: startTime + segment.duration
51970        };
51971      }
51972      /**
51973       * Probe an mpeg2-ts segment to determine the start and end of the segment
51974       * in it's internal "media time".
51975       *
51976       * @private
51977       * @param {SegmentInfo} segmentInfo - The current active request information
51978       * @return {object} The start and end time of the current segment in "media time"
51979       */
51980
51981    }, {
51982      key: 'probeTsSegment_',
51983      value: function probeTsSegment_(segmentInfo) {
51984        var timeInfo = tsprobe(segmentInfo.bytes, this.inspectCache_);
51985        var segmentStartTime = void 0;
51986        var segmentEndTime = void 0;
51987        var segmentTimestampInfo = void 0;
51988
51989        if (!timeInfo) {
51990          return null;
51991        }
51992
51993        if (timeInfo.video && timeInfo.video.length === 2) {
51994          this.inspectCache_ = timeInfo.video[1].dts;
51995          segmentStartTime = timeInfo.video[0].dtsTime;
51996          segmentEndTime = timeInfo.video[1].dtsTime;
51997          segmentTimestampInfo = timeInfo.video;
51998        } else if (timeInfo.audio && timeInfo.audio.length === 2) {
51999          this.inspectCache_ = timeInfo.audio[1].dts;
52000          segmentStartTime = timeInfo.audio[0].dtsTime;
52001          segmentEndTime = timeInfo.audio[1].dtsTime;
52002          segmentTimestampInfo = timeInfo.audio;
52003        }
52004
52005        var probedInfo = {
52006          segmentTimestampInfo: segmentTimestampInfo,
52007          start: segmentStartTime,
52008          end: segmentEndTime,
52009          containsVideo: timeInfo.video && timeInfo.video.length === 2,
52010          containsAudio: timeInfo.audio && timeInfo.audio.length === 2
52011        };
52012        return probedInfo;
52013      }
52014    }, {
52015      key: 'timestampOffsetForTimeline',
52016      value: function timestampOffsetForTimeline(timeline) {
52017        if (typeof this.timelines[timeline] === 'undefined') {
52018          return null;
52019        }
52020
52021        return this.timelines[timeline].time;
52022      }
52023    }, {
52024      key: 'mappingForTimeline',
52025      value: function mappingForTimeline(timeline) {
52026        if (typeof this.timelines[timeline] === 'undefined') {
52027          return null;
52028        }
52029
52030        return this.timelines[timeline].mapping;
52031      }
52032      /**
52033       * Use the "media time" for a segment to generate a mapping to "display time" and
52034       * save that display time to the segment.
52035       *
52036       * @private
52037       * @param {SegmentInfo} segmentInfo
52038       *        The current active request information
52039       * @param {object} timingInfo
52040       *        The start and end time of the current segment in "media time"
52041       * @returns {Boolean}
52042       *          Returns false if segment time mapping could not be calculated
52043       */
52044
52045    }, {
52046      key: 'calculateSegmentTimeMapping_',
52047      value: function calculateSegmentTimeMapping_(segmentInfo, timingInfo) {
52048        var segment = segmentInfo.segment;
52049        var mappingObj = this.timelines[segmentInfo.timeline];
52050
52051        if (segmentInfo.timestampOffset !== null) {
52052          mappingObj = {
52053            time: segmentInfo.startOfSegment,
52054            mapping: segmentInfo.startOfSegment - timingInfo.start
52055          };
52056          this.timelines[segmentInfo.timeline] = mappingObj;
52057          this.trigger('timestampoffset');
52058          this.logger_('time mapping for timeline ' + segmentInfo.timeline + ': ' + ('[time: ' + mappingObj.time + '] [mapping: ' + mappingObj.mapping + ']'));
52059          segment.start = segmentInfo.startOfSegment;
52060          segment.end = timingInfo.end + mappingObj.mapping;
52061        } else if (mappingObj) {
52062          segment.start = timingInfo.start + mappingObj.mapping;
52063          segment.end = timingInfo.end + mappingObj.mapping;
52064        } else {
52065          return false;
52066        }
52067
52068        return true;
52069      }
52070      /**
52071       * Each time we have discontinuity in the playlist, attempt to calculate the location
52072       * in display of the start of the discontinuity and save that. We also save an accuracy
52073       * value so that we save values with the most accuracy (closest to 0.)
52074       *
52075       * @private
52076       * @param {SegmentInfo} segmentInfo - The current active request information
52077       */
52078
52079    }, {
52080      key: 'saveDiscontinuitySyncInfo_',
52081      value: function saveDiscontinuitySyncInfo_(segmentInfo) {
52082        var playlist = segmentInfo.playlist;
52083        var segment = segmentInfo.segment; // If the current segment is a discontinuity then we know exactly where
52084        // the start of the range and it's accuracy is 0 (greater accuracy values
52085        // mean more approximation)
52086
52087        if (segment.discontinuity) {
52088          this.discontinuities[segment.timeline] = {
52089            time: segment.start,
52090            accuracy: 0
52091          };
52092        } else if (playlist.discontinuityStarts && playlist.discontinuityStarts.length) {
52093          // Search for future discontinuities that we can provide better timing
52094          // information for and save that information for sync purposes
52095          for (var i = 0; i < playlist.discontinuityStarts.length; i++) {
52096            var segmentIndex = playlist.discontinuityStarts[i];
52097            var discontinuity = playlist.discontinuitySequence + i + 1;
52098            var mediaIndexDiff = segmentIndex - segmentInfo.mediaIndex;
52099            var accuracy = Math.abs(mediaIndexDiff);
52100
52101            if (!this.discontinuities[discontinuity] || this.discontinuities[discontinuity].accuracy > accuracy) {
52102              var time = void 0;
52103
52104              if (mediaIndexDiff < 0) {
52105                time = segment.start - sumDurations(playlist, segmentInfo.mediaIndex, segmentIndex);
52106              } else {
52107                time = segment.end + sumDurations(playlist, segmentInfo.mediaIndex + 1, segmentIndex);
52108              }
52109
52110              this.discontinuities[discontinuity] = {
52111                time: time,
52112                accuracy: accuracy
52113              };
52114            }
52115          }
52116        }
52117      }
52118    }]);
52119    return SyncController;
52120  }(videojs$1.EventTarget);
52121
52122  var Decrypter$1 = new shimWorker("./decrypter-worker.worker.js", function (window, document$$1) {
52123    var self = this;
52124
52125    var decrypterWorker = function () {
52126      /*
52127       * pkcs7.pad
52128       * https://github.com/brightcove/pkcs7
52129       *
52130       * Copyright (c) 2014 Brightcove
52131       * Licensed under the apache2 license.
52132       */
52133
52134      /**
52135       * Returns the subarray of a Uint8Array without PKCS#7 padding.
52136       * @param padded {Uint8Array} unencrypted bytes that have been padded
52137       * @return {Uint8Array} the unpadded bytes
52138       * @see http://tools.ietf.org/html/rfc5652
52139       */
52140      function unpad(padded) {
52141        return padded.subarray(0, padded.byteLength - padded[padded.byteLength - 1]);
52142      }
52143
52144      var classCallCheck = function classCallCheck(instance, Constructor) {
52145        if (!(instance instanceof Constructor)) {
52146          throw new TypeError("Cannot call a class as a function");
52147        }
52148      };
52149
52150      var createClass = function () {
52151        function defineProperties(target, props) {
52152          for (var i = 0; i < props.length; i++) {
52153            var descriptor = props[i];
52154            descriptor.enumerable = descriptor.enumerable || false;
52155            descriptor.configurable = true;
52156            if ("value" in descriptor) descriptor.writable = true;
52157            Object.defineProperty(target, descriptor.key, descriptor);
52158          }
52159        }
52160
52161        return function (Constructor, protoProps, staticProps) {
52162          if (protoProps) defineProperties(Constructor.prototype, protoProps);
52163          if (staticProps) defineProperties(Constructor, staticProps);
52164          return Constructor;
52165        };
52166      }();
52167
52168      var inherits = function inherits(subClass, superClass) {
52169        if (typeof superClass !== "function" && superClass !== null) {
52170          throw new TypeError("Super expression must either be null or a function, not " + typeof superClass);
52171        }
52172
52173        subClass.prototype = Object.create(superClass && superClass.prototype, {
52174          constructor: {
52175            value: subClass,
52176            enumerable: false,
52177            writable: true,
52178            configurable: true
52179          }
52180        });
52181        if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass;
52182      };
52183
52184      var possibleConstructorReturn = function possibleConstructorReturn(self, call) {
52185        if (!self) {
52186          throw new ReferenceError("this hasn't been initialised - super() hasn't been called");
52187        }
52188
52189        return call && (typeof call === "object" || typeof call === "function") ? call : self;
52190      };
52191      /**
52192       * @file aes.js
52193       *
52194       * This file contains an adaptation of the AES decryption algorithm
52195       * from the Standford Javascript Cryptography Library. That work is
52196       * covered by the following copyright and permissions notice:
52197       *
52198       * Copyright 2009-2010 Emily Stark, Mike Hamburg, Dan Boneh.
52199       * All rights reserved.
52200       *
52201       * Redistribution and use in source and binary forms, with or without
52202       * modification, are permitted provided that the following conditions are
52203       * met:
52204       *
52205       * 1. Redistributions of source code must retain the above copyright
52206       *    notice, this list of conditions and the following disclaimer.
52207       *
52208       * 2. Redistributions in binary form must reproduce the above
52209       *    copyright notice, this list of conditions and the following
52210       *    disclaimer in the documentation and/or other materials provided
52211       *    with the distribution.
52212       *
52213       * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR
52214       * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
52215       * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
52216       * DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> OR CONTRIBUTORS BE
52217       * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
52218       * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
52219       * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
52220       * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
52221       * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
52222       * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
52223       * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
52224       *
52225       * The views and conclusions contained in the software and documentation
52226       * are those of the authors and should not be interpreted as representing
52227       * official policies, either expressed or implied, of the authors.
52228       */
52229
52230      /**
52231       * Expand the S-box tables.
52232       *
52233       * @private
52234       */
52235
52236
52237      var precompute = function precompute() {
52238        var tables = [[[], [], [], [], []], [[], [], [], [], []]];
52239        var encTable = tables[0];
52240        var decTable = tables[1];
52241        var sbox = encTable[4];
52242        var sboxInv = decTable[4];
52243        var i = void 0;
52244        var x = void 0;
52245        var xInv = void 0;
52246        var d = [];
52247        var th = [];
52248        var x2 = void 0;
52249        var x4 = void 0;
52250        var x8 = void 0;
52251        var s = void 0;
52252        var tEnc = void 0;
52253        var tDec = void 0; // Compute double and third tables
52254
52255        for (i = 0; i < 256; i++) {
52256          th[(d[i] = i << 1 ^ (i >> 7) * 283) ^ i] = i;
52257        }
52258
52259        for (x = xInv = 0; !sbox[x]; x ^= x2 || 1, xInv = th[xInv] || 1) {
52260          // Compute sbox
52261          s = xInv ^ xInv << 1 ^ xInv << 2 ^ xInv << 3 ^ xInv << 4;
52262          s = s >> 8 ^ s & 255 ^ 99;
52263          sbox[x] = s;
52264          sboxInv[s] = x; // Compute MixColumns
52265
52266          x8 = d[x4 = d[x2 = d[x]]];
52267          tDec = x8 * 0x1010101 ^ x4 * 0x10001 ^ x2 * 0x101 ^ x * 0x1010100;
52268          tEnc = d[s] * 0x101 ^ s * 0x1010100;
52269
52270          for (i = 0; i < 4; i++) {
52271            encTable[i][x] = tEnc = tEnc << 24 ^ tEnc >>> 8;
52272            decTable[i][s] = tDec = tDec << 24 ^ tDec >>> 8;
52273          }
52274        } // Compactify. Considerable speedup on Firefox.
52275
52276
52277        for (i = 0; i < 5; i++) {
52278          encTable[i] = encTable[i].slice(0);
52279          decTable[i] = decTable[i].slice(0);
52280        }
52281
52282        return tables;
52283      };
52284
52285      var aesTables = null;
52286      /**
52287       * Schedule out an AES key for both encryption and decryption. This
52288       * is a low-level class. Use a cipher mode to do bulk encryption.
52289       *
52290       * @class AES
52291       * @param key {Array} The key as an array of 4, 6 or 8 words.
52292       */
52293
52294      var AES = function () {
52295        function AES(key) {
52296          classCallCheck(this, AES);
52297          /**
52298           * The expanded S-box and inverse S-box tables. These will be computed
52299           * on the client so that we don't have to send them down the wire.
52300           *
52301           * There are two tables, _tables[0] is for encryption and
52302           * _tables[1] is for decryption.
52303           *
52304           * The first 4 sub-tables are the expanded S-box with MixColumns. The
52305           * last (_tables[01][4]) is the S-box itself.
52306           *
52307           * @private
52308           */
52309          // if we have yet to precompute the S-box tables
52310          // do so now
52311
52312          if (!aesTables) {
52313            aesTables = precompute();
52314          } // then make a copy of that object for use
52315
52316
52317          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()]];
52318          var i = void 0;
52319          var j = void 0;
52320          var tmp = void 0;
52321          var encKey = void 0;
52322          var decKey = void 0;
52323          var sbox = this._tables[0][4];
52324          var decTable = this._tables[1];
52325          var keyLen = key.length;
52326          var rcon = 1;
52327
52328          if (keyLen !== 4 && keyLen !== 6 && keyLen !== 8) {
52329            throw new Error('Invalid aes key size');
52330          }
52331
52332          encKey = key.slice(0);
52333          decKey = [];
52334          this._key = [encKey, decKey]; // schedule encryption keys
52335
52336          for (i = keyLen; i < 4 * keyLen + 28; i++) {
52337            tmp = encKey[i - 1]; // apply sbox
52338
52339            if (i % keyLen === 0 || keyLen === 8 && i % keyLen === 4) {
52340              tmp = sbox[tmp >>> 24] << 24 ^ sbox[tmp >> 16 & 255] << 16 ^ sbox[tmp >> 8 & 255] << 8 ^ sbox[tmp & 255]; // shift rows and add rcon
52341
52342              if (i % keyLen === 0) {
52343                tmp = tmp << 8 ^ tmp >>> 24 ^ rcon << 24;
52344                rcon = rcon << 1 ^ (rcon >> 7) * 283;
52345              }
52346            }
52347
52348            encKey[i] = encKey[i - keyLen] ^ tmp;
52349          } // schedule decryption keys
52350
52351
52352          for (j = 0; i; j++, i--) {
52353            tmp = encKey[j & 3 ? i : i - 4];
52354
52355            if (i <= 4 || j < 4) {
52356              decKey[j] = tmp;
52357            } else {
52358              decKey[j] = decTable[0][sbox[tmp >>> 24]] ^ decTable[1][sbox[tmp >> 16 & 255]] ^ decTable[2][sbox[tmp >> 8 & 255]] ^ decTable[3][sbox[tmp & 255]];
52359            }
52360          }
52361        }
52362        /**
52363         * Decrypt 16 bytes, specified as four 32-bit words.
52364         *
52365         * @param {Number} encrypted0 the first word to decrypt
52366         * @param {Number} encrypted1 the second word to decrypt
52367         * @param {Number} encrypted2 the third word to decrypt
52368         * @param {Number} encrypted3 the fourth word to decrypt
52369         * @param {Int32Array} out the array to write the decrypted words
52370         * into
52371         * @param {Number} offset the offset into the output array to start
52372         * writing results
52373         * @return {Array} The plaintext.
52374         */
52375
52376
52377        AES.prototype.decrypt = function decrypt$$1(encrypted0, encrypted1, encrypted2, encrypted3, out, offset) {
52378          var key = this._key[1]; // state variables a,b,c,d are loaded with pre-whitened data
52379
52380          var a = encrypted0 ^ key[0];
52381          var b = encrypted3 ^ key[1];
52382          var c = encrypted2 ^ key[2];
52383          var d = encrypted1 ^ key[3];
52384          var a2 = void 0;
52385          var b2 = void 0;
52386          var c2 = void 0; // key.length === 2 ?
52387
52388          var nInnerRounds = key.length / 4 - 2;
52389          var i = void 0;
52390          var kIndex = 4;
52391          var table = this._tables[1]; // load up the tables
52392
52393          var table0 = table[0];
52394          var table1 = table[1];
52395          var table2 = table[2];
52396          var table3 = table[3];
52397          var sbox = table[4]; // Inner rounds. Cribbed from OpenSSL.
52398
52399          for (i = 0; i < nInnerRounds; i++) {
52400            a2 = table0[a >>> 24] ^ table1[b >> 16 & 255] ^ table2[c >> 8 & 255] ^ table3[d & 255] ^ key[kIndex];
52401            b2 = table0[b >>> 24] ^ table1[c >> 16 & 255] ^ table2[d >> 8 & 255] ^ table3[a & 255] ^ key[kIndex + 1];
52402            c2 = table0[c >>> 24] ^ table1[d >> 16 & 255] ^ table2[a >> 8 & 255] ^ table3[b & 255] ^ key[kIndex + 2];
52403            d = table0[d >>> 24] ^ table1[a >> 16 & 255] ^ table2[b >> 8 & 255] ^ table3[c & 255] ^ key[kIndex + 3];
52404            kIndex += 4;
52405            a = a2;
52406            b = b2;
52407            c = c2;
52408          } // Last round.
52409
52410
52411          for (i = 0; i < 4; i++) {
52412            out[(3 & -i) + offset] = sbox[a >>> 24] << 24 ^ sbox[b >> 16 & 255] << 16 ^ sbox[c >> 8 & 255] << 8 ^ sbox[d & 255] ^ key[kIndex++];
52413            a2 = a;
52414            a = b;
52415            b = c;
52416            c = d;
52417            d = a2;
52418          }
52419        };
52420
52421        return AES;
52422      }();
52423      /**
52424       * @file stream.js
52425       */
52426
52427      /**
52428       * A lightweight readable stream implemention that handles event dispatching.
52429       *
52430       * @class Stream
52431       */
52432
52433
52434      var Stream = function () {
52435        function Stream() {
52436          classCallCheck(this, Stream);
52437          this.listeners = {};
52438        }
52439        /**
52440         * Add a listener for a specified event type.
52441         *
52442         * @param {String} type the event name
52443         * @param {Function} listener the callback to be invoked when an event of
52444         * the specified type occurs
52445         */
52446
52447
52448        Stream.prototype.on = function on(type, listener) {
52449          if (!this.listeners[type]) {
52450            this.listeners[type] = [];
52451          }
52452
52453          this.listeners[type].push(listener);
52454        };
52455        /**
52456         * Remove a listener for a specified event type.
52457         *
52458         * @param {String} type the event name
52459         * @param {Function} listener  a function previously registered for this
52460         * type of event through `on`
52461         * @return {Boolean} if we could turn it off or not
52462         */
52463
52464
52465        Stream.prototype.off = function off(type, listener) {
52466          if (!this.listeners[type]) {
52467            return false;
52468          }
52469
52470          var index = this.listeners[type].indexOf(listener);
52471          this.listeners[type].splice(index, 1);
52472          return index > -1;
52473        };
52474        /**
52475         * Trigger an event of the specified type on this stream. Any additional
52476         * arguments to this function are passed as parameters to event listeners.
52477         *
52478         * @param {String} type the event name
52479         */
52480
52481
52482        Stream.prototype.trigger = function trigger(type) {
52483          var callbacks = this.listeners[type];
52484
52485          if (!callbacks) {
52486            return;
52487          } // Slicing the arguments on every invocation of this method
52488          // can add a significant amount of overhead. Avoid the
52489          // intermediate object creation for the common case of a
52490          // single callback argument
52491
52492
52493          if (arguments.length === 2) {
52494            var length = callbacks.length;
52495
52496            for (var i = 0; i < length; ++i) {
52497              callbacks[i].call(this, arguments[1]);
52498            }
52499          } else {
52500            var args = Array.prototype.slice.call(arguments, 1);
52501            var _length = callbacks.length;
52502
52503            for (var _i = 0; _i < _length; ++_i) {
52504              callbacks[_i].apply(this, args);
52505            }
52506          }
52507        };
52508        /**
52509         * Destroys the stream and cleans up.
52510         */
52511
52512
52513        Stream.prototype.dispose = function dispose() {
52514          this.listeners = {};
52515        };
52516        /**
52517         * Forwards all `data` events on this stream to the destination stream. The
52518         * destination stream should provide a method `push` to receive the data
52519         * events as they arrive.
52520         *
52521         * @param {Stream} destination the stream that will receive all `data` events
52522         * @see http://nodejs.org/api/stream.html#stream_readable_pipe_destination_options
52523         */
52524
52525
52526        Stream.prototype.pipe = function pipe(destination) {
52527          this.on('data', function (data) {
52528            destination.push(data);
52529          });
52530        };
52531
52532        return Stream;
52533      }();
52534      /**
52535       * @file async-stream.js
52536       */
52537
52538      /**
52539       * A wrapper around the Stream class to use setTiemout
52540       * and run stream "jobs" Asynchronously
52541       *
52542       * @class AsyncStream
52543       * @extends Stream
52544       */
52545
52546
52547      var AsyncStream$$1 = function (_Stream) {
52548        inherits(AsyncStream$$1, _Stream);
52549
52550        function AsyncStream$$1() {
52551          classCallCheck(this, AsyncStream$$1);
52552
52553          var _this = possibleConstructorReturn(this, _Stream.call(this, Stream));
52554
52555          _this.jobs = [];
52556          _this.delay = 1;
52557          _this.timeout_ = null;
52558          return _this;
52559        }
52560        /**
52561         * process an async job
52562         *
52563         * @private
52564         */
52565
52566
52567        AsyncStream$$1.prototype.processJob_ = function processJob_() {
52568          this.jobs.shift()();
52569
52570          if (this.jobs.length) {
52571            this.timeout_ = setTimeout(this.processJob_.bind(this), this.delay);
52572          } else {
52573            this.timeout_ = null;
52574          }
52575        };
52576        /**
52577         * push a job into the stream
52578         *
52579         * @param {Function} job the job to push into the stream
52580         */
52581
52582
52583        AsyncStream$$1.prototype.push = function push(job) {
52584          this.jobs.push(job);
52585
52586          if (!this.timeout_) {
52587            this.timeout_ = setTimeout(this.processJob_.bind(this), this.delay);
52588          }
52589        };
52590
52591        return AsyncStream$$1;
52592      }(Stream);
52593      /**
52594       * @file decrypter.js
52595       *
52596       * An asynchronous implementation of AES-128 CBC decryption with
52597       * PKCS#7 padding.
52598       */
52599
52600      /**
52601       * Convert network-order (big-endian) bytes into their little-endian
52602       * representation.
52603       */
52604
52605
52606      var ntoh = function ntoh(word) {
52607        return word << 24 | (word & 0xff00) << 8 | (word & 0xff0000) >> 8 | word >>> 24;
52608      };
52609      /**
52610       * Decrypt bytes using AES-128 with CBC and PKCS#7 padding.
52611       *
52612       * @param {Uint8Array} encrypted the encrypted bytes
52613       * @param {Uint32Array} key the bytes of the decryption key
52614       * @param {Uint32Array} initVector the initialization vector (IV) to
52615       * use for the first round of CBC.
52616       * @return {Uint8Array} the decrypted bytes
52617       *
52618       * @see http://en.wikipedia.org/wiki/Advanced_Encryption_Standard
52619       * @see http://en.wikipedia.org/wiki/Block_cipher_mode_of_operation#Cipher_Block_Chaining_.28CBC.29
52620       * @see https://tools.ietf.org/html/rfc2315
52621       */
52622
52623
52624      var decrypt$$1 = function decrypt$$1(encrypted, key, initVector) {
52625        // word-level access to the encrypted bytes
52626        var encrypted32 = new Int32Array(encrypted.buffer, encrypted.byteOffset, encrypted.byteLength >> 2);
52627        var decipher = new AES(Array.prototype.slice.call(key)); // byte and word-level access for the decrypted output
52628
52629        var decrypted = new Uint8Array(encrypted.byteLength);
52630        var decrypted32 = new Int32Array(decrypted.buffer); // temporary variables for working with the IV, encrypted, and
52631        // decrypted data
52632
52633        var init0 = void 0;
52634        var init1 = void 0;
52635        var init2 = void 0;
52636        var init3 = void 0;
52637        var encrypted0 = void 0;
52638        var encrypted1 = void 0;
52639        var encrypted2 = void 0;
52640        var encrypted3 = void 0; // iteration variable
52641
52642        var wordIx = void 0; // pull out the words of the IV to ensure we don't modify the
52643        // passed-in reference and easier access
52644
52645        init0 = initVector[0];
52646        init1 = initVector[1];
52647        init2 = initVector[2];
52648        init3 = initVector[3]; // decrypt four word sequences, applying cipher-block chaining (CBC)
52649        // to each decrypted block
52650
52651        for (wordIx = 0; wordIx < encrypted32.length; wordIx += 4) {
52652          // convert big-endian (network order) words into little-endian
52653          // (javascript order)
52654          encrypted0 = ntoh(encrypted32[wordIx]);
52655          encrypted1 = ntoh(encrypted32[wordIx + 1]);
52656          encrypted2 = ntoh(encrypted32[wordIx + 2]);
52657          encrypted3 = ntoh(encrypted32[wordIx + 3]); // decrypt the block
52658
52659          decipher.decrypt(encrypted0, encrypted1, encrypted2, encrypted3, decrypted32, wordIx); // XOR with the IV, and restore network byte-order to obtain the
52660          // plaintext
52661
52662          decrypted32[wordIx] = ntoh(decrypted32[wordIx] ^ init0);
52663          decrypted32[wordIx + 1] = ntoh(decrypted32[wordIx + 1] ^ init1);
52664          decrypted32[wordIx + 2] = ntoh(decrypted32[wordIx + 2] ^ init2);
52665          decrypted32[wordIx + 3] = ntoh(decrypted32[wordIx + 3] ^ init3); // setup the IV for the next round
52666
52667          init0 = encrypted0;
52668          init1 = encrypted1;
52669          init2 = encrypted2;
52670          init3 = encrypted3;
52671        }
52672
52673        return decrypted;
52674      };
52675      /**
52676       * The `Decrypter` class that manages decryption of AES
52677       * data through `AsyncStream` objects and the `decrypt`
52678       * function
52679       *
52680       * @param {Uint8Array} encrypted the encrypted bytes
52681       * @param {Uint32Array} key the bytes of the decryption key
52682       * @param {Uint32Array} initVector the initialization vector (IV) to
52683       * @param {Function} done the function to run when done
52684       * @class Decrypter
52685       */
52686
52687
52688      var Decrypter$$1 = function () {
52689        function Decrypter$$1(encrypted, key, initVector, done) {
52690          classCallCheck(this, Decrypter$$1);
52691          var step = Decrypter$$1.STEP;
52692          var encrypted32 = new Int32Array(encrypted.buffer);
52693          var decrypted = new Uint8Array(encrypted.byteLength);
52694          var i = 0;
52695          this.asyncStream_ = new AsyncStream$$1(); // split up the encryption job and do the individual chunks asynchronously
52696
52697          this.asyncStream_.push(this.decryptChunk_(encrypted32.subarray(i, i + step), key, initVector, decrypted));
52698
52699          for (i = step; i < encrypted32.length; i += step) {
52700            initVector = new Uint32Array([ntoh(encrypted32[i - 4]), ntoh(encrypted32[i - 3]), ntoh(encrypted32[i - 2]), ntoh(encrypted32[i - 1])]);
52701            this.asyncStream_.push(this.decryptChunk_(encrypted32.subarray(i, i + step), key, initVector, decrypted));
52702          } // invoke the done() callback when everything is finished
52703
52704
52705          this.asyncStream_.push(function () {
52706            // remove pkcs#7 padding from the decrypted bytes
52707            done(null, unpad(decrypted));
52708          });
52709        }
52710        /**
52711         * a getter for step the maximum number of bytes to process at one time
52712         *
52713         * @return {Number} the value of step 32000
52714         */
52715
52716        /**
52717         * @private
52718         */
52719
52720
52721        Decrypter$$1.prototype.decryptChunk_ = function decryptChunk_(encrypted, key, initVector, decrypted) {
52722          return function () {
52723            var bytes = decrypt$$1(encrypted, key, initVector);
52724            decrypted.set(bytes, encrypted.byteOffset);
52725          };
52726        };
52727
52728        createClass(Decrypter$$1, null, [{
52729          key: 'STEP',
52730          get: function get$$1() {
52731            // 4 * 8000;
52732            return 32000;
52733          }
52734        }]);
52735        return Decrypter$$1;
52736      }();
52737      /**
52738       * @file bin-utils.js
52739       */
52740
52741      /**
52742       * Creates an object for sending to a web worker modifying properties that are TypedArrays
52743       * into a new object with seperated properties for the buffer, byteOffset, and byteLength.
52744       *
52745       * @param {Object} message
52746       *        Object of properties and values to send to the web worker
52747       * @return {Object}
52748       *         Modified message with TypedArray values expanded
52749       * @function createTransferableMessage
52750       */
52751
52752
52753      var createTransferableMessage = function createTransferableMessage(message) {
52754        var transferable = {};
52755        Object.keys(message).forEach(function (key) {
52756          var value = message[key];
52757
52758          if (ArrayBuffer.isView(value)) {
52759            transferable[key] = {
52760              bytes: value.buffer,
52761              byteOffset: value.byteOffset,
52762              byteLength: value.byteLength
52763            };
52764          } else {
52765            transferable[key] = value;
52766          }
52767        });
52768        return transferable;
52769      };
52770      /**
52771       * Our web worker interface so that things can talk to aes-decrypter
52772       * that will be running in a web worker. the scope is passed to this by
52773       * webworkify.
52774       *
52775       * @param {Object} self
52776       *        the scope for the web worker
52777       */
52778
52779
52780      var DecrypterWorker = function DecrypterWorker(self) {
52781        self.onmessage = function (event) {
52782          var data = event.data;
52783          var encrypted = new Uint8Array(data.encrypted.bytes, data.encrypted.byteOffset, data.encrypted.byteLength);
52784          var key = new Uint32Array(data.key.bytes, data.key.byteOffset, data.key.byteLength / 4);
52785          var iv = new Uint32Array(data.iv.bytes, data.iv.byteOffset, data.iv.byteLength / 4);
52786          /* eslint-disable no-new, handle-callback-err */
52787
52788          new Decrypter$$1(encrypted, key, iv, function (err, bytes) {
52789            self.postMessage(createTransferableMessage({
52790              source: data.source,
52791              decrypted: bytes
52792            }), [bytes.buffer]);
52793          });
52794          /* eslint-enable */
52795        };
52796      };
52797
52798      var decrypterWorker = new DecrypterWorker(self);
52799      return decrypterWorker;
52800    }();
52801  });
52802  /**
52803   * Convert the properties of an HLS track into an audioTrackKind.
52804   *
52805   * @private
52806   */
52807
52808  var audioTrackKind_ = function audioTrackKind_(properties) {
52809    var kind = properties["default"] ? 'main' : 'alternative';
52810
52811    if (properties.characteristics && properties.characteristics.indexOf('public.accessibility.describes-video') >= 0) {
52812      kind = 'main-desc';
52813    }
52814
52815    return kind;
52816  };
52817  /**
52818   * Pause provided segment loader and playlist loader if active
52819   *
52820   * @param {SegmentLoader} segmentLoader
52821   *        SegmentLoader to pause
52822   * @param {Object} mediaType
52823   *        Active media type
52824   * @function stopLoaders
52825   */
52826
52827
52828  var stopLoaders = function stopLoaders(segmentLoader, mediaType) {
52829    segmentLoader.abort();
52830    segmentLoader.pause();
52831
52832    if (mediaType && mediaType.activePlaylistLoader) {
52833      mediaType.activePlaylistLoader.pause();
52834      mediaType.activePlaylistLoader = null;
52835    }
52836  };
52837  /**
52838   * Start loading provided segment loader and playlist loader
52839   *
52840   * @param {PlaylistLoader} playlistLoader
52841   *        PlaylistLoader to start loading
52842   * @param {Object} mediaType
52843   *        Active media type
52844   * @function startLoaders
52845   */
52846
52847
52848  var startLoaders = function startLoaders(playlistLoader, mediaType) {
52849    // Segment loader will be started after `loadedmetadata` or `loadedplaylist` from the
52850    // playlist loader
52851    mediaType.activePlaylistLoader = playlistLoader;
52852    playlistLoader.load();
52853  };
52854  /**
52855   * Returns a function to be called when the media group changes. It performs a
52856   * non-destructive (preserve the buffer) resync of the SegmentLoader. This is because a
52857   * change of group is merely a rendition switch of the same content at another encoding,
52858   * rather than a change of content, such as switching audio from English to Spanish.
52859   *
52860   * @param {String} type
52861   *        MediaGroup type
52862   * @param {Object} settings
52863   *        Object containing required information for media groups
52864   * @return {Function}
52865   *         Handler for a non-destructive resync of SegmentLoader when the active media
52866   *         group changes.
52867   * @function onGroupChanged
52868   */
52869
52870
52871  var onGroupChanged = function onGroupChanged(type, settings) {
52872    return function () {
52873      var _settings$segmentLoad = settings.segmentLoaders,
52874          segmentLoader = _settings$segmentLoad[type],
52875          mainSegmentLoader = _settings$segmentLoad.main,
52876          mediaType = settings.mediaTypes[type];
52877      var activeTrack = mediaType.activeTrack();
52878      var activeGroup = mediaType.activeGroup(activeTrack);
52879      var previousActiveLoader = mediaType.activePlaylistLoader;
52880      stopLoaders(segmentLoader, mediaType);
52881
52882      if (!activeGroup) {
52883        // there is no group active
52884        return;
52885      }
52886
52887      if (!activeGroup.playlistLoader) {
52888        if (previousActiveLoader) {
52889          // The previous group had a playlist loader but the new active group does not
52890          // this means we are switching from demuxed to muxed audio. In this case we want to
52891          // do a destructive reset of the main segment loader and n
vendor: 16,384 bytes, lines 52891-53318
52891ot restart the audio
52892          // loaders.
52893          mainSegmentLoader.resetEverything();
52894        }
52895
52896        return;
52897      } // Non-destructive resync
52898
52899
52900      segmentLoader.resyncLoader();
52901      startLoaders(activeGroup.playlistLoader, mediaType);
52902    };
52903  };
52904  /**
52905   * Returns a function to be called when the media track changes. It performs a
52906   * destructive reset of the SegmentLoader to ensure we start loading as close to
52907   * currentTime as possible.
52908   *
52909   * @param {String} type
52910   *        MediaGroup type
52911   * @param {Object} settings
52912   *        Object containing required information for media groups
52913   * @return {Function}
52914   *         Handler for a destructive reset of SegmentLoader when the active media
52915   *         track changes.
52916   * @function onTrackChanged
52917   */
52918
52919
52920  var onTrackChanged = function onTrackChanged(type, settings) {
52921    return function () {
52922      var _settings$segmentLoad2 = settings.segmentLoaders,
52923          segmentLoader = _settings$segmentLoad2[type],
52924          mainSegmentLoader = _settings$segmentLoad2.main,
52925          mediaType = settings.mediaTypes[type];
52926      var activeTrack = mediaType.activeTrack();
52927      var activeGroup = mediaType.activeGroup(activeTrack);
52928      var previousActiveLoader = mediaType.activePlaylistLoader;
52929      stopLoaders(segmentLoader, mediaType);
52930
52931      if (!activeGroup) {
52932        // there is no group active so we do not want to restart loaders
52933        return;
52934      }
52935
52936      if (!activeGroup.playlistLoader) {
52937        // when switching from demuxed audio/video to muxed audio/video (noted by no playlist
52938        // loader for the audio group), we want to do a destructive reset of the main segment
52939        // loader and not restart the audio loaders
52940        mainSegmentLoader.resetEverything();
52941        return;
52942      }
52943
52944      if (previousActiveLoader === activeGroup.playlistLoader) {
52945        // Nothing has actually changed. This can happen because track change events can fire
52946        // multiple times for a "single" change. One for enabling the new active track, and
52947        // one for disabling the track that was active
52948        startLoaders(activeGroup.playlistLoader, mediaType);
52949        return;
52950      }
52951
52952      if (segmentLoader.track) {
52953        // For WebVTT, set the new text track in the segmentloader
52954        segmentLoader.track(activeTrack);
52955      } // destructive reset
52956
52957
52958      segmentLoader.resetEverything();
52959      startLoaders(activeGroup.playlistLoader, mediaType);
52960    };
52961  };
52962
52963  var onError = {
52964    /**
52965     * Returns a function to be called when a SegmentLoader or PlaylistLoader encounters
52966     * an error.
52967     *
52968     * @param {String} type
52969     *        MediaGroup type
52970     * @param {Object} settings
52971     *        Object containing required information for media groups
52972     * @return {Function}
52973     *         Error handler. Logs warning (or error if the playlist is blacklisted) to
52974     *         console and switches back to default audio track.
52975     * @function onError.AUDIO
52976     */
52977    AUDIO: function AUDIO(type, settings) {
52978      return function () {
52979        var segmentLoader = settings.segmentLoaders[type],
52980            mediaType = settings.mediaTypes[type],
52981            blacklistCurrentPlaylist = settings.blacklistCurrentPlaylist;
52982        stopLoaders(segmentLoader, mediaType); // switch back to default audio track
52983
52984        var activeTrack = mediaType.activeTrack();
52985        var activeGroup = mediaType.activeGroup();
52986        var id = (activeGroup.filter(function (group) {
52987          return group["default"];
52988        })[0] || activeGroup[0]).id;
52989        var defaultTrack = mediaType.tracks[id];
52990
52991        if (activeTrack === defaultTrack) {
52992          // Default track encountered an error. All we can do now is blacklist the current
52993          // rendition and hope another will switch audio groups
52994          blacklistCurrentPlaylist({
52995            message: 'Problem encountered loading the default audio track.'
52996          });
52997          return;
52998        }
52999
53000        videojs$1.log.warn('Problem encountered loading the alternate audio track.' + 'Switching back to default.');
53001
53002        for (var trackId in mediaType.tracks) {
53003          mediaType.tracks[trackId].enabled = mediaType.tracks[trackId] === defaultTrack;
53004        }
53005
53006        mediaType.onTrackChanged();
53007      };
53008    },
53009
53010    /**
53011     * Returns a function to be called when a SegmentLoader or PlaylistLoader encounters
53012     * an error.
53013     *
53014     * @param {String} type
53015     *        MediaGroup type
53016     * @param {Object} settings
53017     *        Object containing required information for media groups
53018     * @return {Function}
53019     *         Error handler. Logs warning to console and disables the active subtitle track
53020     * @function onError.SUBTITLES
53021     */
53022    SUBTITLES: function SUBTITLES(type, settings) {
53023      return function () {
53024        var segmentLoader = settings.segmentLoaders[type],
53025            mediaType = settings.mediaTypes[type];
53026        videojs$1.log.warn('Problem encountered loading the subtitle track.' + 'Disabling subtitle track.');
53027        stopLoaders(segmentLoader, mediaType);
53028        var track = mediaType.activeTrack();
53029
53030        if (track) {
53031          track.mode = 'disabled';
53032        }
53033
53034        mediaType.onTrackChanged();
53035      };
53036    }
53037  };
53038  var setupListeners = {
53039    /**
53040     * Setup event listeners for audio playlist loader
53041     *
53042     * @param {String} type
53043     *        MediaGroup type
53044     * @param {PlaylistLoader|null} playlistLoader
53045     *        PlaylistLoader to register listeners on
53046     * @param {Object} settings
53047     *        Object containing required information for media groups
53048     * @function setupListeners.AUDIO
53049     */
53050    AUDIO: function AUDIO(type, playlistLoader, settings) {
53051      if (!playlistLoader) {
53052        // no playlist loader means audio will be muxed with the video
53053        return;
53054      }
53055
53056      var tech = settings.tech,
53057          requestOptions = settings.requestOptions,
53058          segmentLoader = settings.segmentLoaders[type];
53059      playlistLoader.on('loadedmetadata', function () {
53060        var media = playlistLoader.media();
53061        segmentLoader.playlist(media, requestOptions); // if the video is already playing, or if this isn't a live video and preload
53062        // permits, start downloading segments
53063
53064        if (!tech.paused() || media.endList && tech.preload() !== 'none') {
53065          segmentLoader.load();
53066        }
53067      });
53068      playlistLoader.on('loadedplaylist', function () {
53069        segmentLoader.playlist(playlistLoader.media(), requestOptions); // If the player isn't paused, ensure that the segment loader is running
53070
53071        if (!tech.paused()) {
53072          segmentLoader.load();
53073        }
53074      });
53075      playlistLoader.on('error', onError[type](type, settings));
53076    },
53077
53078    /**
53079     * Setup event listeners for subtitle playlist loader
53080     *
53081     * @param {String} type
53082     *        MediaGroup type
53083     * @param {PlaylistLoader|null} playlistLoader
53084     *        PlaylistLoader to register listeners on
53085     * @param {Object} settings
53086     *        Object containing required information for media groups
53087     * @function setupListeners.SUBTITLES
53088     */
53089    SUBTITLES: function SUBTITLES(type, playlistLoader, settings) {
53090      var tech = settings.tech,
53091          requestOptions = settings.requestOptions,
53092          segmentLoader = settings.segmentLoaders[type],
53093          mediaType = settings.mediaTypes[type];
53094      playlistLoader.on('loadedmetadata', function () {
53095        var media = playlistLoader.media();
53096        segmentLoader.playlist(media, requestOptions);
53097        segmentLoader.track(mediaType.activeTrack()); // if the video is already playing, or if this isn't a live video and preload
53098        // permits, start downloading segments
53099
53100        if (!tech.paused() || media.endList && tech.preload() !== 'none') {
53101          segmentLoader.load();
53102        }
53103      });
53104      playlistLoader.on('loadedplaylist', function () {
53105        segmentLoader.playlist(playlistLoader.media(), requestOptions); // If the player isn't paused, ensure that the segment loader is running
53106
53107        if (!tech.paused()) {
53108          segmentLoader.load();
53109        }
53110      });
53111      playlistLoader.on('error', onError[type](type, settings));
53112    }
53113  };
53114  var initialize = {
53115    /**
53116     * Setup PlaylistLoaders and AudioTracks for the audio groups
53117     *
53118     * @param {String} type
53119     *        MediaGroup type
53120     * @param {Object} settings
53121     *        Object containing required information for media groups
53122     * @function initialize.AUDIO
53123     */
53124    'AUDIO': function AUDIO(type, settings) {
53125      var hls = settings.hls,
53126          sourceType = settings.sourceType,
53127          segmentLoader = settings.segmentLoaders[type],
53128          requestOptions = settings.requestOptions,
53129          mediaGroups = settings.master.mediaGroups,
53130          _settings$mediaTypes$ = settings.mediaTypes[type],
53131          groups = _settings$mediaTypes$.groups,
53132          tracks = _settings$mediaTypes$.tracks,
53133          masterPlaylistLoader = settings.masterPlaylistLoader; // force a default if we have none
53134
53135      if (!mediaGroups[type] || Object.keys(mediaGroups[type]).length === 0) {
53136        mediaGroups[type] = {
53137          main: {
53138            "default": {
53139              "default": true
53140            }
53141          }
53142        };
53143      }
53144
53145      for (var groupId in mediaGroups[type]) {
53146        if (!groups[groupId]) {
53147          groups[groupId] = [];
53148        } // List of playlists that have an AUDIO attribute value matching the current
53149        // group ID
53150
53151
53152        for (var variantLabel in mediaGroups[type][groupId]) {
53153          var properties = mediaGroups[type][groupId][variantLabel];
53154          var playlistLoader = void 0;
53155
53156          if (properties.resolvedUri) {
53157            playlistLoader = new PlaylistLoader(properties.resolvedUri, hls, requestOptions);
53158          } else if (properties.playlists && sourceType === 'dash') {
53159            playlistLoader = new DashPlaylistLoader(properties.playlists[0], hls, requestOptions, masterPlaylistLoader);
53160          } else {
53161            // no resolvedUri means the audio is muxed with the video when using this
53162            // audio track
53163            playlistLoader = null;
53164          }
53165
53166          properties = videojs$1.mergeOptions({
53167            id: variantLabel,
53168            playlistLoader: playlistLoader
53169          }, properties);
53170          setupListeners[type](type, properties.playlistLoader, settings);
53171          groups[groupId].push(properties);
53172
53173          if (typeof tracks[variantLabel] === 'undefined') {
53174            var track = new videojs$1.AudioTrack({
53175              id: variantLabel,
53176              kind: audioTrackKind_(properties),
53177              enabled: false,
53178              language: properties.language,
53179              "default": properties["default"],
53180              label: variantLabel
53181            });
53182            tracks[variantLabel] = track;
53183          }
53184        }
53185      } // setup single error event handler for the segment loader
53186
53187
53188      segmentLoader.on('error', onError[type](type, settings));
53189    },
53190
53191    /**
53192     * Setup PlaylistLoaders and TextTracks for the subtitle groups
53193     *
53194     * @param {String} type
53195     *        MediaGroup type
53196     * @param {Object} settings
53197     *        Object containing required information for media groups
53198     * @function initialize.SUBTITLES
53199     */
53200    'SUBTITLES': function SUBTITLES(type, settings) {
53201      var tech = settings.tech,
53202          hls = settings.hls,
53203          sourceType = settings.sourceType,
53204          segmentLoader = settings.segmentLoaders[type],
53205          requestOptions = settings.requestOptions,
53206          mediaGroups = settings.master.mediaGroups,
53207          _settings$mediaTypes$2 = settings.mediaTypes[type],
53208          groups = _settings$mediaTypes$2.groups,
53209          tracks = _settings$mediaTypes$2.tracks,
53210          masterPlaylistLoader = settings.masterPlaylistLoader;
53211
53212      for (var groupId in mediaGroups[type]) {
53213        if (!groups[groupId]) {
53214          groups[groupId] = [];
53215        }
53216
53217        for (var variantLabel in mediaGroups[type][groupId]) {
53218          if (mediaGroups[type][groupId][variantLabel].forced) {
53219            // Subtitle playlists with the forced attribute are not selectable in Safari.
53220            // According to Apple's HLS Authoring Specification:
53221            //   If content has forced subtitles and regular subtitles in a given language,
53222            //   the regular subtitles track in that language MUST contain both the forced
53223            //   subtitles and the regular subtitles for that language.
53224            // Because of this requirement and that Safari does not add forced subtitles,
53225            // forced subtitles are skipped here to maintain consistent experience across
53226            // all platforms
53227            continue;
53228          }
53229
53230          var properties = mediaGroups[type][groupId][variantLabel];
53231          var playlistLoader = void 0;
53232
53233          if (sourceType === 'hls') {
53234            playlistLoader = new PlaylistLoader(properties.resolvedUri, hls, requestOptions);
53235          } else if (sourceType === 'dash') {
53236            playlistLoader = new DashPlaylistLoader(properties.playlists[0], hls, requestOptions, masterPlaylistLoader);
53237          }
53238
53239          properties = videojs$1.mergeOptions({
53240            id: variantLabel,
53241            playlistLoader: playlistLoader
53242          }, properties);
53243          setupListeners[type](type, properties.playlistLoader, settings);
53244          groups[groupId].push(properties);
53245
53246          if (typeof tracks[variantLabel] === 'undefined') {
53247            var track = tech.addRemoteTextTrack({
53248              id: variantLabel,
53249              kind: 'subtitles',
53250              "default": properties["default"] && properties.autoselect,
53251              language: properties.language,
53252              label: variantLabel
53253            }, false).track;
53254            tracks[variantLabel] = track;
53255          }
53256        }
53257      } // setup single error event handler for the segment loader
53258
53259
53260      segmentLoader.on('error', onError[type](type, settings));
53261    },
53262
53263    /**
53264     * Setup TextTracks for the closed-caption groups
53265     *
53266     * @param {String} type
53267     *        MediaGroup type
53268     * @param {Object} settings
53269     *        Object containing required information for media groups
53270     * @function initialize['CLOSED-CAPTIONS']
53271     */
53272    'CLOSED-CAPTIONS': function CLOSEDCAPTIONS(type, settings) {
53273      var tech = settings.tech,
53274          mediaGroups = settings.master.mediaGroups,
53275          _settings$mediaTypes$3 = settings.mediaTypes[type],
53276          groups = _settings$mediaTypes$3.groups,
53277          tracks = _settings$mediaTypes$3.tracks;
53278
53279      for (var groupId in mediaGroups[type]) {
53280        if (!groups[groupId]) {
53281          groups[groupId] = [];
53282        }
53283
53284        for (var variantLabel in mediaGroups[type][groupId]) {
53285          var properties = mediaGroups[type][groupId][variantLabel]; // We only support CEA608 captions for now, so ignore anything that
53286          // doesn't use a CCx INSTREAM-ID
53287
53288          if (!properties.instreamId.match(/CC\d/)) {
53289            continue;
53290          } // No PlaylistLoader is required for Closed-Captions because the captions are
53291          // embedded within the video stream
53292
53293
53294          groups[groupId].push(videojs$1.mergeOptions({
53295            id: variantLabel
53296          }, properties));
53297
53298          if (typeof tracks[variantLabel] === 'undefined') {
53299            var track = tech.addRemoteTextTrack({
53300              id: properties.instreamId,
53301              kind: 'captions',
53302              "default": properties["default"] && properties.autoselect,
53303              language: properties.language,
53304              label: variantLabel
53305            }, false).track;
53306            tracks[variantLabel] = track;
53307          }
53308        }
53309      }
53310    }
53311  };
53312  /**
53313   * Returns a function used to get the active group of the provided type
53314   *
53315   * @param {String} type
53316   *        MediaGroup type
53317   * @param {Object} settings
53318   *        Object containing requi
vendor: 37,520 bytes, lines 53318-54313
53318red information for media groups
53319   * @return {Function}
53320   *         Function that returns the active media group for the provided type. Takes an
53321   *         optional parameter {TextTrack} track. If no track is provided, a list of all
53322   *         variants in the group, otherwise the variant corresponding to the provided
53323   *         track is returned.
53324   * @function activeGroup
53325   */
53326
53327  var activeGroup = function activeGroup(type, settings) {
53328    return function (track) {
53329      var masterPlaylistLoader = settings.masterPlaylistLoader,
53330          groups = settings.mediaTypes[type].groups;
53331      var media = masterPlaylistLoader.media();
53332
53333      if (!media) {
53334        return null;
53335      }
53336
53337      var variants = null;
53338
53339      if (media.attributes[type]) {
53340        variants = groups[media.attributes[type]];
53341      }
53342
53343      variants = variants || groups.main;
53344
53345      if (typeof track === 'undefined') {
53346        return variants;
53347      }
53348
53349      if (track === null) {
53350        // An active track was specified so a corresponding group is expected. track === null
53351        // means no track is currently active so there is no corresponding group
53352        return null;
53353      }
53354
53355      return variants.filter(function (props) {
53356        return props.id === track.id;
53357      })[0] || null;
53358    };
53359  };
53360
53361  var activeTrack = {
53362    /**
53363     * Returns a function used to get the active track of type provided
53364     *
53365     * @param {String} type
53366     *        MediaGroup type
53367     * @param {Object} settings
53368     *        Object containing required information for media groups
53369     * @return {Function}
53370     *         Function that returns the active media track for the provided type. Returns
53371     *         null if no track is active
53372     * @function activeTrack.AUDIO
53373     */
53374    AUDIO: function AUDIO(type, settings) {
53375      return function () {
53376        var tracks = settings.mediaTypes[type].tracks;
53377
53378        for (var id in tracks) {
53379          if (tracks[id].enabled) {
53380            return tracks[id];
53381          }
53382        }
53383
53384        return null;
53385      };
53386    },
53387
53388    /**
53389     * Returns a function used to get the active track of type provided
53390     *
53391     * @param {String} type
53392     *        MediaGroup type
53393     * @param {Object} settings
53394     *        Object containing required information for media groups
53395     * @return {Function}
53396     *         Function that returns the active media track for the provided type. Returns
53397     *         null if no track is active
53398     * @function activeTrack.SUBTITLES
53399     */
53400    SUBTITLES: function SUBTITLES(type, settings) {
53401      return function () {
53402        var tracks = settings.mediaTypes[type].tracks;
53403
53404        for (var id in tracks) {
53405          if (tracks[id].mode === 'showing') {
53406            return tracks[id];
53407          }
53408        }
53409
53410        return null;
53411      };
53412    }
53413  };
53414  /**
53415   * Setup PlaylistLoaders and Tracks for media groups (Audio, Subtitles,
53416   * Closed-Captions) specified in the master manifest.
53417   *
53418   * @param {Object} settings
53419   *        Object containing required information for setting up the media groups
53420   * @param {SegmentLoader} settings.segmentLoaders.AUDIO
53421   *        Audio segment loader
53422   * @param {SegmentLoader} settings.segmentLoaders.SUBTITLES
53423   *        Subtitle segment loader
53424   * @param {SegmentLoader} settings.segmentLoaders.main
53425   *        Main segment loader
53426   * @param {Tech} settings.tech
53427   *        The tech of the player
53428   * @param {Object} settings.requestOptions
53429   *        XHR request options used by the segment loaders
53430   * @param {PlaylistLoader} settings.masterPlaylistLoader
53431   *        PlaylistLoader for the master source
53432   * @param {HlsHandler} settings.hls
53433   *        HLS SourceHandler
53434   * @param {Object} settings.master
53435   *        The parsed master manifest
53436   * @param {Object} settings.mediaTypes
53437   *        Object to store the loaders, tracks, and utility methods for each media type
53438   * @param {Function} settings.blacklistCurrentPlaylist
53439   *        Blacklists the current rendition and forces a rendition switch.
53440   * @function setupMediaGroups
53441   */
53442
53443  var setupMediaGroups = function setupMediaGroups(settings) {
53444    ['AUDIO', 'SUBTITLES', 'CLOSED-CAPTIONS'].forEach(function (type) {
53445      initialize[type](type, settings);
53446    });
53447    var mediaTypes = settings.mediaTypes,
53448        masterPlaylistLoader = settings.masterPlaylistLoader,
53449        tech = settings.tech,
53450        hls = settings.hls; // setup active group and track getters and change event handlers
53451
53452    ['AUDIO', 'SUBTITLES'].forEach(function (type) {
53453      mediaTypes[type].activeGroup = activeGroup(type, settings);
53454      mediaTypes[type].activeTrack = activeTrack[type](type, settings);
53455      mediaTypes[type].onGroupChanged = onGroupChanged(type, settings);
53456      mediaTypes[type].onTrackChanged = onTrackChanged(type, settings);
53457    }); // DO NOT enable the default subtitle or caption track.
53458    // DO enable the default audio track
53459
53460    var audioGroup = mediaTypes.AUDIO.activeGroup();
53461    var groupId = (audioGroup.filter(function (group) {
53462      return group["default"];
53463    })[0] || audioGroup[0]).id;
53464    mediaTypes.AUDIO.tracks[groupId].enabled = true;
53465    mediaTypes.AUDIO.onTrackChanged();
53466    masterPlaylistLoader.on('mediachange', function () {
53467      ['AUDIO', 'SUBTITLES'].forEach(function (type) {
53468        return mediaTypes[type].onGroupChanged();
53469      });
53470    }); // custom audio track change event handler for usage event
53471
53472    var onAudioTrackChanged = function onAudioTrackChanged() {
53473      mediaTypes.AUDIO.onTrackChanged();
53474      tech.trigger({
53475        type: 'usage',
53476        name: 'hls-audio-change'
53477      });
53478    };
53479
53480    tech.audioTracks().addEventListener('change', onAudioTrackChanged);
53481    tech.remoteTextTracks().addEventListener('change', mediaTypes.SUBTITLES.onTrackChanged);
53482    hls.on('dispose', function () {
53483      tech.audioTracks().removeEventListener('change', onAudioTrackChanged);
53484      tech.remoteTextTracks().removeEventListener('change', mediaTypes.SUBTITLES.onTrackChanged);
53485    }); // clear existing audio tracks and add the ones we just created
53486
53487    tech.clearTracks('audio');
53488
53489    for (var id in mediaTypes.AUDIO.tracks) {
53490      tech.audioTracks().addTrack(mediaTypes.AUDIO.tracks[id]);
53491    }
53492  };
53493  /**
53494   * Creates skeleton object used to store the loaders, tracks, and utility methods for each
53495   * media type
53496   *
53497   * @return {Object}
53498   *         Object to store the loaders, tracks, and utility methods for each media type
53499   * @function createMediaTypes
53500   */
53501
53502
53503  var createMediaTypes = function createMediaTypes() {
53504    var mediaTypes = {};
53505    ['AUDIO', 'SUBTITLES', 'CLOSED-CAPTIONS'].forEach(function (type) {
53506      mediaTypes[type] = {
53507        groups: {},
53508        tracks: {},
53509        activePlaylistLoader: null,
53510        activeGroup: noop$1,
53511        activeTrack: noop$1,
53512        onGroupChanged: noop$1,
53513        onTrackChanged: noop$1
53514      };
53515    });
53516    return mediaTypes;
53517  };
53518  /**
53519   * @file master-playlist-controller.js
53520   */
53521
53522
53523  var ABORT_EARLY_BLACKLIST_SECONDS = 60 * 2;
53524  var Hls = void 0; // SegmentLoader stats that need to have each loader's
53525  // values summed to calculate the final value
53526
53527  var loaderStats = ['mediaRequests', 'mediaRequestsAborted', 'mediaRequestsTimedout', 'mediaRequestsErrored', 'mediaTransferDuration', 'mediaBytesTransferred'];
53528
53529  var sumLoaderStat = function sumLoaderStat(stat) {
53530    return this.audioSegmentLoader_[stat] + this.mainSegmentLoader_[stat];
53531  };
53532  /**
53533   * the master playlist controller controller all interactons
53534   * between playlists and segmentloaders. At this time this mainly
53535   * involves a master playlist and a series of audio playlists
53536   * if they are available
53537   *
53538   * @class MasterPlaylistController
53539   * @extends videojs.EventTarget
53540   */
53541
53542
53543  var MasterPlaylistController = function (_videojs$EventTarget) {
53544    inherits$1(MasterPlaylistController, _videojs$EventTarget);
53545
53546    function MasterPlaylistController(options) {
53547      classCallCheck$1(this, MasterPlaylistController);
53548
53549      var _this = possibleConstructorReturn$1(this, (MasterPlaylistController.__proto__ || Object.getPrototypeOf(MasterPlaylistController)).call(this));
53550
53551      var url = options.url,
53552          handleManifestRedirects = options.handleManifestRedirects,
53553          withCredentials = options.withCredentials,
53554          tech = options.tech,
53555          bandwidth = options.bandwidth,
53556          externHls = options.externHls,
53557          useCueTags = options.useCueTags,
53558          blacklistDuration = options.blacklistDuration,
53559          enableLowInitialPlaylist = options.enableLowInitialPlaylist,
53560          sourceType = options.sourceType,
53561          seekTo = options.seekTo,
53562          cacheEncryptionKeys = options.cacheEncryptionKeys;
53563
53564      if (!url) {
53565        throw new Error('A non-empty playlist URL is required');
53566      }
53567
53568      Hls = externHls;
53569      _this.withCredentials = withCredentials;
53570      _this.tech_ = tech;
53571      _this.hls_ = tech.hls;
53572      _this.seekTo_ = seekTo;
53573      _this.sourceType_ = sourceType;
53574      _this.useCueTags_ = useCueTags;
53575      _this.blacklistDuration = blacklistDuration;
53576      _this.enableLowInitialPlaylist = enableLowInitialPlaylist;
53577
53578      if (_this.useCueTags_) {
53579        _this.cueTagsTrack_ = _this.tech_.addTextTrack('metadata', 'ad-cues');
53580        _this.cueTagsTrack_.inBandMetadataTrackDispatchType = '';
53581      }
53582
53583      _this.requestOptions_ = {
53584        withCredentials: withCredentials,
53585        handleManifestRedirects: handleManifestRedirects,
53586        timeout: null
53587      };
53588      _this.mediaTypes_ = createMediaTypes();
53589      _this.mediaSource = new videojs$1.MediaSource(); // load the media source into the player
53590
53591      _this.mediaSource.addEventListener('sourceopen', _this.handleSourceOpen_.bind(_this));
53592
53593      _this.seekable_ = videojs$1.createTimeRanges();
53594
53595      _this.hasPlayed_ = function () {
53596        return false;
53597      };
53598
53599      _this.syncController_ = new SyncController(options);
53600      _this.segmentMetadataTrack_ = tech.addRemoteTextTrack({
53601        kind: 'metadata',
53602        label: 'segment-metadata'
53603      }, false).track;
53604      _this.decrypter_ = new Decrypter$1();
53605      _this.inbandTextTracks_ = {};
53606      var segmentLoaderSettings = {
53607        hls: _this.hls_,
53608        mediaSource: _this.mediaSource,
53609        currentTime: _this.tech_.currentTime.bind(_this.tech_),
53610        seekable: function seekable$$1() {
53611          return _this.seekable();
53612        },
53613        seeking: function seeking() {
53614          return _this.tech_.seeking();
53615        },
53616        duration: function duration$$1() {
53617          return _this.mediaSource.duration;
53618        },
53619        hasPlayed: function hasPlayed() {
53620          return _this.hasPlayed_();
53621        },
53622        goalBufferLength: function goalBufferLength() {
53623          return _this.goalBufferLength();
53624        },
53625        bandwidth: bandwidth,
53626        syncController: _this.syncController_,
53627        decrypter: _this.decrypter_,
53628        sourceType: _this.sourceType_,
53629        inbandTextTracks: _this.inbandTextTracks_,
53630        cacheEncryptionKeys: cacheEncryptionKeys
53631      };
53632      _this.masterPlaylistLoader_ = _this.sourceType_ === 'dash' ? new DashPlaylistLoader(url, _this.hls_, _this.requestOptions_) : new PlaylistLoader(url, _this.hls_, _this.requestOptions_);
53633
53634      _this.setupMasterPlaylistLoaderListeners_(); // setup segment loaders
53635      // combined audio/video or just video when alternate audio track is selected
53636
53637
53638      _this.mainSegmentLoader_ = new SegmentLoader(videojs$1.mergeOptions(segmentLoaderSettings, {
53639        segmentMetadataTrack: _this.segmentMetadataTrack_,
53640        loaderType: 'main'
53641      }), options); // alternate audio track
53642
53643      _this.audioSegmentLoader_ = new SegmentLoader(videojs$1.mergeOptions(segmentLoaderSettings, {
53644        loaderType: 'audio'
53645      }), options);
53646      _this.subtitleSegmentLoader_ = new VTTSegmentLoader(videojs$1.mergeOptions(segmentLoaderSettings, {
53647        loaderType: 'vtt'
53648      }), options);
53649
53650      _this.setupSegmentLoaderListeners_(); // Create SegmentLoader stat-getters
53651
53652
53653      loaderStats.forEach(function (stat) {
53654        _this[stat + '_'] = sumLoaderStat.bind(_this, stat);
53655      });
53656      _this.logger_ = logger('MPC');
53657
53658      _this.masterPlaylistLoader_.load();
53659
53660      return _this;
53661    }
53662    /**
53663     * Register event handlers on the master playlist loader. A helper
53664     * function for construction time.
53665     *
53666     * @private
53667     */
53668
53669
53670    createClass$1(MasterPlaylistController, [{
53671      key: 'setupMasterPlaylistLoaderListeners_',
53672      value: function setupMasterPlaylistLoaderListeners_() {
53673        var _this2 = this;
53674
53675        this.masterPlaylistLoader_.on('loadedmetadata', function () {
53676          var media = _this2.masterPlaylistLoader_.media();
53677
53678          var requestTimeout = media.targetDuration * 1.5 * 1000; // If we don't have any more available playlists, we don't want to
53679          // timeout the request.
53680
53681          if (isLowestEnabledRendition(_this2.masterPlaylistLoader_.master, _this2.masterPlaylistLoader_.media())) {
53682            _this2.requestOptions_.timeout = 0;
53683          } else {
53684            _this2.requestOptions_.timeout = requestTimeout;
53685          } // if this isn't a live video and preload permits, start
53686          // downloading segments
53687
53688
53689          if (media.endList && _this2.tech_.preload() !== 'none') {
53690            _this2.mainSegmentLoader_.playlist(media, _this2.requestOptions_);
53691
53692            _this2.mainSegmentLoader_.load();
53693          }
53694
53695          setupMediaGroups({
53696            sourceType: _this2.sourceType_,
53697            segmentLoaders: {
53698              AUDIO: _this2.audioSegmentLoader_,
53699              SUBTITLES: _this2.subtitleSegmentLoader_,
53700              main: _this2.mainSegmentLoader_
53701            },
53702            tech: _this2.tech_,
53703            requestOptions: _this2.requestOptions_,
53704            masterPlaylistLoader: _this2.masterPlaylistLoader_,
53705            hls: _this2.hls_,
53706            master: _this2.master(),
53707            mediaTypes: _this2.mediaTypes_,
53708            blacklistCurrentPlaylist: _this2.blacklistCurrentPlaylist.bind(_this2)
53709          });
53710
53711          _this2.triggerPresenceUsage_(_this2.master(), media);
53712
53713          try {
53714            _this2.setupSourceBuffers_();
53715          } catch (e) {
53716            videojs$1.log.warn('Failed to create SourceBuffers', e);
53717            return _this2.mediaSource.endOfStream('decode');
53718          }
53719
53720          _this2.setupFirstPlay();
53721
53722          if (!_this2.mediaTypes_.AUDIO.activePlaylistLoader || _this2.mediaTypes_.AUDIO.activePlaylistLoader.media()) {
53723            _this2.trigger('selectedinitialmedia');
53724          } else {
53725            // We must wait for the active audio playlist loader to
53726            // finish setting up before triggering this event so the
53727            // representations API and EME setup is correct
53728            _this2.mediaTypes_.AUDIO.activePlaylistLoader.one('loadedmetadata', function () {
53729              _this2.trigger('selectedinitialmedia');
53730            });
53731          }
53732        });
53733        this.masterPlaylistLoader_.on('loadedplaylist', function () {
53734          var updatedPlaylist = _this2.masterPlaylistLoader_.media();
53735
53736          if (!updatedPlaylist) {
53737            // blacklist any variants that are not supported by the browser before selecting
53738            // an initial media as the playlist selectors do not consider browser support
53739            _this2.excludeUnsupportedVariants_();
53740
53741            var selectedMedia = void 0;
53742
53743            if (_this2.enableLowInitialPlaylist) {
53744              selectedMedia = _this2.selectInitialPlaylist();
53745            }
53746
53747            if (!selectedMedia) {
53748              selectedMedia = _this2.selectPlaylist();
53749            }
53750
53751            _this2.initialMedia_ = selectedMedia;
53752
53753            _this2.masterPlaylistLoader_.media(_this2.initialMedia_);
53754
53755            return;
53756          }
53757
53758          if (_this2.useCueTags_) {
53759            _this2.updateAdCues_(updatedPlaylist);
53760          } // TODO: Create a new event on the PlaylistLoader that signals
53761          // that the segments have changed in some way and use that to
53762          // update the SegmentLoader instead of doing it twice here and
53763          // on `mediachange`
53764
53765
53766          _this2.mainSegmentLoader_.playlist(updatedPlaylist, _this2.requestOptions_);
53767
53768          _this2.updateDuration(); // If the player isn't paused, ensure that the segment loader is running,
53769          // as it is possible that it was temporarily stopped while waiting for
53770          // a playlist (e.g., in case the playlist errored and we re-requested it).
53771
53772
53773          if (!_this2.tech_.paused()) {
53774            _this2.mainSegmentLoader_.load();
53775
53776            if (_this2.audioSegmentLoader_) {
53777              _this2.audioSegmentLoader_.load();
53778            }
53779          }
53780
53781          if (!updatedPlaylist.endList) {
53782            var addSeekableRange = function addSeekableRange() {
53783              var seekable$$1 = _this2.seekable();
53784
53785              if (seekable$$1.length !== 0) {
53786                _this2.mediaSource.addSeekableRange_(seekable$$1.start(0), seekable$$1.end(0));
53787              }
53788            };
53789
53790            if (_this2.duration() !== Infinity) {
53791              var onDurationchange = function onDurationchange() {
53792                if (_this2.duration() === Infinity) {
53793                  addSeekableRange();
53794                } else {
53795                  _this2.tech_.one('durationchange', onDurationchange);
53796                }
53797              };
53798
53799              _this2.tech_.one('durationchange', onDurationchange);
53800            } else {
53801              addSeekableRange();
53802            }
53803          }
53804        });
53805        this.masterPlaylistLoader_.on('error', function () {
53806          _this2.blacklistCurrentPlaylist(_this2.masterPlaylistLoader_.error);
53807        });
53808        this.masterPlaylistLoader_.on('mediachanging', function () {
53809          _this2.mainSegmentLoader_.abort();
53810
53811          _this2.mainSegmentLoader_.pause();
53812        });
53813        this.masterPlaylistLoader_.on('mediachange', function () {
53814          var media = _this2.masterPlaylistLoader_.media();
53815
53816          var requestTimeout = media.targetDuration * 1.5 * 1000; // If we don't have any more available playlists, we don't want to
53817          // timeout the request.
53818
53819          if (isLowestEnabledRendition(_this2.masterPlaylistLoader_.master, _this2.masterPlaylistLoader_.media())) {
53820            _this2.requestOptions_.timeout = 0;
53821          } else {
53822            _this2.requestOptions_.timeout = requestTimeout;
53823          } // TODO: Create a new event on the PlaylistLoader that signals
53824          // that the segments have changed in some way and use that to
53825          // update the SegmentLoader instead of doing it twice here and
53826          // on `loadedplaylist`
53827
53828
53829          _this2.mainSegmentLoader_.playlist(media, _this2.requestOptions_);
53830
53831          _this2.mainSegmentLoader_.load();
53832
53833          _this2.tech_.trigger({
53834            type: 'mediachange',
53835            bubbles: true
53836          });
53837        });
53838        this.masterPlaylistLoader_.on('playlistunchanged', function () {
53839          var updatedPlaylist = _this2.masterPlaylistLoader_.media();
53840
53841          var playlistOutdated = _this2.stuckAtPlaylistEnd_(updatedPlaylist);
53842
53843          if (playlistOutdated) {
53844            // Playlist has stopped updating and we're stuck at its end. Try to
53845            // blacklist it and switch to another playlist in the hope that that
53846            // one is updating (and give the player a chance to re-adjust to the
53847            // safe live point).
53848            _this2.blacklistCurrentPlaylist({
53849              message: 'Playlist no longer updating.'
53850            }); // useful for monitoring QoS
53851
53852
53853            _this2.tech_.trigger('playliststuck');
53854          }
53855        });
53856        this.masterPlaylistLoader_.on('renditiondisabled', function () {
53857          _this2.tech_.trigger({
53858            type: 'usage',
53859            name: 'hls-rendition-disabled'
53860          });
53861        });
53862        this.masterPlaylistLoader_.on('renditionenabled', function () {
53863          _this2.tech_.trigger({
53864            type: 'usage',
53865            name: 'hls-rendition-enabled'
53866          });
53867        });
53868      }
53869      /**
53870       * A helper function for triggerring presence usage events once per source
53871       *
53872       * @private
53873       */
53874
53875    }, {
53876      key: 'triggerPresenceUsage_',
53877      value: function triggerPresenceUsage_(master, media) {
53878        var mediaGroups = master.mediaGroups || {};
53879        var defaultDemuxed = true;
53880        var audioGroupKeys = Object.keys(mediaGroups.AUDIO);
53881
53882        for (var mediaGroup in mediaGroups.AUDIO) {
53883          for (var label in mediaGroups.AUDIO[mediaGroup]) {
53884            var properties = mediaGroups.AUDIO[mediaGroup][label];
53885
53886            if (!properties.uri) {
53887              defaultDemuxed = false;
53888            }
53889          }
53890        }
53891
53892        if (defaultDemuxed) {
53893          this.tech_.trigger({
53894            type: 'usage',
53895            name: 'hls-demuxed'
53896          });
53897        }
53898
53899        if (Object.keys(mediaGroups.SUBTITLES).length) {
53900          this.tech_.trigger({
53901            type: 'usage',
53902            name: 'hls-webvtt'
53903          });
53904        }
53905
53906        if (Hls.Playlist.isAes(media)) {
53907          this.tech_.trigger({
53908            type: 'usage',
53909            name: 'hls-aes'
53910          });
53911        }
53912
53913        if (Hls.Playlist.isFmp4(media)) {
53914          this.tech_.trigger({
53915            type: 'usage',
53916            name: 'hls-fmp4'
53917          });
53918        }
53919
53920        if (audioGroupKeys.length && Object.keys(mediaGroups.AUDIO[audioGroupKeys[0]]).length > 1) {
53921          this.tech_.trigger({
53922            type: 'usage',
53923            name: 'hls-alternate-audio'
53924          });
53925        }
53926
53927        if (this.useCueTags_) {
53928          this.tech_.trigger({
53929            type: 'usage',
53930            name: 'hls-playlist-cue-tags'
53931          });
53932        }
53933      }
53934      /**
53935       * Register event handlers on the segment loaders. A helper function
53936       * for construction time.
53937       *
53938       * @private
53939       */
53940
53941    }, {
53942      key: 'setupSegmentLoaderListeners_',
53943      value: function setupSegmentLoaderListeners_() {
53944        var _this3 = this;
53945
53946        this.mainSegmentLoader_.on('bandwidthupdate', function () {
53947          var nextPlaylist = _this3.selectPlaylist();
53948
53949          var currentPlaylist = _this3.masterPlaylistLoader_.media();
53950
53951          var buffered = _this3.tech_.buffered();
53952
53953          var forwardBuffer = buffered.length ? buffered.end(buffered.length - 1) - _this3.tech_.currentTime() : 0;
53954
53955          var bufferLowWaterLine = _this3.bufferLowWaterLine(); // If the playlist is live, then we want to not take low water line into account.
53956          // This is because in LIVE, the player plays 3 segments from the end of the
53957          // playlist, and if `BUFFER_LOW_WATER_LINE` is greater than the duration availble
53958          // in those segments, a viewer will never experience a rendition upswitch.
53959
53960
53961          if (!currentPlaylist.endList || // For the same reason as LIVE, we ignore the low water line when the VOD
53962          // duration is below the max potential low water line
53963          _this3.duration() < Config.MAX_BUFFER_LOW_WATER_LINE || // we want to switch down to lower resolutions quickly to continue playback, but
53964          nextPlaylist.attributes.BANDWIDTH < currentPlaylist.attributes.BANDWIDTH || // ensure we have some buffer before we switch up to prevent us running out of
53965          // buffer while loading a higher rendition.
53966          forwardBuffer >= bufferLowWaterLine) {
53967            _this3.masterPlaylistLoader_.media(nextPlaylist);
53968          }
53969
53970          _this3.tech_.trigger('bandwidthupdate');
53971        });
53972        this.mainSegmentLoader_.on('progress', function () {
53973          _this3.trigger('progress');
53974        });
53975        this.mainSegmentLoader_.on('error', function () {
53976          _this3.blacklistCurrentPlaylist(_this3.mainSegmentLoader_.error());
53977        });
53978        this.mainSegmentLoader_.on('syncinfoupdate', function () {
53979          _this3.onSyncInfoUpdate_();
53980        });
53981        this.mainSegmentLoader_.on('timestampoffset', function () {
53982          _this3.tech_.trigger({
53983            type: 'usage',
53984            name: 'hls-timestamp-offset'
53985          });
53986        });
53987        this.audioSegmentLoader_.on('syncinfoupdate', function () {
53988          _this3.onSyncInfoUpdate_();
53989        });
53990        this.mainSegmentLoader_.on('ended', function () {
53991          _this3.onEndOfStream();
53992        });
53993        this.mainSegmentLoader_.on('earlyabort', function () {
53994          _this3.blacklistCurrentPlaylist({
53995            message: 'Aborted early because there isn\'t enough bandwidth to complete the ' + 'request without rebuffering.'
53996          }, ABORT_EARLY_BLACKLIST_SECONDS);
53997        });
53998        this.mainSegmentLoader_.on('reseteverything', function () {
53999          // If playing an MTS stream, a videojs.MediaSource is listening for
54000          // hls-reset to reset caption parsing state in the transmuxer
54001          _this3.tech_.trigger('hls-reset');
54002        });
54003        this.mainSegmentLoader_.on('segmenttimemapping', function (event) {
54004          // If playing an MTS stream in html, a videojs.MediaSource is listening for
54005          // hls-segment-time-mapping update its internal mapping of stream to display time
54006          _this3.tech_.trigger({
54007            type: 'hls-segment-time-mapping',
54008            mapping: event.mapping
54009          });
54010        });
54011        this.audioSegmentLoader_.on('ended', function () {
54012          _this3.onEndOfStream();
54013        });
54014      }
54015    }, {
54016      key: 'mediaSecondsLoaded_',
54017      value: function mediaSecondsLoaded_() {
54018        return Math.max(this.audioSegmentLoader_.mediaSecondsLoaded + this.mainSegmentLoader_.mediaSecondsLoaded);
54019      }
54020      /**
54021       * Call load on our SegmentLoaders
54022       */
54023
54024    }, {
54025      key: 'load',
54026      value: function load() {
54027        this.mainSegmentLoader_.load();
54028
54029        if (this.mediaTypes_.AUDIO.activePlaylistLoader) {
54030          this.audioSegmentLoader_.load();
54031        }
54032
54033        if (this.mediaTypes_.SUBTITLES.activePlaylistLoader) {
54034          this.subtitleSegmentLoader_.load();
54035        }
54036      }
54037      /**
54038       * Re-tune playback quality level for the current player
54039       * conditions without performing destructive actions, like
54040       * removing already buffered content
54041       *
54042       * @private
54043       */
54044
54045    }, {
54046      key: 'smoothQualityChange_',
54047      value: function smoothQualityChange_() {
54048        var media = this.selectPlaylist();
54049
54050        if (media !== this.masterPlaylistLoader_.media()) {
54051          this.masterPlaylistLoader_.media(media);
54052          this.mainSegmentLoader_.resetLoader(); // don't need to reset audio as it is reset when media changes
54053        }
54054      }
54055      /**
54056       * Re-tune playback quality level for the current player
54057       * conditions. This method will perform destructive actions like removing
54058       * already buffered content in order to readjust the currently active
54059       * playlist quickly. This is good for manual quality changes
54060       *
54061       * @private
54062       */
54063
54064    }, {
54065      key: 'fastQualityChange_',
54066      value: function fastQualityChange_() {
54067        var _this4 = this;
54068
54069        var media = this.selectPlaylist();
54070
54071        if (media === this.masterPlaylistLoader_.media()) {
54072          return;
54073        }
54074
54075        this.masterPlaylistLoader_.media(media); // Delete all buffered data to allow an immediate quality switch, then seek to give
54076        // the browser a kick to remove any cached frames from the previous rendtion (.04 seconds
54077        // ahead is roughly the minimum that will accomplish this across a variety of content
54078        // in IE and Edge, but seeking in place is sufficient on all other browsers)
54079        // Edge/IE bug: https://developer.microsoft.com/en-us/microsoft-edge/platform/issues/14600375/
54080        // Chrome bug: https://bugs.chromium.org/p/chromium/issues/detail?id=651904
54081
54082        this.mainSegmentLoader_.resetEverything(function () {
54083          // Since this is not a typical seek, we avoid the seekTo method which can cause segments
54084          // from the previously enabled rendition to load before the new playlist has finished loading
54085          if (videojs$1.browser.IE_VERSION || videojs$1.browser.IS_EDGE) {
54086            _this4.tech_.setCurrentTime(_this4.tech_.currentTime() + 0.04);
54087          } else {
54088            _this4.tech_.setCurrentTime(_this4.tech_.currentTime());
54089          }
54090        }); // don't need to reset audio as it is reset when media changes
54091      }
54092      /**
54093       * Begin playback.
54094       */
54095
54096    }, {
54097      key: 'play',
54098      value: function play() {
54099        if (this.setupFirstPlay()) {
54100          return;
54101        }
54102
54103        if (this.tech_.ended()) {
54104          this.seekTo_(0);
54105        }
54106
54107        if (this.hasPlayed_()) {
54108          this.load();
54109        }
54110
54111        var seekable$$1 = this.tech_.seekable(); // if the viewer has paused and we fell out of the live window,
54112        // seek forward to the live point
54113
54114        if (this.tech_.duration() === Infinity) {
54115          if (this.tech_.currentTime() < seekable$$1.start(0)) {
54116            return this.seekTo_(seekable$$1.end(seekable$$1.length - 1));
54117          }
54118        }
54119      }
54120      /**
54121       * Seek to the latest media position if this is a live video and the
54122       * player and video are loaded and initialized.
54123       */
54124
54125    }, {
54126      key: 'setupFirstPlay',
54127      value: function setupFirstPlay() {
54128        var _this5 = this;
54129
54130        var media = this.masterPlaylistLoader_.media(); // Check that everything is ready to begin buffering for the first call to play
54131        //  If 1) there is no active media
54132        //     2) the player is paused
54133        //     3) the first play has already been setup
54134        // then exit early
54135
54136        if (!media || this.tech_.paused() || this.hasPlayed_()) {
54137          return false;
54138        } // when the video is a live stream
54139
54140
54141        if (!media.endList) {
54142          var seekable$$1 = this.seekable();
54143
54144          if (!seekable$$1.length) {
54145            // without a seekable range, the player cannot seek to begin buffering at the live
54146            // point
54147            return false;
54148          }
54149
54150          if (videojs$1.browser.IE_VERSION && this.tech_.readyState() === 0) {
54151            // IE11 throws an InvalidStateError if you try to set currentTime while the
54152            // readyState is 0, so it must be delayed until the tech fires loadedmetadata.
54153            this.tech_.one('loadedmetadata', function () {
54154              _this5.trigger('firstplay');
54155
54156              _this5.seekTo_(seekable$$1.end(0));
54157
54158              _this5.hasPlayed_ = function () {
54159                return true;
54160              };
54161            });
54162            return false;
54163          } // trigger firstplay to inform the source handler to ignore the next seek event
54164
54165
54166          this.trigger('firstplay'); // seek to the live point
54167
54168          this.seekTo_(seekable$$1.end(0));
54169        }
54170
54171        this.hasPlayed_ = function () {
54172          return true;
54173        }; // we can begin loading now that everything is ready
54174
54175
54176        this.load();
54177        return true;
54178      }
54179      /**
54180       * handle the sourceopen event on the MediaSource
54181       *
54182       * @private
54183       */
54184
54185    }, {
54186      key: 'handleSourceOpen_',
54187      value: function handleSourceOpen_() {
54188        // Only attempt to create the source buffer if none already exist.
54189        // handleSourceOpen is also called when we are "re-opening" a source buffer
54190        // after `endOfStream` has been called (in response to a seek for instance)
54191        try {
54192          this.setupSourceBuffers_();
54193        } catch (e) {
54194          videojs$1.log.warn('Failed to create Source Buffers', e);
54195          return this.mediaSource.endOfStream('decode');
54196        } // if autoplay is enabled, begin playback. This is duplicative of
54197        // code in video.js but is required because play() must be invoked
54198        // *after* the media source has opened.
54199
54200
54201        if (this.tech_.autoplay()) {
54202          var playPromise = this.tech_.play(); // Catch/silence error when a pause interrupts a play request
54203          // on browsers which return a promise
54204
54205          if (typeof playPromise !== 'undefined' && typeof playPromise.then === 'function') {
54206            playPromise.then(null, function (e) {});
54207          }
54208        }
54209
54210        this.trigger('sourceopen');
54211      }
54212      /**
54213       * Calls endOfStream on the media source when all active stream types have called
54214       * endOfStream
54215       *
54216       * @param {string} streamType
54217       *        Stream type of the segment loader that called endOfStream
54218       * @private
54219       */
54220
54221    }, {
54222      key: 'onEndOfStream',
54223      value: function onEndOfStream() {
54224        var isEndOfStream = this.mainSegmentLoader_.ended_;
54225
54226        if (this.mediaTypes_.AUDIO.activePlaylistLoader) {
54227          // if the audio playlist loader exists, then alternate audio is active
54228          if (!this.mainSegmentLoader_.startingMedia_ || this.mainSegmentLoader_.startingMedia_.containsVideo) {
54229            // if we do not know if the main segment loader contains video yet or if we
54230            // definitively know the main segment loader contains video, then we need to wait
54231            // for both main and audio segment loaders to call endOfStream
54232            isEndOfStream = isEndOfStream && this.audioSegmentLoader_.ended_;
54233          } else {
54234            // otherwise just rely on the audio loader
54235            isEndOfStream = this.audioSegmentLoader_.ended_;
54236          }
54237        }
54238
54239        if (!isEndOfStream) {
54240          return;
54241        }
54242
54243        this.logger_('calling mediaSource.endOfStream()'); // on chrome calling endOfStream can sometimes cause an exception,
54244        // even when the media source is in a valid state.
54245
54246        try {
54247          this.mediaSource.endOfStream();
54248        } catch (e) {
54249          videojs$1.log.warn('Failed to call media source endOfStream', e);
54250        }
54251      }
54252      /**
54253       * Check if a playlist has stopped being updated
54254       * @param {Object} playlist the media playlist object
54255       * @return {boolean} whether the playlist has stopped being updated or not
54256       */
54257
54258    }, {
54259      key: 'stuckAtPlaylistEnd_',
54260      value: function stuckAtPlaylistEnd_(playlist) {
54261        var seekable$$1 = this.seekable();
54262
54263        if (!seekable$$1.length) {
54264          // playlist doesn't have enough information to determine whether we are stuck
54265          return false;
54266        }
54267
54268        var expired = this.syncController_.getExpiredTime(playlist, this.mediaSource.duration);
54269
54270        if (expired === null) {
54271          return false;
54272        } // does not use the safe live end to calculate playlist end, since we
54273        // don't want to say we are stuck while there is still content
54274
54275
54276        var absolutePlaylistEnd = Hls.Playlist.playlistEnd(playlist, expired);
54277        var currentTime = this.tech_.currentTime();
54278        var buffered = this.tech_.buffered();
54279
54280        if (!buffered.length) {
54281          // return true if the playhead reached the absolute end of the playlist
54282          return absolutePlaylistEnd - currentTime <= SAFE_TIME_DELTA;
54283        }
54284
54285        var bufferedEnd = buffered.end(buffered.length - 1); // return true if there is too little buffer left and buffer has reached absolute
54286        // end of playlist
54287
54288        return bufferedEnd - currentTime <= SAFE_TIME_DELTA && absolutePlaylistEnd - bufferedEnd <= SAFE_TIME_DELTA;
54289      }
54290      /**
54291       * Blacklists a playlist when an error occurs for a set amount of time
54292       * making it unavailable for selection by the rendition selection algorithm
54293       * and then forces a new playlist (rendition) selection.
54294       *
54295       * @param {Object=} error an optional error that may include the playlist
54296       * to blacklist
54297       * @param {Number=} blacklistDuration an optional number of seconds to blacklist the
54298       * playlist
54299       */
54300
54301    }, {
54302      key: 'blacklistCurrentPlaylist',
54303      value: function blacklistCurrentPlaylist() {
54304        var error = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
54305        var blacklistDuration = arguments[1];
54306        var currentPlaylist = void 0;
54307        var nextPlaylist = void 0; // If the `error` was generated by the playlist loader, it will contain
54308        // the playlist we were trying to load (but failed) and that should be
54309        // blacklisted instead of the currently selected playlist which is likely
54310        // out-of-date in this scenario
54311
54312        currentPlaylist = error.playlist || this.masterPlaylistLoader_.media();
54313        blacklistDuration = blacklistDuration || error.blacklistDuration || this.blackl
54313istDuration; // If there is no current playlist, then an error occurred while we were
54314        // trying to load the master OR while we were disposing of the tech
54315
54316        if (!currentPlaylist) {
54317          this.error = error;
54318
54319          try {
54320            return this.mediaSource.endOfStream('network');
54321          } catch (e) {
54322            return this.trigger('error');
54323          }
54324        }
54325
54326        var isFinalRendition = this.masterPlaylistLoader_.master.playlists.filter(isEnabled).length === 1;
54327        var playlists = this.masterPlaylistLoader_.master.playlists;
54328
54329        if (playlists.length === 1) {
54330          // Never blacklisting this playlist because it's the only playlist
54331          videojs$1.log.warn('Problem encountered with the current ' + 'HLS playlist. Trying again since it is the only playlist.');
54332          this.tech_.trigger('retryplaylist');
54333          return this.masterPlaylistLoader_.load(isFinalRendition);
54334        }
54335
54336        if (isFinalRendition) {
54337          // Since we're on the final non-blacklisted playlist, and we're about to blacklist
54338          // it, instead of erring the player or retrying this playlist, clear out the current
54339          // blacklist. This allows other playlists to be attempted in case any have been
54340          // fixed.
54341          videojs$1.log.warn('Removing all playlists from the blacklist because the last ' + 'rendition is about to be blacklisted.');
vendor: 11,849 bytes, lines 54342-54684
54342          playlists.forEach(function (playlist) {
54343            if (playlist.excludeUntil !== Infinity) {
54344              delete playlist.excludeUntil;
54345            }
54346          }); // Technically we are retrying a playlist, in that we are simply retrying a previous
54347          // playlist. This is needed for users relying on the retryplaylist event to catch a
54348          // case where the player might be stuck and looping through "dead" playlists.
54349
54350          this.tech_.trigger('retryplaylist');
54351        } // Blacklist this playlist
54352
54353
54354        currentPlaylist.excludeUntil = Date.now() + blacklistDuration * 1000;
54355        this.tech_.trigger('blacklistplaylist');
54356        this.tech_.trigger({
54357          type: 'usage',
54358          name: 'hls-rendition-blacklisted'
54359        }); // Select a new playlist
54360
54361        nextPlaylist = this.selectPlaylist();
54362        videojs$1.log.warn('Problem encountered with the current HLS playlist.' + (error.message ? ' ' + error.message : '') + ' Switching to another playlist.');
54363        return this.masterPlaylistLoader_.media(nextPlaylist, isFinalRendition);
54364      }
54365      /**
54366       * Pause all segment loaders
54367       */
54368
54369    }, {
54370      key: 'pauseLoading',
54371      value: function pauseLoading() {
54372        this.mainSegmentLoader_.pause();
54373
54374        if (this.mediaTypes_.AUDIO.activePlaylistLoader) {
54375          this.audioSegmentLoader_.pause();
54376        }
54377
54378        if (this.mediaTypes_.SUBTITLES.activePlaylistLoader) {
54379          this.subtitleSegmentLoader_.pause();
54380        }
54381      }
54382      /**
54383       * set the current time on all segment loaders
54384       *
54385       * @param {TimeRange} currentTime the current time to set
54386       * @return {TimeRange} the current time
54387       */
54388
54389    }, {
54390      key: 'setCurrentTime',
54391      value: function setCurrentTime(currentTime) {
54392        var buffered = findRange(this.tech_.buffered(), currentTime);
54393
54394        if (!(this.masterPlaylistLoader_ && this.masterPlaylistLoader_.media())) {
54395          // return immediately if the metadata is not ready yet
54396          return 0;
54397        } // it's clearly an edge-case but don't thrown an error if asked to
54398        // seek within an empty playlist
54399
54400
54401        if (!this.masterPlaylistLoader_.media().segments) {
54402          return 0;
54403        } // In flash playback, the segment loaders should be reset on every seek, even
54404        // in buffer seeks. If the seek location is already buffered, continue buffering as
54405        // usual
54406        // TODO: redo this comment
54407
54408
54409        if (buffered && buffered.length) {
54410          return currentTime;
54411        } // cancel outstanding requests so we begin buffering at the new
54412        // location
54413
54414
54415        this.mainSegmentLoader_.resetEverything();
54416        this.mainSegmentLoader_.abort();
54417
54418        if (this.mediaTypes_.AUDIO.activePlaylistLoader) {
54419          this.audioSegmentLoader_.resetEverything();
54420          this.audioSegmentLoader_.abort();
54421        }
54422
54423        if (this.mediaTypes_.SUBTITLES.activePlaylistLoader) {
54424          this.subtitleSegmentLoader_.resetEverything();
54425          this.subtitleSegmentLoader_.abort();
54426        } // start segment loader loading in case they are paused
54427
54428
54429        this.load();
54430      }
54431      /**
54432       * get the current duration
54433       *
54434       * @return {TimeRange} the duration
54435       */
54436
54437    }, {
54438      key: 'duration',
54439      value: function duration$$1() {
54440        if (!this.masterPlaylistLoader_) {
54441          return 0;
54442        }
54443
54444        if (this.mediaSource) {
54445          return this.mediaSource.duration;
54446        }
54447
54448        return Hls.Playlist.duration(this.masterPlaylistLoader_.media());
54449      }
54450      /**
54451       * check the seekable range
54452       *
54453       * @return {TimeRange} the seekable range
54454       */
54455
54456    }, {
54457      key: 'seekable',
54458      value: function seekable$$1() {
54459        return this.seekable_;
54460      }
54461    }, {
54462      key: 'onSyncInfoUpdate_',
54463      value: function onSyncInfoUpdate_() {
54464        var mainSeekable = void 0;
54465        var audioSeekable = void 0;
54466
54467        if (!this.masterPlaylistLoader_) {
54468          return;
54469        }
54470
54471        var media = this.masterPlaylistLoader_.media();
54472
54473        if (!media) {
54474          return;
54475        }
54476
54477        var expired = this.syncController_.getExpiredTime(media, this.mediaSource.duration);
54478
54479        if (expired === null) {
54480          // not enough information to update seekable
54481          return;
54482        }
54483
54484        mainSeekable = Hls.Playlist.seekable(media, expired);
54485
54486        if (mainSeekable.length === 0) {
54487          return;
54488        }
54489
54490        if (this.mediaTypes_.AUDIO.activePlaylistLoader) {
54491          media = this.mediaTypes_.AUDIO.activePlaylistLoader.media();
54492          expired = this.syncController_.getExpiredTime(media, this.mediaSource.duration);
54493
54494          if (expired === null) {
54495            return;
54496          }
54497
54498          audioSeekable = Hls.Playlist.seekable(media, expired);
54499
54500          if (audioSeekable.length === 0) {
54501            return;
54502          }
54503        }
54504
54505        var oldEnd = void 0;
54506        var oldStart = void 0;
54507
54508        if (this.seekable_ && this.seekable_.length) {
54509          oldEnd = this.seekable_.end(0);
54510          oldStart = this.seekable_.start(0);
54511        }
54512
54513        if (!audioSeekable) {
54514          // seekable has been calculated based on buffering video data so it
54515          // can be returned directly
54516          this.seekable_ = mainSeekable;
54517        } else if (audioSeekable.start(0) > mainSeekable.end(0) || mainSeekable.start(0) > audioSeekable.end(0)) {
54518          // seekables are pretty far off, rely on main
54519          this.seekable_ = mainSeekable;
54520        } else {
54521          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)]]);
54522        } // seekable is the same as last time
54523
54524
54525        if (this.seekable_ && this.seekable_.length) {
54526          if (this.seekable_.end(0) === oldEnd && this.seekable_.start(0) === oldStart) {
54527            return;
54528          }
54529        }
54530
54531        this.logger_('seekable updated [' + printableRange(this.seekable_) + ']');
54532        this.tech_.trigger('seekablechanged');
54533      }
54534      /**
54535       * Update the player duration
54536       */
54537
54538    }, {
54539      key: 'updateDuration',
54540      value: function updateDuration() {
54541        var _this6 = this;
54542
54543        var oldDuration = this.mediaSource.duration;
54544        var newDuration = Hls.Playlist.duration(this.masterPlaylistLoader_.media());
54545        var buffered = this.tech_.buffered();
54546
54547        var setDuration = function setDuration() {
54548          // on firefox setting the duration may sometimes cause an exception
54549          // even if the media source is open and source buffers are not
54550          // updating, something about the media source being in an invalid state.
54551          _this6.logger_('Setting duration from ' + _this6.mediaSource.duration + ' => ' + newDuration);
54552
54553          try {
54554            _this6.mediaSource.duration = newDuration;
54555          } catch (e) {
54556            videojs$1.log.warn('Failed to set media source duration', e);
54557          }
54558
54559          _this6.tech_.trigger('durationchange');
54560
54561          _this6.mediaSource.removeEventListener('sourceopen', setDuration);
54562        };
54563
54564        if (buffered.length > 0) {
54565          newDuration = Math.max(newDuration, buffered.end(buffered.length - 1));
54566        } // if the duration has changed, invalidate the cached value
54567
54568
54569        if (oldDuration !== newDuration) {
54570          // update the duration
54571          if (this.mediaSource.readyState !== 'open') {
54572            this.mediaSource.addEventListener('sourceopen', setDuration);
54573          } else {
54574            setDuration();
54575          }
54576        }
54577      }
54578      /**
54579       * dispose of the MasterPlaylistController and everything
54580       * that it controls
54581       */
54582
54583    }, {
54584      key: 'dispose',
54585      value: function dispose() {
54586        var _this7 = this;
54587
54588        this.decrypter_.terminate();
54589        this.masterPlaylistLoader_.dispose();
54590        this.mainSegmentLoader_.dispose();
54591        ['AUDIO', 'SUBTITLES'].forEach(function (type) {
54592          var groups = _this7.mediaTypes_[type].groups;
54593
54594          for (var id in groups) {
54595            groups[id].forEach(function (group) {
54596              if (group.playlistLoader) {
54597                group.playlistLoader.dispose();
54598              }
54599            });
54600          }
54601        });
54602        this.audioSegmentLoader_.dispose();
54603        this.subtitleSegmentLoader_.dispose();
54604      }
54605      /**
54606       * return the master playlist object if we have one
54607       *
54608       * @return {Object} the master playlist object that we parsed
54609       */
54610
54611    }, {
54612      key: 'master',
54613      value: function master() {
54614        return this.masterPlaylistLoader_.master;
54615      }
54616      /**
54617       * return the currently selected playlist
54618       *
54619       * @return {Object} the currently selected playlist object that we parsed
54620       */
54621
54622    }, {
54623      key: 'media',
54624      value: function media() {
54625        // playlist loader will not return media if it has not been fully loaded
54626        return this.masterPlaylistLoader_.media() || this.initialMedia_;
54627      }
54628      /**
54629       * setup our internal source buffers on our segment Loaders
54630       *
54631       * @private
54632       */
54633
54634    }, {
54635      key: 'setupSourceBuffers_',
54636      value: function setupSourceBuffers_() {
54637        var media = this.masterPlaylistLoader_.media();
54638        var mimeTypes = void 0; // wait until a media playlist is available and the Media Source is
54639        // attached
54640
54641        if (!media || this.mediaSource.readyState !== 'open') {
54642          return;
54643        }
54644
54645        mimeTypes = mimeTypesForPlaylist(this.masterPlaylistLoader_.master, media);
54646
54647        if (mimeTypes.length < 1) {
54648          this.error = 'No compatible SourceBuffer configuration for the variant stream:' + media.resolvedUri;
54649          return this.mediaSource.endOfStream('decode');
54650        }
54651
54652        this.configureLoaderMimeTypes_(mimeTypes); // exclude any incompatible variant streams from future playlist
54653        // selection
54654
54655        this.excludeIncompatibleVariants_(media);
54656      }
54657    }, {
54658      key: 'configureLoaderMimeTypes_',
54659      value: function configureLoaderMimeTypes_(mimeTypes) {
54660        // If the content is demuxed, we can't start appending segments to a source buffer
54661        // until both source buffers are set up, or else the browser may not let us add the
54662        // second source buffer (it will assume we are playing either audio only or video
54663        // only).
54664        var sourceBufferEmitter = // If there is more than one mime type
54665        mimeTypes.length > 1 && // and the first mime type does not have muxed video and audio
54666        mimeTypes[0].indexOf(',') === -1 && // and the two mime types are different (they can be the same in the case of audio
54667        // only with alternate audio)
54668        mimeTypes[0] !== mimeTypes[1] ? // then we want to wait on the second source buffer
54669        new videojs$1.EventTarget() : // otherwise there is no need to wait as the content is either audio only,
54670        // video only, or muxed content.
54671        null;
54672        this.mainSegmentLoader_.mimeType(mimeTypes[0], sourceBufferEmitter);
54673
54674        if (mimeTypes[1]) {
54675          this.audioSegmentLoader_.mimeType(mimeTypes[1], sourceBufferEmitter);
54676        }
54677      }
54678      /**
54679       * Blacklists playlists with codecs that are unsupported by the browser.
54680       */
54681
54682    }, {
54683      key: 'excludeUnsupportedVariants_',
54684      value: function excludeUnsupportedVariants_() {
54685        this.master().playlists.forEach(function (variant) {
54686          if (variant.attributes.CODECS && window$1.MediaSource && window$1.MediaSource.isTypeSupported && !window$1.MediaSource.isTypeSupported('video/mp4; codecs="' + mapLegacyAvcCodecs(variant.attributes.CODECS) + '"')) {
54687            variant.excludeUntil = Infinity;
54688          }
54689        });
54690      }
54691      /**
54692       * Blacklist playlists that are known to be codec or
54693       * stream-incompatible with the SourceBuffer configuration. For
54694       * instance, Media Source Extensions would cause the video element to
54695       * stall waiting for video data if you switched from a variant with
54696       * video and audio to an audio-only one.
54697       *
54698       * @param {Object} media a media playlist compatible with the current
54699       * set of SourceBuffers. Variants in the current master playlist that
54700       * do not appear to have compatible codec or stream configurations
54701       * will be excluded from the default playlist selection algorithm
54702       * indefinitely.
54703       * @private
54704       */
54705
54706    }, {
54707      key: 'excludeIncompatibleVariants_',
54708      value: function excludeIncompatibleVariants_(media) {
54709        var codecCount = 2;
54710        var videoCodec = null;
54711        var codecs = void 0;
54712
54713        if (media.attributes.CODECS) {
54714          codecs = parseCodecs(media.attributes.CODECS);
54715          videoCodec = codecs.videoCodec;
54716          codecCount = codecs.codecCount;
54717        }
54718
vendor: 12,940 bytes, lines 54719-55089
54719        this.master().playlists.forEach(function (variant) {
54720          var variantCodecs = {
54721            codecCount: 2,
54722            videoCodec: null
54723          };
54724
54725          if (variant.attributes.CODECS) {
54726            variantCodecs = parseCodecs(variant.attributes.CODECS);
54727          } // if the streams differ in the presence or absence of audio or
54728          // video, they are incompatible
54729
54730
54731          if (variantCodecs.codecCount !== codecCount) {
54732            variant.excludeUntil = Infinity;
54733          } // if h.264 is specified on the current playlist, some flavor of
54734          // it must be specified on all compatible variants
54735
54736
54737          if (variantCodecs.videoCodec !== videoCodec) {
54738            variant.excludeUntil = Infinity;
54739          }
54740        });
54741      }
54742    }, {
54743      key: 'updateAdCues_',
54744      value: function updateAdCues_(media) {
54745        var offset = 0;
54746        var seekable$$1 = this.seekable();
54747
54748        if (seekable$$1.length) {
54749          offset = seekable$$1.start(0);
54750        }
54751
54752        updateAdCues(media, this.cueTagsTrack_, offset);
54753      }
54754      /**
54755       * Calculates the desired forward buffer length based on current time
54756       *
54757       * @return {Number} Desired forward buffer length in seconds
54758       */
54759
54760    }, {
54761      key: 'goalBufferLength',
54762      value: function goalBufferLength() {
54763        var currentTime = this.tech_.currentTime();
54764        var initial = Config.GOAL_BUFFER_LENGTH;
54765        var rate = Config.GOAL_BUFFER_LENGTH_RATE;
54766        var max = Math.max(initial, Config.MAX_GOAL_BUFFER_LENGTH);
54767        return Math.min(initial + currentTime * rate, max);
54768      }
54769      /**
54770       * Calculates the desired buffer low water line based on current time
54771       *
54772       * @return {Number} Desired buffer low water line in seconds
54773       */
54774
54775    }, {
54776      key: 'bufferLowWaterLine',
54777      value: function bufferLowWaterLine() {
54778        var currentTime = this.tech_.currentTime();
54779        var initial = Config.BUFFER_LOW_WATER_LINE;
54780        var rate = Config.BUFFER_LOW_WATER_LINE_RATE;
54781        var max = Math.max(initial, Config.MAX_BUFFER_LOW_WATER_LINE);
54782        return Math.min(initial + currentTime * rate, max);
54783      }
54784    }]);
54785    return MasterPlaylistController;
54786  }(videojs$1.EventTarget);
54787  /**
54788   * Returns a function that acts as the Enable/disable playlist function.
54789   *
54790   * @param {PlaylistLoader} loader - The master playlist loader
54791   * @param {String} playlistUri - uri of the playlist
54792   * @param {Function} changePlaylistFn - A function to be called after a
54793   * playlist's enabled-state has been changed. Will NOT be called if a
54794   * playlist's enabled-state is unchanged
54795   * @param {Boolean=} enable - Value to set the playlist enabled-state to
54796   * or if undefined returns the current enabled-state for the playlist
54797   * @return {Function} Function for setting/getting enabled
54798   */
54799
54800
54801  var enableFunction = function enableFunction(loader, playlistUri, changePlaylistFn) {
54802    return function (enable) {
54803      var playlist = loader.master.playlists[playlistUri];
54804      var incompatible = isIncompatible(playlist);
54805      var currentlyEnabled = isEnabled(playlist);
54806
54807      if (typeof enable === 'undefined') {
54808        return currentlyEnabled;
54809      }
54810
54811      if (enable) {
54812        delete playlist.disabled;
54813      } else {
54814        playlist.disabled = true;
54815      }
54816
54817      if (enable !== currentlyEnabled && !incompatible) {
54818        // Ensure the outside world knows about our changes
54819        changePlaylistFn();
54820
54821        if (enable) {
54822          loader.trigger('renditionenabled');
54823        } else {
54824          loader.trigger('renditiondisabled');
54825        }
54826      }
54827
54828      return enable;
54829    };
54830  };
54831  /**
54832   * The representation object encapsulates the publicly visible information
54833   * in a media playlist along with a setter/getter-type function (enabled)
54834   * for changing the enabled-state of a particular playlist entry
54835   *
54836   * @class Representation
54837   */
54838
54839
54840  var Representation = function Representation(hlsHandler, playlist, id) {
54841    classCallCheck$1(this, Representation);
54842    var mpc = hlsHandler.masterPlaylistController_,
54843        smoothQualityChange = hlsHandler.options_.smoothQualityChange; // Get a reference to a bound version of the quality change function
54844
54845    var changeType = smoothQualityChange ? 'smooth' : 'fast';
54846    var qualityChangeFunction = mpc[changeType + 'QualityChange_'].bind(mpc); // some playlist attributes are optional
54847
54848    if (playlist.attributes.RESOLUTION) {
54849      var resolution = playlist.attributes.RESOLUTION;
54850      this.width = resolution.width;
54851      this.height = resolution.height;
54852    }
54853
54854    this.bandwidth = playlist.attributes.BANDWIDTH; // The id is simply the ordinality of the media playlist
54855    // within the master playlist
54856
54857    this.id = id; // Partially-apply the enableFunction to create a playlist-
54858    // specific variant
54859
54860    this.enabled = enableFunction(hlsHandler.playlists, playlist.uri, qualityChangeFunction);
54861  };
54862  /**
54863   * A mixin function that adds the `representations` api to an instance
54864   * of the HlsHandler class
54865   * @param {HlsHandler} hlsHandler - An instance of HlsHandler to add the
54866   * representation API into
54867   */
54868
54869
54870  var renditionSelectionMixin = function renditionSelectionMixin(hlsHandler) {
54871    var playlists = hlsHandler.playlists; // Add a single API-specific function to the HlsHandler instance
54872
54873    hlsHandler.representations = function () {
54874      return playlists.master.playlists.filter(function (media) {
54875        return !isIncompatible(media);
54876      }).map(function (e, i) {
54877        return new Representation(hlsHandler, e, e.uri);
54878      });
54879    };
54880  };
54881  /**
54882   * @file playback-watcher.js
54883   *
54884   * Playback starts, and now my watch begins. It shall not end until my death. I shall
54885   * take no wait, hold no uncleared timeouts, father no bad seeks. I shall wear no crowns
54886   * and win no glory. I shall live and die at my post. I am the corrector of the underflow.
54887   * I am the watcher of gaps. I am the shield that guards the realms of seekable. I pledge
54888   * my life and honor to the Playback Watch, for this Player and all the Players to come.
54889   */
54890  // Set of events that reset the playback-watcher time check logic and clear the timeout
54891
54892
54893  var timerCancelEvents = ['seeking', 'seeked', 'pause', 'playing', 'error'];
54894  /**
54895   * @class PlaybackWatcher
54896   */
54897
54898  var PlaybackWatcher = function () {
54899    /**
54900     * Represents an PlaybackWatcher object.
54901     * @constructor
54902     * @param {object} options an object that includes the tech and settings
54903     */
54904    function PlaybackWatcher(options) {
54905      var _this = this;
54906
54907      classCallCheck$1(this, PlaybackWatcher);
54908      this.tech_ = options.tech;
54909      this.seekable = options.seekable;
54910      this.seekTo = options.seekTo;
54911      this.allowSeeksWithinUnsafeLiveWindow = options.allowSeeksWithinUnsafeLiveWindow;
54912      this.media = options.media;
54913      this.consecutiveUpdates = 0;
54914      this.lastRecordedTime = null;
54915      this.timer_ = null;
54916      this.checkCurrentTimeTimeout_ = null;
54917      this.logger_ = logger('PlaybackWatcher');
54918      this.logger_('initialize');
54919
54920      var canPlayHandler = function canPlayHandler() {
54921        return _this.monitorCurrentTime_();
54922      };
54923
54924      var waitingHandler = function waitingHandler() {
54925        return _this.techWaiting_();
54926      };
54927
54928      var cancelTimerHandler = function cancelTimerHandler() {
54929        return _this.cancelTimer_();
54930      };
54931
54932      var fixesBadSeeksHandler = function fixesBadSeeksHandler() {
54933        return _this.fixesBadSeeks_();
54934      };
54935
54936      this.tech_.on('seekablechanged', fixesBadSeeksHandler);
54937      this.tech_.on('waiting', waitingHandler);
54938      this.tech_.on(timerCancelEvents, cancelTimerHandler);
54939      this.tech_.on('canplay', canPlayHandler); // Define the dispose function to clean up our events
54940
54941      this.dispose = function () {
54942        _this.logger_('dispose');
54943
54944        _this.tech_.off('seekablechanged', fixesBadSeeksHandler);
54945
54946        _this.tech_.off('waiting', waitingHandler);
54947
54948        _this.tech_.off(timerCancelEvents, cancelTimerHandler);
54949
54950        _this.tech_.off('canplay', canPlayHandler);
54951
54952        if (_this.checkCurrentTimeTimeout_) {
54953          window$1.clearTimeout(_this.checkCurrentTimeTimeout_);
54954        }
54955
54956        _this.cancelTimer_();
54957      };
54958    }
54959    /**
54960     * Periodically check current time to see if playback stopped
54961     *
54962     * @private
54963     */
54964
54965
54966    createClass$1(PlaybackWatcher, [{
54967      key: 'monitorCurrentTime_',
54968      value: function monitorCurrentTime_() {
54969        this.checkCurrentTime_();
54970
54971        if (this.checkCurrentTimeTimeout_) {
54972          window$1.clearTimeout(this.checkCurrentTimeTimeout_);
54973        } // 42 = 24 fps // 250 is what Webkit uses // FF uses 15
54974
54975
54976        this.checkCurrentTimeTimeout_ = window$1.setTimeout(this.monitorCurrentTime_.bind(this), 250);
54977      }
54978      /**
54979       * The purpose of this function is to emulate the "waiting" event on
54980       * browsers that do not emit it when they are waiting for more
54981       * data to continue playback
54982       *
54983       * @private
54984       */
54985
54986    }, {
54987      key: 'checkCurrentTime_',
54988      value: function checkCurrentTime_() {
54989        if (this.tech_.seeking() && this.fixesBadSeeks_()) {
54990          this.consecutiveUpdates = 0;
54991          this.lastRecordedTime = this.tech_.currentTime();
54992          return;
54993        }
54994
54995        if (this.tech_.paused() || this.tech_.seeking()) {
54996          return;
54997        }
54998
54999        var currentTime = this.tech_.currentTime();
55000        var buffered = this.tech_.buffered();
55001
55002        if (this.lastRecordedTime === currentTime && (!buffered.length || currentTime + SAFE_TIME_DELTA >= buffered.end(buffered.length - 1))) {
55003          // If current time is at the end of the final buffered region, then any playback
55004          // stall is most likely caused by buffering in a low bandwidth environment. The tech
55005          // should fire a `waiting` event in this scenario, but due to browser and tech
55006          // inconsistencies. Calling `techWaiting_` here allows us to simulate
55007          // responding to a native `waiting` event when the tech fails to emit one.
55008          return this.techWaiting_();
55009        }
55010
55011        if (this.consecutiveUpdates >= 5 && currentTime === this.lastRecordedTime) {
55012          this.consecutiveUpdates++;
55013          this.waiting_();
55014        } else if (currentTime === this.lastRecordedTime) {
55015          this.consecutiveUpdates++;
55016        } else {
55017          this.consecutiveUpdates = 0;
55018          this.lastRecordedTime = currentTime;
55019        }
55020      }
55021      /**
55022       * Cancels any pending timers and resets the 'timeupdate' mechanism
55023       * designed to detect that we are stalled
55024       *
55025       * @private
55026       */
55027
55028    }, {
55029      key: 'cancelTimer_',
55030      value: function cancelTimer_() {
55031        this.consecutiveUpdates = 0;
55032
55033        if (this.timer_) {
55034          this.logger_('cancelTimer_');
55035          clearTimeout(this.timer_);
55036        }
55037
55038        this.timer_ = null;
55039      }
55040      /**
55041       * Fixes situations where there's a bad seek
55042       *
55043       * @return {Boolean} whether an action was taken to fix the seek
55044       * @private
55045       */
55046
55047    }, {
55048      key: 'fixesBadSeeks_',
55049      value: function fixesBadSeeks_() {
55050        var seeking = this.tech_.seeking();
55051
55052        if (!seeking) {
55053          return false;
55054        }
55055
55056        var seekable = this.seekable();
55057        var currentTime = this.tech_.currentTime();
55058        var isAfterSeekableRange = this.afterSeekableWindow_(seekable, currentTime, this.media(), this.allowSeeksWithinUnsafeLiveWindow);
55059        var seekTo = void 0;
55060
55061        if (isAfterSeekableRange) {
55062          var seekableEnd = seekable.end(seekable.length - 1); // sync to live point (if VOD, our seekable was updated and we're simply adjusting)
55063
55064          seekTo = seekableEnd;
55065        }
55066
55067        if (this.beforeSeekableWindow_(seekable, currentTime)) {
55068          var seekableStart = seekable.start(0); // sync to the beginning of the live window
55069          // provide a buffer of .1 seconds to handle rounding/imprecise numbers
55070
55071          seekTo = seekableStart + SAFE_TIME_DELTA;
55072        }
55073
55074        if (typeof seekTo !== 'undefined') {
55075          this.logger_('Trying to seek outside of seekable at time ' + currentTime + ' with ' + ('seekable range ' + printableRange(seekable) + '. Seeking to ') + (seekTo + '.'));
55076          this.seekTo(seekTo);
55077          return true;
55078        }
55079
55080        return false;
55081      }
55082      /**
55083       * Handler for situations when we determine the player is waiting.
55084       *
55085       * @private
55086       */
55087
55088    }, {
55089      key: 'waiting_',
55090      value: function waiting_() {
55091        if (this.techWaiting_()) {
55092          return;
55093        } // All tech waiting checks failed. Use last resort correction
55094
55095
55096        var currentTime = this.tech_.currentTime();
55097        var buffered = this.tech_.buffered();
55098        var currentRange = findRange(buffered, currentTime); // Sometimes the player can stall for unknown reasons within a contiguous buffered
55099        // region with no indication that anything is amiss (seen in Firefox). Seeking to
55100        // currentTime is usually enough to kickstart the player. This checks that the player
55101        // is currently within a buffered region before attempting a corrective seek.
55102        // Chrome does not appear to continue `timeupdate` events after a `waiting` event
55103        // until there is ~ 3 seconds of forward buffer available. PlaybackWatcher should also
55104        // make sure there is ~3 seconds of forward buffer before taking any corrective action
55105        // to avoid triggering an `unknownwaiting` event when the network is slow.
55106
55107        if (currentRange.length && currentTime + 3 <= currentRange.end(0)) {
55108          this.cancelTimer_();
55109          this.seekTo(currentTime);
55110          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
55111
55112          this.tech_.trigger({
55113            type: 'usage',
55114            name: 'hls-unknown-waiting'
55115          });
55116          return;
55117        }
55118      }
55119      /**
55120       * Handler for situations when the tech fires a `waiting` event
55121       *
55122       * @return {Boolean}
55123       *         True if an action (or none) was needed to correct the waiting. False if no
55124       *         checks passed
55125       * @private
55126       */
55127
55128    }, {
55129      key: 'techWaiting_',
vendor: 18,787 bytes, lines 55130-55660
55130      value: function techWaiting_() {
55131        var seekable = this.seekable();
55132        var currentTime = this.tech_.currentTime();
55133
55134        if (this.tech_.seeking() && this.fixesBadSeeks_()) {
55135          // Tech is seeking or bad seek fixed, no action needed
55136          return true;
55137        }
55138
55139        if (this.tech_.seeking() || this.timer_ !== null) {
55140          // Tech is seeking or already waiting on another action, no action needed
55141          return true;
55142        }
55143
55144        if (this.beforeSeekableWindow_(seekable, currentTime)) {
55145          var livePoint = seekable.end(seekable.length - 1);
55146          this.logger_('Fell out of live window at time ' + currentTime + '. Seeking to ' + ('live point (seekable end) ' + livePoint));
55147          this.cancelTimer_();
55148          this.seekTo(livePoint); // live window resyncs may be useful for monitoring QoS
55149
55150          this.tech_.trigger({
55151            type: 'usage',
55152            name: 'hls-live-resync'
55153          });
55154          return true;
55155        }
55156
55157        var buffered = this.tech_.buffered();
55158        var nextRange = findNextRange(buffered, currentTime);
55159
55160        if (this.videoUnderflow_(nextRange, buffered, currentTime)) {
55161          // Even though the video underflowed and was stuck in a gap, the audio overplayed
55162          // the gap, leading currentTime into a buffered range. Seeking to currentTime
55163          // allows the video to catch up to the audio position without losing any audio
55164          // (only suffering ~3 seconds of frozen video and a pause in audio playback).
55165          this.cancelTimer_();
55166          this.seekTo(currentTime); // video underflow may be useful for monitoring QoS
55167
55168          this.tech_.trigger({
55169            type: 'usage',
55170            name: 'hls-video-underflow'
55171          });
55172          return true;
55173        } // check for gap
55174
55175
55176        if (nextRange.length > 0) {
55177          var difference = nextRange.start(0) - currentTime;
55178          this.logger_('Stopped at ' + currentTime + ', setting timer for ' + difference + ', seeking ' + ('to ' + nextRange.start(0)));
55179          this.timer_ = setTimeout(this.skipTheGap_.bind(this), difference * 1000, currentTime);
55180          return true;
55181        } // All checks failed. Returning false to indicate failure to correct waiting
55182
55183
55184        return false;
55185      }
55186    }, {
55187      key: 'afterSeekableWindow_',
55188      value: function afterSeekableWindow_(seekable, currentTime, playlist) {
55189        var allowSeeksWithinUnsafeLiveWindow = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : false;
55190
55191        if (!seekable.length) {
55192          // we can't make a solid case if there's no seekable, default to false
55193          return false;
55194        }
55195
55196        var allowedEnd = seekable.end(seekable.length - 1) + SAFE_TIME_DELTA;
55197        var isLive = !playlist.endList;
55198
55199        if (isLive && allowSeeksWithinUnsafeLiveWindow) {
55200          allowedEnd = seekable.end(seekable.length - 1) + playlist.targetDuration * 3;
55201        }
55202
55203        if (currentTime > allowedEnd) {
55204          return true;
55205        }
55206
55207        return false;
55208      }
55209    }, {
55210      key: 'beforeSeekableWindow_',
55211      value: function beforeSeekableWindow_(seekable, currentTime) {
55212        if (seekable.length && // can't fall before 0 and 0 seekable start identifies VOD stream
55213        seekable.start(0) > 0 && currentTime < seekable.start(0) - SAFE_TIME_DELTA) {
55214          return true;
55215        }
55216
55217        return false;
55218      }
55219    }, {
55220      key: 'videoUnderflow_',
55221      value: function videoUnderflow_(nextRange, buffered, currentTime) {
55222        if (nextRange.length === 0) {
55223          // Even if there is no available next range, there is still a possibility we are
55224          // stuck in a gap due to video underflow.
55225          var gap = this.gapFromVideoUnderflow_(buffered, currentTime);
55226
55227          if (gap) {
55228            this.logger_('Encountered a gap in video from ' + gap.start + ' to ' + gap.end + '. ' + ('Seeking to current time ' + currentTime));
55229            return true;
55230          }
55231        }
55232
55233        return false;
55234      }
55235      /**
55236       * Timer callback. If playback still has not proceeded, then we seek
55237       * to the start of the next buffered region.
55238       *
55239       * @private
55240       */
55241
55242    }, {
55243      key: 'skipTheGap_',
55244      value: function skipTheGap_(scheduledCurrentTime) {
55245        var buffered = this.tech_.buffered();
55246        var currentTime = this.tech_.currentTime();
55247        var nextRange = findNextRange(buffered, currentTime);
55248        this.cancelTimer_();
55249
55250        if (nextRange.length === 0 || currentTime !== scheduledCurrentTime) {
55251          return;
55252        }
55253
55254        this.logger_('skipTheGap_:', 'currentTime:', currentTime, 'scheduled currentTime:', scheduledCurrentTime, 'nextRange start:', nextRange.start(0)); // only seek if we still have not played
55255
55256        this.seekTo(nextRange.start(0) + TIME_FUDGE_FACTOR);
55257        this.tech_.trigger({
55258          type: 'usage',
55259          name: 'hls-gap-skip'
55260        });
55261      }
55262    }, {
55263      key: 'gapFromVideoUnderflow_',
55264      value: function gapFromVideoUnderflow_(buffered, currentTime) {
55265        // At least in Chrome, if there is a gap in the video buffer, the audio will continue
55266        // playing for ~3 seconds after the video gap starts. This is done to account for
55267        // video buffer underflow/underrun (note that this is not done when there is audio
55268        // buffer underflow/underrun -- in that case the video will stop as soon as it
55269        // encounters the gap, as audio stalls are more noticeable/jarring to a user than
55270        // video stalls). The player's time will reflect the playthrough of audio, so the
55271        // time will appear as if we are in a buffered region, even if we are stuck in a
55272        // "gap."
55273        //
55274        // Example:
55275        // video buffer:   0 => 10.1, 10.2 => 20
55276        // audio buffer:   0 => 20
55277        // overall buffer: 0 => 10.1, 10.2 => 20
55278        // current time: 13
55279        //
55280        // Chrome's video froze at 10 seconds, where the video buffer encountered the gap,
55281        // however, the audio continued playing until it reached ~3 seconds past the gap
55282        // (13 seconds), at which point it stops as well. Since current time is past the
55283        // gap, findNextRange will return no ranges.
55284        //
55285        // To check for this issue, we see if there is a gap that starts somewhere within
55286        // a 3 second range (3 seconds +/- 1 second) back from our current time.
55287        var gaps = findGaps(buffered);
55288
55289        for (var i = 0; i < gaps.length; i++) {
55290          var start = gaps.start(i);
55291          var end = gaps.end(i); // gap is starts no more than 4 seconds back
55292
55293          if (currentTime - start < 4 && currentTime - start > 2) {
55294            return {
55295              start: start,
55296              end: end
55297            };
55298          }
55299        }
55300
55301        return null;
55302      }
55303    }]);
55304    return PlaybackWatcher;
55305  }();
55306
55307  var defaultOptions = {
55308    errorInterval: 30,
55309    getSource: function getSource(next) {
55310      var tech = this.tech({
55311        IWillNotUseThisInPlugins: true
55312      });
55313      var sourceObj = tech.currentSource_;
55314      return next(sourceObj);
55315    }
55316  };
55317  /**
55318   * Main entry point for the plugin
55319   *
55320   * @param {Player} player a reference to a videojs Player instance
55321   * @param {Object} [options] an object with plugin options
55322   * @private
55323   */
55324
55325  var initPlugin = function initPlugin(player, options) {
55326    var lastCalled = 0;
55327    var seekTo = 0;
55328    var localOptions = videojs$1.mergeOptions(defaultOptions, options);
55329    player.ready(function () {
55330      player.trigger({
55331        type: 'usage',
55332        name: 'hls-error-reload-initialized'
55333      });
55334    });
55335    /**
55336     * Player modifications to perform that must wait until `loadedmetadata`
55337     * has been triggered
55338     *
55339     * @private
55340     */
55341
55342    var loadedMetadataHandler = function loadedMetadataHandler() {
55343      if (seekTo) {
55344        player.currentTime(seekTo);
55345      }
55346    };
55347    /**
55348     * Set the source on the player element, play, and seek if necessary
55349     *
55350     * @param {Object} sourceObj An object specifying the source url and mime-type to play
55351     * @private
55352     */
55353
55354
55355    var setSource = function setSource(sourceObj) {
55356      if (sourceObj === null || sourceObj === undefined) {
55357        return;
55358      }
55359
55360      seekTo = player.duration() !== Infinity && player.currentTime() || 0;
55361      player.one('loadedmetadata', loadedMetadataHandler);
55362      player.src(sourceObj);
55363      player.trigger({
55364        type: 'usage',
55365        name: 'hls-error-reload'
55366      });
55367      player.play();
55368    };
55369    /**
55370     * Attempt to get a source from either the built-in getSource function
55371     * or a custom function provided via the options
55372     *
55373     * @private
55374     */
55375
55376
55377    var errorHandler = function errorHandler() {
55378      // Do not attempt to reload the source if a source-reload occurred before
55379      // 'errorInterval' time has elapsed since the last source-reload
55380      if (Date.now() - lastCalled < localOptions.errorInterval * 1000) {
55381        player.trigger({
55382          type: 'usage',
55383          name: 'hls-error-reload-canceled'
55384        });
55385        return;
55386      }
55387
55388      if (!localOptions.getSource || typeof localOptions.getSource !== 'function') {
55389        videojs$1.log.error('ERROR: reloadSourceOnError - The option getSource must be a function!');
55390        return;
55391      }
55392
55393      lastCalled = Date.now();
55394      return localOptions.getSource.call(player, setSource);
55395    };
55396    /**
55397     * Unbind any event handlers that were bound by the plugin
55398     *
55399     * @private
55400     */
55401
55402
55403    var cleanupEvents = function cleanupEvents() {
55404      player.off('loadedmetadata', loadedMetadataHandler);
55405      player.off('error', errorHandler);
55406      player.off('dispose', cleanupEvents);
55407    };
55408    /**
55409     * Cleanup before re-initializing the plugin
55410     *
55411     * @param {Object} [newOptions] an object with plugin options
55412     * @private
55413     */
55414
55415
55416    var reinitPlugin = function reinitPlugin(newOptions) {
55417      cleanupEvents();
55418      initPlugin(player, newOptions);
55419    };
55420
55421    player.on('error', errorHandler);
55422    player.on('dispose', cleanupEvents); // Overwrite the plugin function so that we can correctly cleanup before
55423    // initializing the plugin
55424
55425    player.reloadSourceOnError = reinitPlugin;
55426  };
55427  /**
55428   * Reload the source when an error is detected as long as there
55429   * wasn't an error previously within the last 30 seconds
55430   *
55431   * @param {Object} [options] an object with plugin options
55432   */
55433
55434
55435  var reloadSourceOnError = function reloadSourceOnError(options) {
55436    initPlugin(this, options);
55437  };
55438
55439  var version$1 = "1.10.6"; // since VHS handles HLS and DASH (and in the future, more types), use * to capture all
55440
55441  videojs$1.use('*', function (player) {
55442    return {
55443      setSource: function setSource(srcObj, next) {
55444        // pass null as the first argument to indicate that the source is not rejected
55445        next(null, srcObj);
55446      },
55447      // VHS needs to know when seeks happen. For external seeks (generated at the player
55448      // level), this middleware will capture the action. For internal seeks (generated at
55449      // the tech level), we use a wrapped function so that we can handle it on our own
55450      // (specified elsewhere).
55451      setCurrentTime: function setCurrentTime(time) {
55452        if (player.vhs && player.currentSource().src === player.vhs.source_.src) {
55453          player.vhs.setCurrentTime(time);
55454        }
55455
55456        return time;
55457      },
55458      // Sync VHS after play requests.
55459      // This specifically handles replay where the order of actions is
55460      // play, video element will seek to 0 (skipping the setCurrentTime middleware)
55461      // then triggers a play event.
55462      play: function play() {
55463        if (player.vhs && player.currentSource().src === player.vhs.source_.src) {
55464          player.vhs.setCurrentTime(player.tech_.currentTime());
55465        }
55466      }
55467    };
55468  });
55469  /**
55470   * @file videojs-http-streaming.js
55471   *
55472   * The main file for the HLS project.
55473   * License: https://github.com/videojs/videojs-http-streaming/blob/master/LICENSE
55474   */
55475
55476  var Hls$1 = {
55477    PlaylistLoader: PlaylistLoader,
55478    Playlist: Playlist,
55479    Decrypter: Decrypter,
55480    AsyncStream: AsyncStream,
55481    decrypt: decrypt,
55482    utils: utils$1,
55483    STANDARD_PLAYLIST_SELECTOR: lastBandwidthSelector,
55484    INITIAL_PLAYLIST_SELECTOR: lowestBitrateCompatibleVariantSelector,
55485    comparePlaylistBandwidth: comparePlaylistBandwidth,
55486    comparePlaylistResolution: comparePlaylistResolution,
55487    xhr: xhrFactory()
55488  }; // Define getter/setters for config properites
55489
55490  ['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) {
55491    Object.defineProperty(Hls$1, prop, {
55492      get: function get$$1() {
55493        videojs$1.log.warn('using Hls.' + prop + ' is UNSAFE be sure you know what you are doing');
55494        return Config[prop];
55495      },
55496      set: function set$$1(value) {
55497        videojs$1.log.warn('using Hls.' + prop + ' is UNSAFE be sure you know what you are doing');
55498
55499        if (typeof value !== 'number' || value < 0) {
55500          videojs$1.log.warn('value of Hls.' + prop + ' must be greater than or equal to 0');
55501          return;
55502        }
55503
55504        Config[prop] = value;
55505      }
55506    });
55507  });
55508  var LOCAL_STORAGE_KEY$1 = 'videojs-vhs';
55509
55510  var simpleTypeFromSourceType = function simpleTypeFromSourceType(type) {
55511    var mpegurlRE = /^(audio|video|application)\/(x-|vnd\.apple\.)?mpegurl/i;
55512
55513    if (mpegurlRE.test(type)) {
55514      return 'hls';
55515    }
55516
55517    var dashRE = /^application\/dash\+xml/i;
55518
55519    if (dashRE.test(type)) {
55520      return 'dash';
55521    }
55522
55523    return null;
55524  };
55525  /**
55526   * Updates the selectedIndex of the QualityLevelList when a mediachange happens in hls.
55527   *
55528   * @param {QualityLevelList} qualityLevels The QualityLevelList to update.
55529   * @param {PlaylistLoader} playlistLoader PlaylistLoader containing the new media info.
55530   * @function handleHlsMediaChange
55531   */
55532
55533
55534  var handleHlsMediaChange = function handleHlsMediaChange(qualityLevels, playlistLoader) {
55535    var newPlaylist = playlistLoader.media();
55536    var selectedIndex = -1;
55537
55538    for (var i = 0; i < qualityLevels.length; i++) {
55539      if (qualityLevels[i].id === newPlaylist.uri) {
55540        selectedIndex = i;
55541        break;
55542      }
55543    }
55544
55545    qualityLevels.selectedIndex_ = selectedIndex;
55546    qualityLevels.trigger({
55547      selectedIndex: selectedIndex,
55548      type: 'change'
55549    });
55550  };
55551  /**
55552   * Adds quality levels to list once playlist metadata is available
55553   *
55554   * @param {QualityLevelList} qualityLevels The QualityLevelList to attach events to.
55555   * @param {Object} hls Hls object to listen to for media events.
55556   * @function handleHlsLoadedMetadata
55557   */
55558
55559
55560  var handleHlsLoadedMetadata = function handleHlsLoadedMetadata(qualityLevels, hls) {
55561    hls.representations().forEach(function (rep) {
55562      qualityLevels.addQualityLevel(rep);
55563    });
55564    handleHlsMediaChange(qualityLevels, hls.playlists);
55565  }; // HLS is a source handler, not a tech. Make sure attempts to use it
55566  // as one do not cause exceptions.
55567
55568
55569  Hls$1.canPlaySource = function () {
55570    return videojs$1.log.warn('HLS is no longer a tech. Please remove it from ' + 'your player\'s techOrder.');
55571  };
55572
55573  var emeKeySystems = function emeKeySystems(keySystemOptions, videoPlaylist, audioPlaylist) {
55574    if (!keySystemOptions) {
55575      return keySystemOptions;
55576    } // upsert the content types based on the selected playlist
55577
55578
55579    var keySystemContentTypes = {};
55580
55581    for (var keySystem in keySystemOptions) {
55582      keySystemContentTypes[keySystem] = {
55583        audioContentType: 'audio/mp4; codecs="' + audioPlaylist.attributes.CODECS + '"',
55584        videoContentType: 'video/mp4; codecs="' + videoPlaylist.attributes.CODECS + '"'
55585      };
55586
55587      if (videoPlaylist.contentProtection && videoPlaylist.contentProtection[keySystem] && videoPlaylist.contentProtection[keySystem].pssh) {
55588        keySystemContentTypes[keySystem].pssh = videoPlaylist.contentProtection[keySystem].pssh;
55589      } // videojs-contrib-eme accepts the option of specifying: 'com.some.cdm': 'url'
55590      // so we need to prevent overwriting the URL entirely
55591
55592
55593      if (typeof keySystemOptions[keySystem] === 'string') {
55594        keySystemContentTypes[keySystem].url = keySystemOptions[keySystem];
55595      }
55596    }
55597
55598    return videojs$1.mergeOptions(keySystemOptions, keySystemContentTypes);
55599  };
55600
55601  var setupEmeOptions = function setupEmeOptions(hlsHandler) {
55602    if (hlsHandler.options_.sourceType !== 'dash') {
55603      return;
55604    }
55605
55606    var player = videojs$1.players[hlsHandler.tech_.options_.playerId];
55607
55608    if (player.eme) {
55609      var sourceOptions = emeKeySystems(hlsHandler.source_.keySystems, hlsHandler.playlists.media(), hlsHandler.masterPlaylistController_.mediaTypes_.AUDIO.activePlaylistLoader.media());
55610
55611      if (sourceOptions) {
55612        player.currentSource().keySystems = sourceOptions; // works around https://bugs.chromium.org/p/chromium/issues/detail?id=895449
55613
55614        if (player.eme.initializeMediaKeys) {
55615          player.eme.initializeMediaKeys();
55616        }
55617      }
55618    }
55619  };
55620
55621  var getVhsLocalStorage = function getVhsLocalStorage() {
55622    if (!window.localStorage) {
55623      return null;
55624    }
55625
55626    var storedObject = window.localStorage.getItem(LOCAL_STORAGE_KEY$1);
55627
55628    if (!storedObject) {
55629      return null;
55630    }
55631
55632    try {
55633      return JSON.parse(storedObject);
55634    } catch (e) {
55635      // someone may have tampered with the value
55636      return null;
55637    }
55638  };
55639
55640  var updateVhsLocalStorage = function updateVhsLocalStorage(options) {
55641    if (!window.localStorage) {
55642      return false;
55643    }
55644
55645    var objectToStore = getVhsLocalStorage();
55646    objectToStore = objectToStore ? videojs$1.mergeOptions(objectToStore, options) : options;
55647
55648    try {
55649      window.localStorage.setItem(LOCAL_STORAGE_KEY$1, JSON.stringify(objectToStore));
55650    } catch (e) {
55651      // Throws if storage is full (e.g., always on iOS 5+ Safari private mode, where
55652      // storage is set to 0).
55653      // https://developer.mozilla.org/en-US/docs/Web/API/Storage/setItem#Exceptions
55654      // No need to perform any operation.
55655      return false;
55656    }
55657
55658    return objectToStore;
55659  };
55660  /**
vendor: 16,384 bytes, lines 55661-56070
55661   * Whether the browser has built-in HLS support.
55662   */
55663
55664
55665  Hls$1.supportsNativeHls = function () {
55666    var video = document.createElement('video'); // native HLS is definitely not supported if HTML5 video isn't
55667
55668    if (!videojs$1.getTech('Html5').isSupported()) {
55669      return false;
55670    } // HLS manifests can go by many mime-types
55671
55672
55673    var canPlay = [// Apple santioned
55674    'application/vnd.apple.mpegurl', // Apple sanctioned for backwards compatibility
55675    'audio/mpegurl', // Very common
55676    'audio/x-mpegurl', // Very common
55677    'application/x-mpegurl', // Included for completeness
55678    'video/x-mpegurl', 'video/mpegurl', 'application/mpegurl'];
55679    return canPlay.some(function (canItPlay) {
55680      return /maybe|probably/i.test(video.canPlayType(canItPlay));
55681    });
55682  }();
55683
55684  Hls$1.supportsNativeDash = function () {
55685    if (!videojs$1.getTech('Html5').isSupported()) {
55686      return false;
55687    }
55688
55689    return /maybe|probably/i.test(document.createElement('video').canPlayType('application/dash+xml'));
55690  }();
55691
55692  Hls$1.supportsTypeNatively = function (type) {
55693    if (type === 'hls') {
55694      return Hls$1.supportsNativeHls;
55695    }
55696
55697    if (type === 'dash') {
55698      return Hls$1.supportsNativeDash;
55699    }
55700
55701    return false;
55702  };
55703  /**
55704   * HLS is a source handler, not a tech. Make sure attempts to use it
55705   * as one do not cause exceptions.
55706   */
55707
55708
55709  Hls$1.isSupported = function () {
55710    return videojs$1.log.warn('HLS is no longer a tech. Please remove it from ' + 'your player\'s techOrder.');
55711  };
55712
55713  var Component$1 = videojs$1.getComponent('Component');
55714  /**
55715   * The Hls Handler object, where we orchestrate all of the parts
55716   * of HLS to interact with video.js
55717   *
55718   * @class HlsHandler
55719   * @extends videojs.Component
55720   * @param {Object} source the soruce object
55721   * @param {Tech} tech the parent tech object
55722   * @param {Object} options optional and required options
55723   */
55724
55725  var HlsHandler = function (_Component) {
55726    inherits$1(HlsHandler, _Component);
55727
55728    function HlsHandler(source, tech, options) {
55729      classCallCheck$1(this, HlsHandler); // tech.player() is deprecated but setup a reference to HLS for
55730      // backwards-compatibility
55731
55732      var _this = possibleConstructorReturn$1(this, (HlsHandler.__proto__ || Object.getPrototypeOf(HlsHandler)).call(this, tech, options.hls));
55733
55734      if (tech.options_ && tech.options_.playerId) {
55735        var _player = videojs$1(tech.options_.playerId);
55736
55737        if (!_player.hasOwnProperty('hls')) {
55738          Object.defineProperty(_player, 'hls', {
55739            get: function get$$1() {
55740              videojs$1.log.warn('player.hls is deprecated. Use player.tech().hls instead.');
55741              tech.trigger({
55742                type: 'usage',
55743                name: 'hls-player-access'
55744              });
55745              return _this;
55746            },
55747            configurable: true
55748          });
55749        } // Set up a reference to the HlsHandler from player.vhs. This allows users to start
55750        // migrating from player.tech_.hls... to player.vhs... for API access. Although this
55751        // isn't the most appropriate form of reference for video.js (since all APIs should
55752        // be provided through core video.js), it is a common pattern for plugins, and vhs
55753        // will act accordingly.
55754
55755
55756        _player.vhs = _this; // deprecated, for backwards compatibility
55757
55758        _player.dash = _this;
55759        _this.player_ = _player;
55760      }
55761
55762      _this.tech_ = tech;
55763      _this.source_ = source;
55764      _this.stats = {};
55765
55766      _this.setOptions_();
55767
55768      if (_this.options_.overrideNative && tech.overrideNativeAudioTracks && tech.overrideNativeVideoTracks) {
55769        tech.overrideNativeAudioTracks(true);
55770        tech.overrideNativeVideoTracks(true);
55771      } else if (_this.options_.overrideNative && (tech.featuresNativeVideoTracks || tech.featuresNativeAudioTracks)) {
55772        // overriding native HLS only works if audio tracks have been emulated
55773        // error early if we're misconfigured
55774        throw new Error('Overriding native HLS requires emulated tracks. ' + 'See https://git.io/vMpjB');
55775      } // listen for fullscreenchange events for this player so that we
55776      // can adjust our quality selection quickly
55777
55778
55779      _this.on(document, ['fullscreenchange', 'webkitfullscreenchange', 'mozfullscreenchange', 'MSFullscreenChange'], function (event) {
55780        var fullscreenElement = document.fullscreenElement || document.webkitFullscreenElement || document.mozFullScreenElement || document.msFullscreenElement;
55781
55782        if (fullscreenElement && fullscreenElement.contains(_this.tech_.el())) {
55783          _this.masterPlaylistController_.smoothQualityChange_();
55784        }
55785      }); // Handle seeking when looping - middleware doesn't handle this seek event from the tech
55786
55787
55788      _this.on(_this.tech_, 'seeking', function () {
55789        if (this.tech_.currentTime() === 0 && this.tech_.player_.loop()) {
55790          this.setCurrentTime(0);
55791        }
55792      });
55793
55794      _this.on(_this.tech_, 'error', function () {
55795        if (this.masterPlaylistController_) {
55796          this.masterPlaylistController_.pauseLoading();
55797        }
55798      });
55799
55800      _this.on(_this.tech_, 'play', _this.play);
55801
55802      return _this;
55803    }
55804
55805    createClass$1(HlsHandler, [{
55806      key: 'setOptions_',
55807      value: function setOptions_() {
55808        var _this2 = this; // defaults
55809
55810
55811        this.options_.withCredentials = this.options_.withCredentials || false;
55812        this.options_.handleManifestRedirects = this.options_.handleManifestRedirects || false;
55813        this.options_.limitRenditionByPlayerDimensions = this.options_.limitRenditionByPlayerDimensions === false ? false : true;
55814        this.options_.smoothQualityChange = this.options_.smoothQualityChange || false;
55815        this.options_.useBandwidthFromLocalStorage = typeof this.source_.useBandwidthFromLocalStorage !== 'undefined' ? this.source_.useBandwidthFromLocalStorage : this.options_.useBandwidthFromLocalStorage || false;
55816        this.options_.customTagParsers = this.options_.customTagParsers || [];
55817        this.options_.customTagMappers = this.options_.customTagMappers || [];
55818        this.options_.cacheEncryptionKeys = this.options_.cacheEncryptionKeys || false;
55819
55820        if (typeof this.options_.blacklistDuration !== 'number') {
55821          this.options_.blacklistDuration = 5 * 60;
55822        }
55823
55824        if (typeof this.options_.bandwidth !== 'number') {
55825          if (this.options_.useBandwidthFromLocalStorage) {
55826            var storedObject = getVhsLocalStorage();
55827
55828            if (storedObject && storedObject.bandwidth) {
55829              this.options_.bandwidth = storedObject.bandwidth;
55830              this.tech_.trigger({
55831                type: 'usage',
55832                name: 'hls-bandwidth-from-local-storage'
55833              });
55834            }
55835
55836            if (storedObject && storedObject.throughput) {
55837              this.options_.throughput = storedObject.throughput;
55838              this.tech_.trigger({
55839                type: 'usage',
55840                name: 'hls-throughput-from-local-storage'
55841              });
55842            }
55843          }
55844        } // if bandwidth was not set by options or pulled from local storage, start playlist
55845        // selection at a reasonable bandwidth
55846
55847
55848        if (typeof this.options_.bandwidth !== 'number') {
55849          this.options_.bandwidth = Config.INITIAL_BANDWIDTH;
55850        } // If the bandwidth number is unchanged from the initial setting
55851        // then this takes precedence over the enableLowInitialPlaylist option
55852
55853
55854        this.options_.enableLowInitialPlaylist = this.options_.enableLowInitialPlaylist && this.options_.bandwidth === Config.INITIAL_BANDWIDTH; // grab options passed to player.src
55855
55856        ['withCredentials', 'limitRenditionByPlayerDimensions', 'bandwidth', 'smoothQualityChange', 'customTagParsers', 'customTagMappers', 'handleManifestRedirects', 'cacheEncryptionKeys'].forEach(function (option) {
55857          if (typeof _this2.source_[option] !== 'undefined') {
55858            _this2.options_[option] = _this2.source_[option];
55859          }
55860        });
55861        this.limitRenditionByPlayerDimensions = this.options_.limitRenditionByPlayerDimensions;
55862      }
55863      /**
55864       * called when player.src gets called, handle a new source
55865       *
55866       * @param {Object} src the source object to handle
55867       */
55868
55869    }, {
55870      key: 'src',
55871      value: function src(_src, type) {
55872        var _this3 = this; // do nothing if the src is falsey
55873
55874
55875        if (!_src) {
55876          return;
55877        }
55878
55879        this.setOptions_(); // add master playlist controller options
55880
55881        this.options_.url = this.source_.src;
55882        this.options_.tech = this.tech_;
55883        this.options_.externHls = Hls$1;
55884        this.options_.sourceType = simpleTypeFromSourceType(type); // Whenever we seek internally, we should update both the tech and call our own
55885        // setCurrentTime function. This is needed because "seeking" events aren't always
55886        // reliable. External seeks (via the player object) are handled via middleware.
55887
55888        this.options_.seekTo = function (time) {
55889          _this3.tech_.setCurrentTime(time);
55890
55891          _this3.setCurrentTime(time);
55892        };
55893
55894        this.masterPlaylistController_ = new MasterPlaylistController(this.options_);
55895        this.playbackWatcher_ = new PlaybackWatcher(videojs$1.mergeOptions(this.options_, {
55896          seekable: function seekable$$1() {
55897            return _this3.seekable();
55898          },
55899          media: function media() {
55900            return _this3.masterPlaylistController_.media();
55901          }
55902        }));
55903        this.masterPlaylistController_.on('error', function () {
55904          var player = videojs$1.players[_this3.tech_.options_.playerId];
55905          player.error(_this3.masterPlaylistController_.error);
55906        }); // `this` in selectPlaylist should be the HlsHandler for backwards
55907        // compatibility with < v2
55908
55909        this.masterPlaylistController_.selectPlaylist = this.selectPlaylist ? this.selectPlaylist.bind(this) : Hls$1.STANDARD_PLAYLIST_SELECTOR.bind(this);
55910        this.masterPlaylistController_.selectInitialPlaylist = Hls$1.INITIAL_PLAYLIST_SELECTOR.bind(this); // re-expose some internal objects for backwards compatibility with < v2
55911
55912        this.playlists = this.masterPlaylistController_.masterPlaylistLoader_;
55913        this.mediaSource = this.masterPlaylistController_.mediaSource; // Proxy assignment of some properties to the master playlist
55914        // controller. Using a custom property for backwards compatibility
55915        // with < v2
55916
55917        Object.defineProperties(this, {
55918          selectPlaylist: {
55919            get: function get$$1() {
55920              return this.masterPlaylistController_.selectPlaylist;
55921            },
55922            set: function set$$1(selectPlaylist) {
55923              this.masterPlaylistController_.selectPlaylist = selectPlaylist.bind(this);
55924            }
55925          },
55926          throughput: {
55927            get: function get$$1() {
55928              return this.masterPlaylistController_.mainSegmentLoader_.throughput.rate;
55929            },
55930            set: function set$$1(throughput) {
55931              this.masterPlaylistController_.mainSegmentLoader_.throughput.rate = throughput; // By setting `count` to 1 the throughput value becomes the starting value
55932              // for the cumulative average
55933
55934              this.masterPlaylistController_.mainSegmentLoader_.throughput.count = 1;
55935            }
55936          },
55937          bandwidth: {
55938            get: function get$$1() {
55939              return this.masterPlaylistController_.mainSegmentLoader_.bandwidth;
55940            },
55941            set: function set$$1(bandwidth) {
55942              this.masterPlaylistController_.mainSegmentLoader_.bandwidth = bandwidth; // setting the bandwidth manually resets the throughput counter
55943              // `count` is set to zero that current value of `rate` isn't included
55944              // in the cumulative average
55945
55946              this.masterPlaylistController_.mainSegmentLoader_.throughput = {
55947                rate: 0,
55948                count: 0
55949              };
55950            }
55951          },
55952
55953          /**
55954           * `systemBandwidth` is a combination of two serial processes bit-rates. The first
55955           * is the network bitrate provided by `bandwidth` and the second is the bitrate of
55956           * the entire process after that - decryption, transmuxing, and appending - provided
55957           * by `throughput`.
55958           *
55959           * Since the two process are serial, the overall system bandwidth is given by:
55960           *   sysBandwidth = 1 / (1 / bandwidth + 1 / throughput)
55961           */
55962          systemBandwidth: {
55963            get: function get$$1() {
55964              var invBandwidth = 1 / (this.bandwidth || 1);
55965              var invThroughput = void 0;
55966
55967              if (this.throughput > 0) {
55968                invThroughput = 1 / this.throughput;
55969              } else {
55970                invThroughput = 0;
55971              }
55972
55973              var systemBitrate = Math.floor(1 / (invBandwidth + invThroughput));
55974              return systemBitrate;
55975            },
55976            set: function set$$1() {
55977              videojs$1.log.error('The "systemBandwidth" property is read-only');
55978            }
55979          }
55980        });
55981
55982        if (this.options_.bandwidth) {
55983          this.bandwidth = this.options_.bandwidth;
55984        }
55985
55986        if (this.options_.throughput) {
55987          this.throughput = this.options_.throughput;
55988        }
55989
55990        Object.defineProperties(this.stats, {
55991          bandwidth: {
55992            get: function get$$1() {
55993              return _this3.bandwidth || 0;
55994            },
55995            enumerable: true
55996          },
55997          mediaRequests: {
55998            get: function get$$1() {
55999              return _this3.masterPlaylistController_.mediaRequests_() || 0;
56000            },
56001            enumerable: true
56002          },
56003          mediaRequestsAborted: {
56004            get: function get$$1() {
56005              return _this3.masterPlaylistController_.mediaRequestsAborted_() || 0;
56006            },
56007            enumerable: true
56008          },
56009          mediaRequestsTimedout: {
56010            get: function get$$1() {
56011              return _this3.masterPlaylistController_.mediaRequestsTimedout_() || 0;
56012            },
56013            enumerable: true
56014          },
56015          mediaRequestsErrored: {
56016            get: function get$$1() {
56017              return _this3.masterPlaylistController_.mediaRequestsErrored_() || 0;
56018            },
56019            enumerable: true
56020          },
56021          mediaTransferDuration: {
56022            get: function get$$1() {
56023              return _this3.masterPlaylistController_.mediaTransferDuration_() || 0;
56024            },
56025            enumerable: true
56026          },
56027          mediaBytesTransferred: {
56028            get: function get$$1() {
56029              return _this3.masterPlaylistController_.mediaBytesTransferred_() || 0;
56030            },
56031            enumerable: true
56032          },
56033          mediaSecondsLoaded: {
56034            get: function get$$1() {
56035              return _this3.masterPlaylistController_.mediaSecondsLoaded_() || 0;
56036            },
56037            enumerable: true
56038          },
56039          buffered: {
56040            get: function get$$1() {
56041              return timeRangesToArray(_this3.tech_.buffered());
56042            },
56043            enumerable: true
56044          },
56045          currentTime: {
56046            get: function get$$1() {
56047              return _this3.tech_.currentTime();
56048            },
56049            enumerable: true
56050          },
56051          currentSource: {
56052            get: function get$$1() {
56053              return _this3.tech_.currentSource_;
56054            },
56055            enumerable: true
56056          },
56057          currentTech: {
56058            get: function get$$1() {
56059              return _this3.tech_.name_;
56060            },
56061            enumerable: true
56062          },
56063          duration: {
56064            get: function get$$1() {
56065              return _this3.tech_.duration();
56066            },
56067            enumerable: true
56068          },
56069          master: {
56070            get: function get$$1() {
vendor: 4,201 bytes, lines 56071-56204
56071              return _this3.playlists.master;
56072            },
56073            enumerable: true
56074          },
56075          playerDimensions: {
56076            get: function get$$1() {
56077              return _this3.tech_.currentDimensions();
56078            },
56079            enumerable: true
56080          },
56081          seekable: {
56082            get: function get$$1() {
56083              return timeRangesToArray(_this3.tech_.seekable());
56084            },
56085            enumerable: true
56086          },
56087          timestamp: {
56088            get: function get$$1() {
56089              return Date.now();
56090            },
56091            enumerable: true
56092          },
56093          videoPlaybackQuality: {
56094            get: function get$$1() {
56095              return _this3.tech_.getVideoPlaybackQuality();
56096            },
56097            enumerable: true
56098          }
56099        });
56100        this.tech_.one('canplay', this.masterPlaylistController_.setupFirstPlay.bind(this.masterPlaylistController_));
56101        this.tech_.on('bandwidthupdate', function () {
56102          if (_this3.options_.useBandwidthFromLocalStorage) {
56103            updateVhsLocalStorage({
56104              bandwidth: _this3.bandwidth,
56105              throughput: Math.round(_this3.throughput)
56106            });
56107          }
56108        });
56109        this.masterPlaylistController_.on('selectedinitialmedia', function () {
56110          // Add the manual rendition mix-in to HlsHandler
56111          renditionSelectionMixin(_this3);
56112          setupEmeOptions(_this3);
56113        }); // the bandwidth of the primary segment loader is our best
56114        // estimate of overall bandwidth
56115
56116        this.on(this.masterPlaylistController_, 'progress', function () {
56117          this.tech_.trigger('progress');
56118        });
56119        this.tech_.ready(function () {
56120          return _this3.setupQualityLevels_();
56121        }); // do nothing if the tech has been disposed already
56122        // this can occur if someone sets the src in player.ready(), for instance
56123
56124        if (!this.tech_.el()) {
56125          return;
56126        }
56127
56128        this.tech_.src(videojs$1.URL.createObjectURL(this.masterPlaylistController_.mediaSource));
56129      }
56130      /**
56131       * Initializes the quality levels and sets listeners to update them.
56132       *
56133       * @method setupQualityLevels_
56134       * @private
56135       */
56136
56137    }, {
56138      key: 'setupQualityLevels_',
56139      value: function setupQualityLevels_() {
56140        var _this4 = this;
56141
56142        var player = videojs$1.players[this.tech_.options_.playerId];
56143
56144        if (player && player.qualityLevels) {
56145          this.qualityLevels_ = player.qualityLevels();
56146          this.masterPlaylistController_.on('selectedinitialmedia', function () {
56147            handleHlsLoadedMetadata(_this4.qualityLevels_, _this4);
56148          });
56149          this.playlists.on('mediachange', function () {
56150            handleHlsMediaChange(_this4.qualityLevels_, _this4.playlists);
56151          });
56152        }
56153      }
56154      /**
56155       * Begin playing the video.
56156       */
56157
56158    }, {
56159      key: 'play',
56160      value: function play() {
56161        this.masterPlaylistController_.play();
56162      }
56163      /**
56164       * a wrapper around the function in MasterPlaylistController
56165       */
56166
56167    }, {
56168      key: 'setCurrentTime',
56169      value: function setCurrentTime(currentTime) {
56170        this.masterPlaylistController_.setCurrentTime(currentTime);
56171      }
56172      /**
56173       * a wrapper around the function in MasterPlaylistController
56174       */
56175
56176    }, {
56177      key: 'duration',
56178      value: function duration$$1() {
56179        return this.masterPlaylistController_.duration();
56180      }
56181      /**
56182       * a wrapper around the function in MasterPlaylistController
56183       */
56184
56185    }, {
56186      key: 'seekable',
56187      value: function seekable$$1() {
56188        return this.masterPlaylistController_.seekable();
56189      }
56190      /**
56191       * Abort all outstanding work and cleanup.
56192       */
56193
56194    }, {
56195      key: 'dispose',
56196      value: function dispose() {
56197        if (this.playbackWatcher_) {
56198          this.playbackWatcher_.dispose();
56199        }
56200
56201        if (this.masterPlaylistController_) {
56202          this.masterPlaylistController_.dispose();
56203        }
56204
56205        if (this.qualityLevels_) {
56206          this.qualityLevels_.dispose();
56207        }
56208
56209        if (this.player_) {
56210          delete this.player_.vhs;
56211          delete this.player_.dash;
56212          delete this.player_.hls;
56213        }
56214
56215        if (this.tech_ && this.tech_.hls) {
56216          delete this.tech_.hls;
56217        }
56218
56219        get$1(HlsHandler.prototype.__proto__ || Object.getPrototypeOf(HlsHandler.prototype), 'dispose', this).call(this);
56220      }
56221    }, {
56222      key: 'convertToProgramTime',
56223      value: function convertToProgramTime(time, callback) {
56224        return getProgramTime({
56225          playlist: this.masterPlaylistController_.media(),
56226          time: time,
56227          callback: callback
56228        });
56229      } // the player must be playing before calling this
56230
56231    }, {
56232      key: 'seekToProgramTime',
56233      value: function seekToProgramTime$$1(programTime, callback) {
56234        var pauseAfterSeek = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : true;
56235        var retryCount = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : 2;
56236        return seekToProgramTime({
56237          programTime: programTime,
56238          playlist: this.masterPlaylistController_.media(),
56239          retryCount: retryCount,
56240          pauseAfterSeek: pauseAfterSeek,
56241          seekTo: this.options_.seekTo,
56242          tech: this.options_.tech,
56243          callback: callback
56244        });
56245      }
56246    }]);
56247    return HlsHandler;
56248  }(Component$1);
56249  /**
56250   * The Source Handler object, which informs video.js what additional
56251   * MIME types are supported and sets up playback. It is registered
56252   * automatically to the appropriate tech based on the capabilities of
56253   * the browser it is running in. It is not necessary to use or modify
56254   * this object in normal usage.
56255   */
56256
56257
56258  var HlsSourceHandler = {
56259    name: 'videojs-http-streaming',
56260    VERSION: version$1,
56261    canHandleSource: function canHandleSource(srcObj) {
56262      var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
56263      var localOptions = videojs$1.mergeOptions(videojs$1.options, options);
56264      return HlsSourceHandler.canPlayType(srcObj.type, localOptions);
56265    },
56266    handleSource: function handleSource(source, tech) {
56267      var options = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
56268      var localOptions = videojs$1.mergeOptions(videojs$1.options, options);
56269      tech.hls = new HlsHandler(source, tech, localOptions);
56270      tech.hls.xhr = xhrFactory();
56271      tech.hls.src(source.src, source.type);
56272      return tech.hls;
56273    },
56274    canPlayType: function canPlayType(type) {
56275      var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
56276
56277      var _videojs$mergeOptions = videojs$1.mergeOptions(videojs$1.options, options),
56278          overrideNative = _videojs$mergeOptions.hls.overrideNative;
56279
56280      var supportedType = simpleTypeFromSourceType(type);
56281      var canUseMsePlayback = supportedType && (!Hls$1.supportsTypeNatively(supportedType) || overrideNative);
56282      return canUseMsePlayback ? 'maybe' : '';
56283    }
56284  };
56285
56286  if (typeof videojs$1.MediaSource === 'undefined' || typeof videojs$1.URL === 'undefined') {
56287    videojs$1.MediaSource = MediaSource;
56288    videojs$1.URL = URL$1;
56289  } // register source handlers with the appropriate techs
56290
56291
56292  if (MediaSource.supportsNativeMediaSources()) {
56293    videojs$1.getTech('Html5').registerSourceHandler(HlsSourceHandler, 0);
56294  }
56295
56296  videojs$1.HlsHandler = HlsHandler;
56297  videojs$1.HlsSourceHandler = HlsSourceHandler;
56298  videojs$1.Hls = Hls$1;
56299
56300  if (!videojs$1.use) {
56301    videojs$1.registerComponent('Hls', Hls$1);
56302  }
56303
56304  videojs$1.options.hls = videojs$1.options.hls || {};
56305
56306  if (videojs$1.registerPlugin) {
56307    videojs$1.registerPlugin('reloadSourceOnError', reloadSourceOnError);
56308  } else {
56309    videojs$1.plugin('reloadSourceOnError', reloadSourceOnError);
56310  }
56311
56312  return videojs$1;
56313
56314}));

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.