PageSourceSearch

https://udprf.ru/sites/all/libraries/video-js/video.js?swcj8p

js udprf.ru collected 2026-10-02 03:41:41 UTC 688,621 bytes, 24,835 lines download raw bytes

vendor: 15,376 bytes, lines 1-610
1/**
2 * @license
3 * Video.js 6.2.8 <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() :
15	typeof define === 'function' && define.amd ? define(factory) :
16	(global.videojs = factory());
17}(this, (function () {
18
19var version = "6.2.8";
20
21var commonjsGlobal = typeof window !== 'undefined' ? window : typeof global !== 'undefined' ? global : typeof self !== 'undefined' ? self : {};
22
23
24
25
26
27function createCommonjsModule(fn, module) {
28	return module = { exports: {} }, fn(module, module.exports), module.exports;
29}
30
31var win;
32
33if (typeof window !== "undefined") {
34    win = window;
35} else if (typeof commonjsGlobal !== "undefined") {
36    win = commonjsGlobal;
37} else if (typeof self !== "undefined"){
38    win = self;
39} else {
40    win = {};
41}
42
43var window_1 = win;
44
45var empty = {};
46
47
48var empty$1 = (Object.freeze || Object)({
49	'default': empty
50});
51
52var minDoc = ( empty$1 && empty ) || empty$1;
53
54var topLevel = typeof commonjsGlobal !== 'undefined' ? commonjsGlobal :
55    typeof window !== 'undefined' ? window : {};
56
57
58var doccy;
59
60if (typeof document !== 'undefined') {
61    doccy = document;
62} else {
63    doccy = topLevel['__GLOBAL_DOCUMENT_CACHE@4'];
64
65    if (!doccy) {
66        doccy = topLevel['__GLOBAL_DOCUMENT_CACHE@4'] = minDoc;
67    }
68}
69
70var document_1 = doccy;
71
72/**
73 * @file browser.js
74 * @module browser
75 */
76var USER_AGENT = window_1.navigator && window_1.navigator.userAgent || '';
77var webkitVersionMap = /AppleWebKit\/([\d.]+)/i.exec(USER_AGENT);
78var appleWebkitVersion = webkitVersionMap ? parseFloat(webkitVersionMap.pop()) : null;
79
80/*
81 * Device is an iPhone
82 *
83 * @type {Boolean}
84 * @constant
85 * @private
86 */
87var IS_IPAD = /iPad/i.test(USER_AGENT);
88
89// The Facebook app's UIWebView identifies as both an iPhone and iPad, so
90// to identify iPhones, we need to exclude iPads.
91// http://artsy.github.io/blog/2012/10/18/the-perils-of-ios-user-agent-sniffing/
92var IS_IPHONE = /iPhone/i.test(USER_AGENT) && !IS_IPAD;
93var IS_IPOD = /iPod/i.test(USER_AGENT);
94var IS_IOS = IS_IPHONE || IS_IPAD || IS_IPOD;
95
96var IOS_VERSION = function () {
97  var match = USER_AGENT.match(/OS (\d+)_/i);
98
99  if (match && match[1]) {
100    return match[1];
101  }
102  return null;
103}();
104
105var IS_ANDROID = /Android/i.test(USER_AGENT);
106var ANDROID_VERSION = function () {
107  // This matches Android Major.Minor.Patch versions
108  // ANDROID_VERSION is Major.Minor as a Number, if Minor isn't available, then only Major is returned
109  var match = USER_AGENT.match(/Android (\d+)(?:\.(\d+))?(?:\.(\d+))*/i);
110
111  if (!match) {
112    return null;
113  }
114
115  var major = match[1] && parseFloat(match[1]);
116  var minor = match[2] && parseFloat(match[2]);
117
118  if (major && minor) {
119    return parseFloat(match[1] + '.' + match[2]);
120  } else if (major) {
121    return major;
122  }
123  return null;
124}();
125
126// Old Android is defined as Version older than 2.3, and requiring a webkit version of the android browser
127var IS_OLD_ANDROID = IS_ANDROID && /webkit/i.test(USER_AGENT) && ANDROID_VERSION < 2.3;
128var IS_NATIVE_ANDROID = IS_ANDROID && ANDROID_VERSION < 5 && appleWebkitVersion < 537;
129
130var IS_FIREFOX = /Firefox/i.test(USER_AGENT);
131var IS_EDGE = /Edge/i.test(USER_AGENT);
132var IS_CHROME = !IS_EDGE && /Chrome/i.test(USER_AGENT);
133var CHROME_VERSION = function () {
134  var match = USER_AGENT.match(/Chrome\/(\d+)/);
135
136  if (match && match[1]) {
137    return parseFloat(match[1]);
138  }
139  return null;
140}();
141var IS_IE8 = /MSIE\s8\.0/.test(USER_AGENT);
142var IE_VERSION = function () {
143  var result = /MSIE\s(\d+)\.\d/.exec(USER_AGENT);
144  var version = result && parseFloat(result[1]);
145
146  if (!version && /Trident\/7.0/i.test(USER_AGENT) && /rv:11.0/.test(USER_AGENT)) {
147    // IE 11 has a different user agent string than other IE versions
148    version = 11.0;
149  }
150
151  return version;
152}();
153
154var IS_SAFARI = /Safari/i.test(USER_AGENT) && !IS_CHROME && !IS_ANDROID && !IS_EDGE;
155var IS_ANY_SAFARI = IS_SAFARI || IS_IOS;
156
157var TOUCH_ENABLED = isReal() && ('ontouchstart' in window_1 || window_1.DocumentTouch && window_1.document instanceof window_1.DocumentTouch);
158
159var BACKGROUND_SIZE_SUPPORTED = isReal() && 'backgroundSize' in window_1.document.createElement('video').style;
160
161var browser = (Object.freeze || Object)({
162	IS_IPAD: IS_IPAD,
163	IS_IPHONE: IS_IPHONE,
164	IS_IPOD: IS_IPOD,
165	IS_IOS: IS_IOS,
166	IOS_VERSION: IOS_VERSION,
167	IS_ANDROID: IS_ANDROID,
168	ANDROID_VERSION: ANDROID_VERSION,
169	IS_OLD_ANDROID: IS_OLD_ANDROID,
170	IS_NATIVE_ANDROID: IS_NATIVE_ANDROID,
171	IS_FIREFOX: IS_FIREFOX,
172	IS_EDGE: IS_EDGE,
173	IS_CHROME: IS_CHROME,
174	CHROME_VERSION: CHROME_VERSION,
175	IS_IE8: IS_IE8,
176	IE_VERSION: IE_VERSION,
177	IS_SAFARI: IS_SAFARI,
178	IS_ANY_SAFARI: IS_ANY_SAFARI,
179	TOUCH_ENABLED: TOUCH_ENABLED,
180	BACKGROUND_SIZE_SUPPORTED: BACKGROUND_SIZE_SUPPORTED
181});
182
183var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) {
184  return typeof obj;
185} : function (obj) {
186  return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj;
187};
188
189
190
191
192
193
194
195
196
197
198
199var classCallCheck = function (instance, Constructor) {
200  if (!(instance instanceof Constructor)) {
201    throw new TypeError("Cannot call a class as a function");
202  }
203};
204
205
206
207
208
209
210
211
212
213
214
215var inherits = function (subClass, superClass) {
216  if (typeof superClass !== "function" && superClass !== null) {
217    throw new TypeError("Super expression must either be null or a function, not " + typeof superClass);
218  }
219
220  subClass.prototype = Object.create(superClass && superClass.prototype, {
221    constructor: {
222      value: subClass,
223      enumerable: false,
224      writable: true,
225      configurable: true
226    }
227  });
228  if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass;
229};
230
231
232
233
234
235
236
237
238
239
240
241var possibleConstructorReturn = function (self, call) {
242  if (!self) {
243    throw new ReferenceError("this hasn't been initialised - super() hasn't been called");
244  }
245
246  return call && (typeof call === "object" || typeof call === "function") ? call : self;
247};
248
249
250
251
252
253
254
255
256
257
258
259var taggedTemplateLiteralLoose = function (strings, raw) {
260  strings.raw = raw;
261  return strings;
262};
263
264/**
265 * @file obj.js
266 * @module obj
267 */
268
269/**
270 * @callback obj:EachCallback
271 *
272 * @param {Mixed} value
273 *        The current key for the object that is being iterated over.
274 *
275 * @param {string} key
276 *        The current key-value for object that is being iterated over
277 */
278
279/**
280 * @callback obj:ReduceCallback
281 *
282 * @param {Mixed} accum
283 *        The value that is accumulating over the reduce loop.
284 *
285 * @param {Mixed} value
286 *        The current key for the object that is being iterated over.
287 *
288 * @param {string} key
289 *        The current key-value for object that is being iterated over
290 *
291 * @return {Mixed}
292 *         The new accumulated value.
293 */
294var toString = Object.prototype.toString;
295
296/**
297 * Get the keys of an Object
298 *
299 * @param {Object}
300 *        The Object to get the keys from
301 *
302 * @return {string[]}
303 *         An array of the keys from the object. Returns an empty array if the
304 *         object passed in was invalid or had no keys.
305 *
306 * @private
307 */
308var keys = function keys(object) {
309  return isObject(object) ? Object.keys(object) : [];
310};
311
312/**
313 * Array-like iteration for objects.
314 *
315 * @param {Object} object
316 *        The object to iterate over
317 *
318 * @param {obj:EachCallback} fn
319 *        The callback function which is called for each key in the object.
320 */
321function each(object, fn) {
322  keys(object).forEach(function (key) {
323    return fn(object[key], key);
324  });
325}
326
327/**
328 * Array-like reduce for objects.
329 *
330 * @param {Object} object
331 *        The Object that you want to reduce.
332 *
333 * @param {Function} fn
334 *         A callback function which is called for each key in the object. It
335 *         receives the accumulated value and the per-iteration value and key
336 *         as arguments.
337 *
338 * @param {Mixed} [initial = 0]
339 *        Starting value
340 *
341 * @return {Mixed}
342 *         The final accumulated value.
343 */
344function reduce(object, fn) {
345  var initial = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 0;
346
347  return keys(object).reduce(function (accum, key) {
348    return fn(accum, object[key], key);
349  }, initial);
350}
351
352/**
353 * Object.assign-style object shallow merge/extend.
354 *
355 * @param  {Object} target
356 * @param  {Object} ...sources
357 * @return {Object}
358 */
359function assign(target) {
360  for (var _len = arguments.length, sources = Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {
361    sources[_key - 1] = arguments[_key];
362  }
363
364  if (Object.assign) {
365    return Object.assign.apply(Object, [target].concat(sources));
366  }
367
368  sources.forEach(function (source) {
369    if (!source) {
370      return;
371    }
372
373    each(source, function (value, key) {
374      target[key] = value;
375    });
376  });
377
378  return target;
379}
380
381/**
382 * Returns whether a value is an object of any kind - including DOM nodes,
383 * arrays, regular expressions, etc. Not functions, though.
384 *
385 * This avoids the gotcha where using `typeof` on a `null` value
386 * results in `'object'`.
387 *
388 * @param  {Object} value
389 * @return {Boolean}
390 */
391function isObject(value) {
392  return !!value && (typeof value === 'undefined' ? 'undefined' : _typeof(value)) === 'object';
393}
394
395/**
396 * Returns whether an object appears to be a "plain" object - that is, a
397 * direct instance of `Object`.
398 *
399 * @param  {Object} value
400 * @return {Boolean}
401 */
402function isPlain(value) {
403  return isObject(value) && toString.call(value) === '[object Object]' && value.constructor === Object;
404}
405
406/**
407 * @file log.js
408 * @module log
409 */
410var log = void 0;
411
412// This is the private tracking variable for logging level.
413var level = 'all';
414
415// This is the private tracking variable for the logging history.
416var history = [];
417
418/**
419 * Log messages to the console and history based on the type of message
420 *
421 * @private
422 * @param  {string} type
423 *         The name of the console method to use.
424 *
425 * @param  {Array} args
426 *         The arguments to be passed to the matching console method.
427 *
428 * @param  {boolean} [stringify]
429 *         By default, only old IEs should get console argument stringification,
430 *         but this is exposed as a parameter to facilitate testing.
431 */
432var logByType = function logByType(type, args) {
433  var stringify = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : !!IE_VERSION && IE_VERSION < 11;
434
435  var lvl = log.levels[level];
436  var lvlRegExp = new RegExp('^(' + lvl + ')$');
437
438  if (type !== 'log') {
439
440    // Add the type to the front of the message when it's not "log".
441    args.unshift(type.toUpperCase() + ':');
442  }
443
444  // Add a clone of the args at this point to history.
445  if (history) {
446    history.push([].concat(args));
447  }
448
449  // Add console prefix after adding to history.
450  args.unshift('VIDEOJS:');
451
452  // If there's no console then don't try to output messages, but they will
453  // still be stored in history.
454  //
455  // Was setting these once outside of this function, but containing them
456  // in the function makes it easier to test cases where console doesn't exist
457  // when the module is executed.
458  var fn = window_1.console && window_1.console[type];
459
460  // Bail out if there's no console or if this type is not allowed by the
461  // current logging level.
462  if (!fn || !lvl || !lvlRegExp.test(type)) {
463    return;
464  }
465
466  // IEs previous to 11 log objects uselessly as "[object Object]"; so, JSONify
467  // objects and arrays for those less-capable browsers.
468  if (stringify) {
469    args = args.map(function (a) {
470      if (isObject(a) || Array.isArray(a)) {
471        try {
472          return JSON.stringify(a);
473        } catch (x) {
474          return String(a);
475        }
476      }
477
478      // Cast to string before joining, so we get null and undefined explicitly
479      // included in output (as we would in a modern console).
480      return String(a);
481    }).join(' ');
482  }
483
484  // Old IE versions do not allow .apply() for console methods (they are
485  // reported as objects rather than functions).
486  if (!fn.apply) {
487    fn(args);
488  } else {
489    fn[Array.isArray(args) ? 'apply' : 'call'](window_1.console, args);
490  }
491};
492
493/**
494 * Logs plain debug messages. Similar to `console.log`.
495 *
496 * @class
497 * @param    {Mixed[]} args
498 *           One or more messages or objects that should be logged.
499 */
500log = function log() {
501  for (var _len = arguments.length, args = Array(_len), _key = 0; _key < _len; _key++) {
502    args[_key] = arguments[_key];
503  }
504
505  logByType('log', args);
506};
507
508/**
509 * Enumeration of available logging levels, where the keys are the level names
510 * and the values are `|`-separated strings containing logging methods allowed
511 * in that logging level. These strings are used to create a regular expression
512 * matching the function name being called.
513 *
514 * Levels provided by video.js are:
515 *
516 * - `off`: Matches no calls. Any value that can be cast to `false` will have
517 *   this effect. The most restrictive.
518 * - `all` (default): Matches only Video.js-provided functions (`log`,
519 *   `log.warn`, and `log.error`).
520 * - `warn`: Matches `log.warn` and `log.error` calls.
521 * - `error`: Matches only `log.error` calls.
522 *
523 * @type {Object}
524 */
525log.levels = {
526  all: 'log|warn|error',
527  error: 'error',
528  off: '',
529  warn: 'warn|error',
530  DEFAULT: level
531};
532
533/**
534 * Get or set the current logging level. If a string matching a key from
535 * {@link log.levels} is provided, acts as a setter. Regardless of argument,
536 * returns the current logging level.
537 *
538 * @param  {string} [lvl]
539 *         Pass to set a new logging level.
540 *
541 * @return {string}
542 *         The current logging level.
543 */
544log.level = function (lvl) {
545  if (typeof lvl === 'string') {
546    if (!log.levels.hasOwnProperty(lvl)) {
547      throw new Error('"' + lvl + '" in not a valid log level');
548    }
549    level = lvl;
550  }
551  return level;
552};
553
554/**
555 * Returns an array containing everything that has been logged to the history.
556 *
557 * This array is a shallow clone of the internal history record. However, its
558 * contents are _not_ cloned; so, mutating objects inside this array will
559 * mutate them in history.
560 *
561 * @return {Array}
562 */
563log.history = function () {
564  return history ? [].concat(history) : [];
565};
566
567/**
568 * Clears the internal history tracking, but does not prevent further history
569 * tracking.
570 */
571log.history.clear = function () {
572  if (history) {
573    history.length = 0;
574  }
575};
576
577/**
578 * Disable history tracking if it is currently enabled.
579 */
580log.history.disable = function () {
581  if (history !== null) {
582    history.length = 0;
583    history = null;
584  }
585};
586
587/**
588 * Enable history tracking if it is currently disabled.
589 */
590log.history.enable = function () {
591  if (history === null) {
592    history = [];
593  }
594};
595
596/**
597 * Logs error messages. Similar to `console.error`.
598 *
599 * @param {Mixed[]} args
600 *        One or more messages or objects that should be logged as an error
601 */
602log.error = function () {
603  for (var _len2 = arguments.length, args = Array(_len2), _key2 = 0; _key2 < _len2; _key2++) {
604    args[_key2] = arguments[_key2];
605  }
606
607  return logByType('error', args);
608};
609
610/**
611 * Logs warning messages. Similar to `console.warn`.
612 *
613 * @param {Mixed[]} args
614 *        One or more messages or objects that should be logged as a warning.
615 */
616log.warn = function () {
617  for (var _len3 = arguments.length, args = Array(_len3), _key3 = 0; _key3 < _len3; _key3++) {
618    args[_key3] = arguments[_key3];
619  }
620
621  return logByType('warn', args);
622};
623
624var log$1 = log;
625
626function clean (s) {
627  return s.replace(/\n\r?\s*/g, '')
628}
629
630
631var tsml = function tsml (sa) {
632  var s = ''
633    , i = 0;
634
635  for (; i < arguments.length; i++)
636    s += clean(sa[i]) + (arguments[i + 1] || '');
637
638  return s
639};
640
641/**
642 * @file computed-style.js
643 * @module computed-style
644 */
645/**
646 * A safe getComputedStyle with an IE8 fallback.
647 *
648 * This is needed because in Firefox, if the player is loaded in an iframe with
649 * `display:none`, then `getComputedStyle` returns `null`, so, we do a null-check to
650 * make sure  that the player doesn't break in these cases.
651 *
652 * @param {Element} el
653 *        The element you want the computed style of
654 *
655 * @param {string} prop
656 *        The property name you want
657 *
658 * @see https://bugzilla.mozilla.org/show_bug.cgi?id=548397
659 *
660 * @static
661 * @const
662 */
663function computedStyle(el, prop) {
664  if (!el || !prop) {
665    return '';
666  }
667
668  if (typeof window_1.getComputedStyle === 'function') {
669    var cs = window_1.getComputedStyle(el);
670
671    return cs ? cs[prop] : '';
672  }
673
674  return el.currentStyle[prop] || '';
675}
676
677var _templateObject = taggedTemplateLiteralLoose(['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 ', ' to ', '.'], ['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 ', ' to ', '.']);
678
679/**
680 * @file dom.js
681 * @module dom
682 */
683/**
684 * Detect if a value is a string with any non-whitespace characters.
685 *
686 * @param {string} str
687 *        The string to check
688 *
689 * @return {boolean}
690 *         - True if the string is non-blank
691 *         - False otherwise
692 *
693 */
694function isNonBlankString(str) {
695  return typeof str === 'string' && /\S/.test(str);
696}
697
698/**
699 * Throws an error if the passed string has whitespace. This is used by
700 * class methods to be relatively consistent with the classList API.
701 *
702 * @param {string} str
703 *         The string to check for whitespace.
704 *
705 * @throws {Error}
706 *         Throws an error if there is whitespace in the string.
707 *
708 */
709function throwIfWhitespace(str) {
710  if (/\s/.test(str)) {
711    throw new Error('class has illegal whitespace characters');
712  }
713}
714
715/**
716 * Produce a regular expression for matching a className within an elements className.
717 *
718 * @param {string} className
719 *         The className to generate the RegExp for.
720 *
721 * @return {RegExp}
722 *         The RegExp that will check for a specific `className` in an elements
723 *         className.
724 */
725function classRegExp(className) {
726  return new RegExp('(^|\\s)' + className + '($|\\s)');
727}
728
729/**
730 * Whether the current DOM interface appears to be real.
731 *
732 * @return {Boolean}
733 */
734function isReal() {
735  return (
736
737    // Both document and window will never be undefined thanks to `global`.
738    document_1 === window_1.document &&
739
740    // In IE < 9, DOM methods return "object" as their type, so all we can
741    // confidently check is that it exists.
742    typeof document_1.createElement !== 'undefined'
743  );
744}
745
746/**
747 * Determines, via duck typing, whether or not a value is a DOM element.
748 *
749 * @param {Mixed} value
750 *        The thing to check
751 *
752 * @return {boolean}
753 *         - True if it is a DOM element
754 *         - False otherwise
755 */
756function isEl(value) {
757  return isObject(value) && value.nodeType === 1;
758}
759
760/**
761 * Creates functions to query the DOM using a given method.
762 *
763 * @param {string} method
764 *         The method to create the query with.
765 *
766 * @return {Function}
767 *         The query method
768 */
769function createQuerier(method) {
770  return function (selector, context) {
771    if (!isNonBlankString(selector)) {
772      return document_1[method](null);
773    }
774    if (isNonBlankString(context)) {
775      context = document_1.querySelector(context);
776    }
777
778    var ctx = isEl(context) ? context : document_1;
779
780    return ctx[method] && ctx[method](selector);
781  };
782}
783
784/**
785 * Creates an element and applies properties.
786 *
787 * @param {string} [tagName='div']
788 *         Name of tag to be created.
789 *
790 * @param {Object} [properties={}]
791 *         Element properties to be applied.
792 *
793 * @param {Object} [attributes={}]
794 *         Element attributes to be applied.
795 *
796 * @param {String|Element|TextNode|Array|Function} [content]
797 *         Contents for the element (see: {@link dom:normalizeContent})
798 *
799 * @return {Element}
800 *         The element that was created.
801 */
802function createEl() {
803  var tagName = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 'div';
804  var properties = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
805  var attributes = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
806  var content = arguments[3];
807
808  var el = document_1.createElement(tagName);
809
vendor: 4,609 bytes, lines 810-986
810  Object.getOwnPropertyNames(properties).forEach(function (propName) {
811    var val = properties[propName];
812
813    // See #2176
814    // We originally were accepting both properties and attributes in the
815    // same object, but that doesn't work so well.
816    if (propName.indexOf('aria-') !== -1 || propName === 'role' || propName === 'type') {
817      log$1.warn(tsml(_templateObject, propName, val));
818      el.setAttribute(propName, val);
819
820      // Handle textContent since it's not supported everywhere and we have a
821      // method for it.
822    } else if (propName === 'textContent') {
823      textContent(el, val);
824    } else {
825      el[propName] = val;
826    }
827  });
828
829  Object.getOwnPropertyNames(attributes).forEach(function (attrName) {
830    el.setAttribute(attrName, attributes[attrName]);
831  });
832
833  if (content) {
834    appendContent(el, content);
835  }
836
837  return el;
838}
839
840/**
841 * Injects text into an element, replacing any existing contents entirely.
842 *
843 * @param {Element} el
844 *        The element to add text content into
845 *
846 * @param {string} text
847 *        The text content to add.
848 *
849 * @return {Element}
850 *         The element with added text content.
851 */
852function textContent(el, text) {
853  if (typeof el.textContent === 'undefined') {
854    el.innerText = text;
855  } else {
856    el.textContent = text;
857  }
858  return el;
859}
860
861/**
862 * Insert an element as the first child node of another
863 *
864 * @param {Element} child
865 *        Element to insert
866 *
867 * @param {Element} parent
868 *        Element to insert child into
869 */
870function prependTo(child, parent) {
871  if (parent.firstChild) {
872    parent.insertBefore(child, parent.firstChild);
873  } else {
874    parent.appendChild(child);
875  }
876}
877
878/**
879 * Check if an element has a CSS class
880 *
881 * @param {Element} element
882 *        Element to check
883 *
884 * @param {string} classToCheck
885 *        Class name to check for
886 *
887 * @return {boolean}
888 *         - True if the element had the class
889 *         - False otherwise.
890 *
891 * @throws {Error}
892 *         Throws an error if `classToCheck` has white space.
893 */
894function hasClass(element, classToCheck) {
895  throwIfWhitespace(classToCheck);
896  if (element.classList) {
897    return element.classList.contains(classToCheck);
898  }
899  return classRegExp(classToCheck).test(element.className);
900}
901
902/**
903 * Add a CSS class name to an element
904 *
905 * @param {Element} element
906 *        Element to add class name to.
907 *
908 * @param {string} classToAdd
909 *        Class name to add.
910 *
911 * @return {Element}
912 *         The dom element with the added class name.
913 */
914function addClass(element, classToAdd) {
915  if (element.classList) {
916    element.classList.add(classToAdd);
917
918    // Don't need to `throwIfWhitespace` here because `hasElClass` will do it
919    // in the case of classList not being supported.
920  } else if (!hasClass(element, classToAdd)) {
921    element.className = (element.className + ' ' + classToAdd).trim();
922  }
923
924  return element;
925}
926
927/**
928 * Remove a CSS class name from an element
929 *
930 * @param {Element} element
931 *        Element to remove a class name from.
932 *
933 * @param {string} classToRemove
934 *        Class name to remove
935 *
936 * @return {Element}
937 *         The dom element with class name removed.
938 */
939function removeClass(element, classToRemove) {
940  if (element.classList) {
941    element.classList.remove(classToRemove);
942  } else {
943    throwIfWhitespace(classToRemove);
944    element.className = element.className.split(/\s+/).filter(function (c) {
945      return c !== classToRemove;
946    }).join(' ');
947  }
948
949  return element;
950}
951
952/**
953 * The callback definition for toggleElClass.
954 *
955 * @callback Dom~PredicateCallback
956 * @param {Element} element
957 *        The DOM element of the Component.
958 *
959 * @param {string} classToToggle
960 *        The `className` that wants to be toggled
961 *
962 * @return {boolean|undefined}
963 *         - If true the `classToToggle` will get added to `element`.
964 *         - If false the `classToToggle` will get removed from `element`.
965 *         - If undefined this callback will be ignored
966 */
967
968/**
969 * Adds or removes a CSS class name on an element depending on an optional
970 * condition or the presence/absence of the class name.
971 *
972 * @param {Element} element
973 *        The element to toggle a class name on.
974 *
975 * @param {string} classToToggle
976 *        The class that should be toggled
977 *
978 * @param {boolean|PredicateCallback} [predicate]
979 *        See the return value for {@link Dom~PredicateCallback}
980 *
981 * @return {Element}
982 *         The element with a class that has been toggled.
983 */
984function toggleClass(element, classToToggle, predicate) {
985
986  // This CANNOT use `classList` internally because IE does not support the
vendor: 5,424 bytes, lines 987-1195
987  // second parameter to the `classList.toggle()` method! Which is fine because
988  // `classList` will be used by the add/remove functions.
989  var has = hasClass(element, classToToggle);
990
991  if (typeof predicate === 'function') {
992    predicate = predicate(element, classToToggle);
993  }
994
995  if (typeof predicate !== 'boolean') {
996    predicate = !has;
997  }
998
999  // If the necessary class operation matches the current state of the
1000  // element, no action is required.
1001  if (predicate === has) {
1002    return;
1003  }
1004
1005  if (predicate) {
1006    addClass(element, classToToggle);
1007  } else {
1008    removeClass(element, classToToggle);
1009  }
1010
1011  return element;
1012}
1013
1014/**
1015 * Apply attributes to an HTML element.
1016 *
1017 * @param {Element} el
1018 *        Element to add attributes to.
1019 *
1020 * @param {Object} [attributes]
1021 *        Attributes to be applied.
1022 */
1023function setAttributes(el, attributes) {
1024  Object.getOwnPropertyNames(attributes).forEach(function (attrName) {
1025    var attrValue = attributes[attrName];
1026
1027    if (attrValue === null || typeof attrValue === 'undefined' || attrValue === false) {
1028      el.removeAttribute(attrName);
1029    } else {
1030      el.setAttribute(attrName, attrValue === true ? '' : attrValue);
1031    }
1032  });
1033}
1034
1035/**
1036 * Get an element's attribute values, as defined on the HTML tag
1037 * Attributes are not the same as properties. They're defined on the tag
1038 * or with setAttribute (which shouldn't be used with HTML)
1039 * This will return true or false for boolean attributes.
1040 *
1041 * @param {Element} tag
1042 *        Element from which to get tag attributes.
1043 *
1044 * @return {Object}
1045 *         All attributes of the element.
1046 */
1047function getAttributes(tag) {
1048  var obj = {};
1049
1050  // known boolean attributes
1051  // we can check for matching boolean properties, but older browsers
1052  // won't know about HTML5 boolean attributes that we still read from
1053  var knownBooleans = ',' + 'autoplay,controls,playsinline,loop,muted,default,defaultMuted' + ',';
1054
1055  if (tag && tag.attributes && tag.attributes.length > 0) {
1056    var attrs = tag.attributes;
1057
1058    for (var i = attrs.length - 1; i >= 0; i--) {
1059      var attrName = attrs[i].name;
1060      var attrVal = attrs[i].value;
1061
1062      // check for known booleans
1063      // the matching element property will return a value for typeof
1064      if (typeof tag[attrName] === 'boolean' || knownBooleans.indexOf(',' + attrName + ',') !== -1) {
1065        // the value of an included boolean attribute is typically an empty
1066        // string ('') which would equal false if we just check for a false value.
1067        // we also don't want support bad code like autoplay='false'
1068        attrVal = attrVal !== null ? true : false;
1069      }
1070
1071      obj[attrName] = attrVal;
1072    }
1073  }
1074
1075  return obj;
1076}
1077
1078/**
1079 * Get the value of an element's attribute
1080 *
1081 * @param {Element} el
1082 *        A DOM element
1083 *
1084 * @param {string} attribute
1085 *        Attribute to get the value of
1086 *
1087 * @return {string}
1088 *         value of the attribute
1089 */
1090function getAttribute(el, attribute) {
1091  return el.getAttribute(attribute);
1092}
1093
1094/**
1095 * Set the value of an element's attribute
1096 *
1097 * @param {Element} el
1098 *        A DOM element
1099 *
1100 * @param {string} attribute
1101 *        Attribute to set
1102 *
1103 * @param {string} value
1104 *        Value to set the attribute to
1105 */
1106function setAttribute(el, attribute, value) {
1107  el.setAttribute(attribute, value);
1108}
1109
1110/**
1111 * Remove an element's attribute
1112 *
1113 * @param {Element} el
1114 *        A DOM element
1115 *
1116 * @param {string} attribute
1117 *        Attribute to remove
1118 */
1119function removeAttribute(el, attribute) {
1120  el.removeAttribute(attribute);
1121}
1122
1123/**
1124 * Attempt to block the ability to select text while dragging controls
1125 */
1126function blockTextSelection() {
1127  document_1.body.focus();
1128  document_1.onselectstart = function () {
1129    return false;
1130  };
1131}
1132
1133/**
1134 * Turn off text selection blocking
1135 */
1136function unblockTextSelection() {
1137  document_1.onselectstart = function () {
1138    return true;
1139  };
1140}
1141
1142/**
1143 * Identical to the native `getBoundingClientRect` function, but ensures that
1144 * the method is supported at all (it is in all browsers we claim to support)
1145 * and that the element is in the DOM before continuing.
1146 *
1147 * This wrapper function also shims properties which are not provided by some
1148 * older browsers (namely, IE8).
1149 *
1150 * Additionally, some browsers do not support adding properties to a
1151 * `ClientRect`/`DOMRect` object; so, we shallow-copy it with the standard
1152 * properties (except `x` and `y` which are not widely supported). This helps
1153 * avoid implementations where keys are non-enumerable.
1154 *
1155 * @param  {Element} el
1156 *         Element whose `ClientRect` we want to calculate.
1157 *
1158 * @return {Object|undefined}
1159 *         Always returns a plain
1160 */
1161function getBoundingClientRect(el) {
1162  if (el && el.getBoundingClientRect && el.parentNode) {
1163    var rect = el.getBoundingClientRect();
1164    var result = {};
1165
1166    ['bottom', 'height', 'left', 'right', 'top', 'width'].forEach(function (k) {
1167      if (rect[k] !== undefined) {
1168        result[k] = rect[k];
1169      }
1170    });
1171
1172    if (!result.height) {
1173      result.height = parseFloat(computedStyle(el, 'height'));
1174    }
1175
1176    if (!result.width) {
1177      result.width = parseFloat(computedStyle(el, 'width'));
1178    }
1179
1180    return result;
1181  }
1182}
1183
1184/**
1185 * The postion of a DOM element on the page.
1186 *
1187 * @typedef {Object} module:dom~Position
1188 *
1189 * @property {number} left
1190 *           Pixels to the left
1191 *
1192 * @property {number} top
1193 *           Pixels on top
1194 */
1195
vendor: 18,409 bytes, lines 1196-1893
1196/**
1197 * Offset Left.
1198 * getBoundingClientRect technique from
1199 * John Resig
1200 *
1201 * @see http://ejohn.org/blog/getboundingclientrect-is-awesome/
1202 *
1203 * @param {Element} el
1204 *        Element from which to get offset
1205 *
1206 * @return {module:dom~Position}
1207 *         The position of the element that was passed in.
1208 */
1209function findPosition(el) {
1210  var box = void 0;
1211
1212  if (el.getBoundingClientRect && el.parentNode) {
1213    box = el.getBoundingClientRect();
1214  }
1215
1216  if (!box) {
1217    return {
1218      left: 0,
1219      top: 0
1220    };
1221  }
1222
1223  var docEl = document_1.documentElement;
1224  var body = document_1.body;
1225
1226  var clientLeft = docEl.clientLeft || body.clientLeft || 0;
1227  var scrollLeft = window_1.pageXOffset || body.scrollLeft;
1228  var left = box.left + scrollLeft - clientLeft;
1229
1230  var clientTop = docEl.clientTop || body.clientTop || 0;
1231  var scrollTop = window_1.pageYOffset || body.scrollTop;
1232  var top = box.top + scrollTop - clientTop;
1233
1234  // Android sometimes returns slightly off decimal values, so need to round
1235  return {
1236    left: Math.round(left),
1237    top: Math.round(top)
1238  };
1239}
1240
1241/**
1242 * x and y coordinates for a dom element or mouse pointer
1243 *
1244 * @typedef {Object} Dom~Coordinates
1245 *
1246 * @property {number} x
1247 *           x coordinate in pixels
1248 *
1249 * @property {number} y
1250 *           y coordinate in pixels
1251 */
1252
1253/**
1254 * Get pointer position in element
1255 * Returns an object with x and y coordinates.
1256 * The base on the coordinates are the bottom left of the element.
1257 *
1258 * @param {Element} el
1259 *        Element on which to get the pointer position on
1260 *
1261 * @param {EventTarget~Event} event
1262 *        Event object
1263 *
1264 * @return {Dom~Coordinates}
1265 *         A Coordinates object corresponding to the mouse position.
1266 *
1267 */
1268function getPointerPosition(el, event) {
1269  var position = {};
1270  var box = findPosition(el);
1271  var boxW = el.offsetWidth;
1272  var boxH = el.offsetHeight;
1273
1274  var boxY = box.top;
1275  var boxX = box.left;
1276  var pageY = event.pageY;
1277  var pageX = event.pageX;
1278
1279  if (event.changedTouches) {
1280    pageX = event.changedTouches[0].pageX;
1281    pageY = event.changedTouches[0].pageY;
1282  }
1283
1284  position.y = Math.max(0, Math.min(1, (boxY - pageY + boxH) / boxH));
1285  position.x = Math.max(0, Math.min(1, (pageX - boxX) / boxW));
1286
1287  return position;
1288}
1289
1290/**
1291 * Determines, via duck typing, whether or not a value is a text node.
1292 *
1293 * @param {Mixed} value
1294 *        Check if this value is a text node.
1295 *
1296 * @return {boolean}
1297 *         - True if it is a text node
1298 *         - False otherwise
1299 */
1300function isTextNode(value) {
1301  return isObject(value) && value.nodeType === 3;
1302}
1303
1304/**
1305 * Empties the contents of an element.
1306 *
1307 * @param {Element} el
1308 *        The element to empty children from
1309 *
1310 * @return {Element}
1311 *         The element with no children
1312 */
1313function emptyEl(el) {
1314  while (el.firstChild) {
1315    el.removeChild(el.firstChild);
1316  }
1317  return el;
1318}
1319
1320/**
1321 * Normalizes content for eventual insertion into the DOM.
1322 *
1323 * This allows a wide range of content definition methods, but protects
1324 * from falling into the trap of simply writing to `innerHTML`, which is
1325 * an XSS concern.
1326 *
1327 * The content for an element can be passed in multiple types and
1328 * combinations, whose behavior is as follows:
1329 *
1330 * @param {String|Element|TextNode|Array|Function} content
1331 *        - String: Normalized into a text node.
1332 *        - Element/TextNode: Passed through.
1333 *        - Array: A one-dimensional array of strings, elements, nodes, or functions
1334 *          (which return single strings, elements, or nodes).
1335 *        - Function: If the sole argument, is expected to produce a string, element,
1336 *          node, or array as defined above.
1337 *
1338 * @return {Array}
1339 *         All of the content that was passed in normalized.
1340 */
1341function normalizeContent(content) {
1342
1343  // First, invoke content if it is a function. If it produces an array,
1344  // that needs to happen before normalization.
1345  if (typeof content === 'function') {
1346    content = content();
1347  }
1348
1349  // Next up, normalize to an array, so one or many items can be normalized,
1350  // filtered, and returned.
1351  return (Array.isArray(content) ? content : [content]).map(function (value) {
1352
1353    // First, invoke value if it is a function to produce a new value,
1354    // which will be subsequently normalized to a Node of some kind.
1355    if (typeof value === 'function') {
1356      value = value();
1357    }
1358
1359    if (isEl(value) || isTextNode(value)) {
1360      return value;
1361    }
1362
1363    if (typeof value === 'string' && /\S/.test(value)) {
1364      return document_1.createTextNode(value);
1365    }
1366  }).filter(function (value) {
1367    return value;
1368  });
1369}
1370
1371/**
1372 * Normalizes and appends content to an element.
1373 *
1374 * @param {Element} el
1375 *        Element to append normalized content to.
1376 *
1377 *
1378 * @param {String|Element|TextNode|Array|Function} content
1379 *        See the `content` argument of {@link dom:normalizeContent}
1380 *
1381 * @return {Element}
1382 *         The element with appended normalized content.
1383 */
1384function appendContent(el, content) {
1385  normalizeContent(content).forEach(function (node) {
1386    return el.appendChild(node);
1387  });
1388  return el;
1389}
1390
1391/**
1392 * Normalizes and inserts content into an element; this is identical to
1393 * `appendContent()`, except it empties the element first.
1394 *
1395 * @param {Element} el
1396 *        Element to insert normalized content into.
1397 *
1398 * @param {String|Element|TextNode|Array|Function} content
1399 *        See the `content` argument of {@link dom:normalizeContent}
1400 *
1401 * @return {Element}
1402 *         The element with inserted normalized content.
1403 *
1404 */
1405function insertContent(el, content) {
1406  return appendContent(emptyEl(el), content);
1407}
1408
1409/**
1410 * Finds a single DOM element matching `selector` within the optional
1411 * `context` of another DOM element (defaulting to `document`).
1412 *
1413 * @param {string} selector
1414 *        A valid CSS selector, which will be passed to `querySelector`.
1415 *
1416 * @param {Element|String} [context=document]
1417 *        A DOM element within which to query. Can also be a selector
1418 *        string in which case the first matching element will be used
1419 *        as context. If missing (or no element matches selector), falls
1420 *        back to `document`.
1421 *
1422 * @return {Element|null}
1423 *         The element that was found or null.
1424 */
1425var $ = createQuerier('querySelector');
1426
1427/**
1428 * Finds a all DOM elements matching `selector` within the optional
1429 * `context` of another DOM element (defaulting to `document`).
1430 *
1431 * @param {string} selector
1432 *           A valid CSS selector, which will be passed to `querySelectorAll`.
1433 *
1434 * @param {Element|String} [context=document]
1435 *           A DOM element within which to query. Can also be a selector
1436 *           string in which case the first matching element will be used
1437 *           as context. If missing (or no element matches selector), falls
1438 *           back to `document`.
1439 *
1440 * @return {NodeList}
1441 *         A element list of elements that were found. Will be empty if none were found.
1442 *
1443 */
1444var $$ = createQuerier('querySelectorAll');
1445
1446
1447
1448var Dom = (Object.freeze || Object)({
1449	isReal: isReal,
1450	isEl: isEl,
1451	createEl: createEl,
1452	textContent: textContent,
1453	prependTo: prependTo,
1454	hasClass: hasClass,
1455	addClass: addClass,
1456	removeClass: removeClass,
1457	toggleClass: toggleClass,
1458	setAttributes: setAttributes,
1459	getAttributes: getAttributes,
1460	getAttribute: getAttribute,
1461	setAttribute: setAttribute,
1462	removeAttribute: removeAttribute,
1463	blockTextSelection: blockTextSelection,
1464	unblockTextSelection: unblockTextSelection,
1465	getBoundingClientRect: getBoundingClientRect,
1466	findPosition: findPosition,
1467	getPointerPosition: getPointerPosition,
1468	isTextNode: isTextNode,
1469	emptyEl: emptyEl,
1470	normalizeContent: normalizeContent,
1471	appendContent: appendContent,
1472	insertContent: insertContent,
1473	$: $,
1474	$$: $$
1475});
1476
1477/**
1478 * @file guid.js
1479 * @module guid
1480 */
1481
1482/**
1483 * Unique ID for an element or function
1484 * @type {Number}
1485 */
1486var _guid = 1;
1487
1488/**
1489 * Get a unique auto-incrementing ID by number that has not been returned before.
1490 *
1491 * @return {number}
1492 *         A new unique ID.
1493 */
1494function newGUID() {
1495  return _guid++;
1496}
1497
1498/**
1499 * @file dom-data.js
1500 * @module dom-data
1501 */
1502/**
1503 * Element Data Store.
1504 *
1505 * Allows for binding data to an element without putting it directly on the
1506 * element. Ex. Event listeners are stored here.
1507 * (also from jsninja.com, slightly modified and updated for closure compiler)
1508 *
1509 * @type {Object}
1510 * @private
1511 */
1512var elData = {};
1513
1514/*
1515 * Unique attribute name to store an element's guid in
1516 *
1517 * @type {String}
1518 * @constant
1519 * @private
1520 */
1521var elIdAttr = 'vdata' + new Date().getTime();
1522
1523/**
1524 * Returns the cache object where data for an element is stored
1525 *
1526 * @param {Element} el
1527 *        Element to store data for.
1528 *
1529 * @return {Object}
1530 *         The cache object for that el that was passed in.
1531 */
1532function getData(el) {
1533  var id = el[elIdAttr];
1534
1535  if (!id) {
1536    id = el[elIdAttr] = newGUID();
1537  }
1538
1539  if (!elData[id]) {
1540    elData[id] = {};
1541  }
1542
1543  return elData[id];
1544}
1545
1546/**
1547 * Returns whether or not an element has cached data
1548 *
1549 * @param {Element} el
1550 *        Check if this element has cached data.
1551 *
1552 * @return {boolean}
1553 *         - True if the DOM element has cached data.
1554 *         - False otherwise.
1555 */
1556function hasData(el) {
1557  var id = el[elIdAttr];
1558
1559  if (!id) {
1560    return false;
1561  }
1562
1563  return !!Object.getOwnPropertyNames(elData[id]).length;
1564}
1565
1566/**
1567 * Delete data for the element from the cache and the guid attr from getElementById
1568 *
1569 * @param {Element} el
1570 *        Remove cached data for this element.
1571 */
1572function removeData(el) {
1573  var id = el[elIdAttr];
1574
1575  if (!id) {
1576    return;
1577  }
1578
1579  // Remove all stored data
1580  delete elData[id];
1581
1582  // Remove the elIdAttr property from the DOM node
1583  try {
1584    delete el[elIdAttr];
1585  } catch (e) {
1586    if (el.removeAttribute) {
1587      el.removeAttribute(elIdAttr);
1588    } else {
1589      // IE doesn't appear to support removeAttribute on the document element
1590      el[elIdAttr] = null;
1591    }
1592  }
1593}
1594
1595/**
1596 * @file events.js. An Event System (John Resig - Secrets of a JS Ninja http://jsninja.com/)
1597 * (Original book version wasn't completely usable, so fixed some things and made Closure Compiler compatible)
1598 * This should work very similarly to jQuery's events, however it's based off the book version which isn't as
1599 * robust as jquery's, so there's probably some differences.
1600 *
1601 * @module events
1602 */
1603
1604/**
1605 * Clean up the listener cache and dispatchers
1606 *
1607 * @param {Element|Object} elem
1608 *        Element to clean up
1609 *
1610 * @param {string} type
1611 *        Type of event to clean up
1612 */
1613function _cleanUpEvents(elem, type) {
1614  var data = getData(elem);
1615
1616  // Remove the events of a particular type if there are none left
1617  if (data.handlers[type].length === 0) {
1618    delete data.handlers[type];
1619    // data.handlers[type] = null;
1620    // Setting to null was causing an error with data.handlers
1621
1622    // Remove the meta-handler from the element
1623    if (elem.removeEventListener) {
1624      elem.removeEventListener(type, data.dispatcher, false);
1625    } else if (elem.detachEvent) {
1626      elem.detachEvent('on' + type, data.dispatcher);
1627    }
1628  }
1629
1630  // Remove the events object if there are no types left
1631  if (Object.getOwnPropertyNames(data.handlers).length <= 0) {
1632    delete data.handlers;
1633    delete data.dispatcher;
1634    delete data.disabled;
1635  }
1636
1637  // Finally remove the element data if there is no data left
1638  if (Object.getOwnPropertyNames(data).length === 0) {
1639    removeData(elem);
1640  }
1641}
1642
1643/**
1644 * Loops through an array of event types and calls the requested method for each type.
1645 *
1646 * @param {Function} fn
1647 *        The event method we want to use.
1648 *
1649 * @param {Element|Object} elem
1650 *        Element or object to bind listeners to
1651 *
1652 * @param {string} type
1653 *        Type of event to bind to.
1654 *
1655 * @param {EventTarget~EventListener} callback
1656 *        Event listener.
1657 */
1658function _handleMultipleEvents(fn, elem, types, callback) {
1659  types.forEach(function (type) {
1660    // Call the event method for each one of the types
1661    fn(elem, type, callback);
1662  });
1663}
1664
1665/**
1666 * Fix a native event to have standard property values
1667 *
1668 * @param {Object} event
1669 *        Event object to fix.
1670 *
1671 * @return {Object}
1672 *         Fixed event object.
1673 */
1674function fixEvent(event) {
1675
1676  function returnTrue() {
1677    return true;
1678  }
1679
1680  function returnFalse() {
1681    return false;
1682  }
1683
1684  // Test if fixing up is needed
1685  // Used to check if !event.stopPropagation instead of isPropagationStopped
1686  // But native events return true for stopPropagation, but don't have
1687  // other expected methods like isPropagationStopped. Seems to be a problem
1688  // with the Javascript Ninja code. So we're just overriding all events now.
1689  if (!event || !event.isPropagationStopped) {
1690    var old = event || window_1.event;
1691
1692    event = {};
1693    // Clone the old object so that we can modify the values event = {};
1694    // IE8 Doesn't like when you mess with native event properties
1695    // Firefox returns false for event.hasOwnProperty('type') and other props
1696    //  which makes copying more difficult.
1697    // TODO: Probably best to create a whitelist of event props
1698    for (var key in old) {
1699      // Safari 6.0.3 warns you if you try to copy deprecated layerX/Y
1700      // Chrome warns you if you try to copy deprecated keyboardEvent.keyLocation
1701      // and webkitMovementX/Y
1702      if (key !== 'layerX' && key !== 'layerY' && key !== 'keyLocation' && key !== 'webkitMovementX' && key !== 'webkitMovementY') {
1703        // Chrome 32+ warns if you try to copy deprecated returnValue, but
1704        // we still want to if preventDefault isn't supported (IE8).
1705        if (!(key === 'returnValue' && old.preventDefault)) {
1706          event[key] = old[key];
1707        }
1708      }
1709    }
1710
1711    // The event occurred on this element
1712    if (!event.target) {
1713      event.target = event.srcElement || document_1;
1714    }
1715
1716    // Handle which other element the event is related to
1717    if (!event.relatedTarget) {
1718      event.relatedTarget = event.fromElement === event.target ? event.toElement : event.fromElement;
1719    }
1720
1721    // Stop the default browser action
1722    event.preventDefault = function () {
1723      if (old.preventDefault) {
1724        old.preventDefault();
1725      }
1726      event.returnValue = false;
1727      old.returnValue = false;
1728      event.defaultPrevented = true;
1729    };
1730
1731    event.defaultPrevented = false;
1732
1733    // Stop the event from bubbling
1734    event.stopPropagation = function () {
1735      if (old.stopPropagation) {
1736        old.stopPropagation();
1737      }
1738      event.cancelBubble = true;
1739      old.cancelBubble = true;
1740      event.isPropagationStopped = returnTrue;
1741    };
1742
1743    event.isPropagationStopped = returnFalse;
1744
1745    // Stop the event from bubbling and executing other handlers
1746    event.stopImmediatePropagation = function () {
1747      if (old.stopImmediatePropagation) {
1748        old.stopImmediatePropagation();
1749      }
1750      event.isImmediatePropagationStopped = returnTrue;
1751      event.stopPropagation();
1752    };
1753
1754    event.isImmediatePropagationStopped = returnFalse;
1755
1756    // Handle mouse position
1757    if (event.clientX !== null && event.clientX !== undefined) {
1758      var doc = document_1.documentElement;
1759      var body = document_1.body;
1760
1761      event.pageX = event.clientX + (doc && doc.scrollLeft || body && body.scrollLeft || 0) - (doc && doc.clientLeft || body && body.clientLeft || 0);
1762      event.pageY = event.clientY + (doc && doc.scrollTop || body && body.scrollTop || 0) - (doc && doc.clientTop || body && body.clientTop || 0);
1763    }
1764
1765    // Handle key presses
1766    event.which = event.charCode || event.keyCode;
1767
1768    // Fix button for mouse clicks:
1769    // 0 == left; 1 == middle; 2 == right
1770    if (event.button !== null && event.button !== undefined) {
1771
1772      // The following is disabled because it does not pass videojs-standard
1773      // and... yikes.
1774      /* eslint-disable */
1775      event.button = event.button & 1 ? 0 : event.button & 4 ? 1 : event.button & 2 ? 2 : 0;
1776      /* eslint-enable */
1777    }
1778  }
1779
1780  // Returns fixed-up instance
1781  return event;
1782}
1783
1784/**
1785 * Whether passive event listeners are supported
1786 */
1787var _supportsPassive = false;
1788
1789(function () {
1790  try {
1791    var opts = Object.defineProperty({}, 'passive', {
1792      get: function get() {
1793        _supportsPassive = true;
1794      }
1795    });
1796
1797    window_1.addEventListener('test', null, opts);
1798  } catch (e) {
1799    // disregard
1800  }
1801})();
1802
1803/**
1804 * Touch events Chrome expects to be passive
1805 */
1806var passiveEvents = ['touchstart', 'touchmove'];
1807
1808/**
1809 * Add an event listener to element
1810 * It stores the handler function in a separate cache object
1811 * and adds a generic handler to the element's event,
1812 * along with a unique id (guid) to the element.
1813 *
1814 * @param {Element|Object} elem
1815 *        Element or object to bind listeners to
1816 *
1817 * @param {string|string[]} type
1818 *        Type of event to bind to.
1819 *
1820 * @param {EventTarget~EventListener} fn
1821 *        Event listener.
1822 */
1823function on(elem, type, fn) {
1824  if (Array.isArray(type)) {
1825    return _handleMultipleEvents(on, elem, type, fn);
1826  }
1827
1828  var data = getData(elem);
1829
1830  // We need a place to store all our handler data
1831  if (!data.handlers) {
1832    data.handlers = {};
1833  }
1834
1835  if (!data.handlers[type]) {
1836    data.handlers[type] = [];
1837  }
1838
1839  if (!fn.guid) {
1840    fn.guid = newGUID();
1841  }
1842
1843  data.handlers[type].push(fn);
1844
1845  if (!data.dispatcher) {
1846    data.disabled = false;
1847
1848    data.dispatcher = function (event, hash) {
1849
1850      if (data.disabled) {
1851        return;
1852      }
1853
1854      event = fixEvent(event);
1855
1856      var handlers = data.handlers[event.type];
1857
1858      if (handlers) {
1859        // Copy handlers so if handlers are added/removed during the process it doesn't throw everything off.
1860        var handlersCopy = handlers.slice(0);
1861
1862        for (var m = 0, n = handlersCopy.length; m < n; m++) {
1863          if (event.isImmediatePropagationStopped()) {
1864            break;
1865          } else {
1866            try {
1867              handlersCopy[m].call(elem, event, hash);
1868            } catch (e) {
1869              log$1.error(e);
1870            }
1871          }
1872        }
1873      }
1874    };
1875  }
1876
1877  if (data.handlers[type].length === 1) {
1878    if (elem.addEventListener) {
1879      var options = false;
1880
1881      if (_supportsPassive && passiveEvents.indexOf(type) > -1) {
1882        options = { passive: true };
1883      }
1884      elem.addEventListener(type, data.dispatcher, options);
1885    } else if (elem.attachEvent) {
1886      elem.attachEvent('on' + type, data.dispatcher);
1887    }
1888  }
1889}
1890
1891/**
1892 * Removes event listeners from an element
1893 *
1894 * @param {Element|Object} elem
1895 *        Object to remove listeners from.
1896 *
1897 * @param {string|string[]} [type]
1898 *        Type of listener to remove. Don't include to remove all events from element.
1899 *
1900 * @param {EventTarget~EventListener} [fn]
1901 *        Specific listener to remove. Don't include to remove listeners for an event
1902 *        type.
1903 */
1904function off(elem, type, fn) {
1905  // Don't want to add a cache object through getElData if not needed
1906  if (!hasData(elem)) {
1907    return;
1908  }
1909
1910  var data = getData(elem);
1911
1912  // If no events exist, nothing to unbind
1913  if (!data.handlers) {
1914    return;
1915  }
1916
1917  if (Array.isArray(type)) {
1918    return _handleMultipleEvents(off, elem, type, fn);
1919  }
1920
1921  // Utility function
1922  var removeType = function removeType(t) {
1923    data.handlers[t] = [];
1924    _cleanUpEvents(elem, t);
1925  };
1926
1927  // Are we removing all bound events?
1928  if (!type) {
1929    for (var t in data.handlers) {
1930      removeType(t);
1931    }
1932    return;
1933  }
1934
1935  var handlers = data.handlers[type];
1936
1937  // If no handlers exist, nothing to unbind
1938  if (!handlers) {
1939    return;
1940  }
1941
1942  // If no listener was provided, remove all listeners for type
1943  if (!fn) {
1944    removeType(type);
1945    return;
1946  }
1947
1948  // We're only removing a single handler
1949  if (fn.guid) {
1950    for (var n = 0; n < handlers.length; n++) {
1951      if (handlers[n].guid === fn.guid) {
1952        handlers.splice(n--, 1);
1953      }
1954    }
1955  }
1956
1957  _cleanUpEvents(elem, type);
1958}
1959
1960/**
1961 * Trigger an event for an element
1962 *
1963 * @param {Element|Object} elem
1964 *        Element to trigger an event on
1965 *
1966 * @param {EventTarget~Event|string} event
1967 *        A string (the type) or an event object with a type attribute
1968 *
1969 * @param {Object} [hash]
1970 *        data hash to pass along with the event
1971 *
1972 * @return {boolean|undefined}
1973 *         - Returns the opposite of `defaultPrevented` if default was prevented
1974 *         - Otherwise returns undefined
1975 */
1976function trigger(elem, event, hash) {
1977  // Fetches element data and a reference to the parent (for bubbling).
1978  // Don't want to add a data object to cache for every parent,
1979  // so checking hasElData first.
1980  var elemData = hasData(elem) ? getData(elem) : {};
1981  var parent = elem.parentNode || elem.ownerDocument;
1982  // type = event.type || event,
1983  // handler;
1984
1985  // If an event name was passed as a string, creates an event out of it
1986  if (typeof event === 'string') {
1987    event = { type: event, target: elem };
1988  }
1989  // Normalizes the event properties.
1990  event = fixEvent(event);
1991
1992  // If the passed element has a dispatcher, executes the established handlers.
1993  if (elemData.dispatcher) {
1994    elemData.dispatcher.call(elem, event, hash);
1995  }
1996
1997  // Unless explicitly stopped or the event does not bubble (e.g. media events)
1998  // recursively calls this function to bubble the event up the DOM.
1999  if (parent && !event.isPropagationStopped() && event.bubbles === true) {
2000    trigger.call(null, parent, event, hash);
2001
2002    // If at the top of the DOM, triggers the default action unless disabled.
2003  } else if (!parent && !event.defaultPrevented) {
2004    var targetData = getData(event.target);
2005
2006    // Checks if the target has a default action for this event.
2007    if (event.target[event.type]) {
2008      // Temporarily disables event dispatching on the target as we have already executed the handler.
2009      targetData.disabled = true;
2010      // Executes the default action.
2011      if (typeof event.target[event.type] === 'function') {
2012        event.target[event.type]();
2013      }
2014      // Re-enables event dispatching.
2015      targetData.disabled = false;
2016    }
2017  }
2018
2019  // Inform the triggerer if the default was prevented by returning false
2020  return !event.defaultPrevented;
2021}
2022
2023/**
2024 * Trigger a listener only once for an event
2025 *
2026 * @param {Element|Object} elem
2027 *        Element or object to bind to.
2028 *
2029 * @param {string|string[]} type
2030 *        Name/type of event
2031 *
2032 * @param {Event~EventListener} fn
2033 *        Event Listener function
2034 */
2035function one(elem, type, fn) {
2036  if (Array.isArray(type)) {
2037    return _handleMultipleEvents(one, elem, type, fn);
2038  }
2039  var func = function func() {
2040    off(elem, type, func);
2041    fn.apply(this, arguments);
2042  };
2043
2044  // copy the guid to the new function so it can removed using the original function's ID
2045  func.guid = fn.guid = fn.guid || newGUID();
2046  on(elem, type, func);
2047}
2048
2049var Events = (Object.freeze || Object)({
2050	fixEvent: fixEvent,
2051	on: on,
2052	off: off,
2053	trigger: trigger,
2054	one: one
2055});
2056
2057/**
2058 * @file setup.js - Functions for setting up a player without
2059 * user interaction based on the data-setup `attribute` of the video tag.
2060 *
2061 * @module setup
2062 */
2063var _windowLoaded = false;
2064var videojs$2 = void 0;
2065
2066/**
2067 * Set up any tags that have a data-setup `attribute` when the player is started.
2068 */
2069var autoSetup = function autoSetup() {
2070
2071  // Protect against breakage in non-browser environments.
2072  if (!isReal()) {
2073    return;
2074  }
2075
2076  // One day, when we stop supporting IE8, go back to this, but in the meantime...*hack hack hack*
2077  // var vids = Array.prototype.slice.call(document.getElementsByTagName('video'));
2078  // var audios = Array.prototype.slice.call(document.getElementsByTagName('audio'));
2079  // var mediaEls = vids.concat(audios);
2080
2081  // Because IE8 doesn't support calling slice on a node list, we need to loop
2082  // through each list of elements to build up a new, combined list of elements.
2083  var vids = document_1.getElementsByTagName('video');
2084  var audios = document_1.getElementsByTagName('audio');
2085  var mediaEls = [];
2086
2087  if (vids && vids.length > 0) {
2088    for (var i = 0, e = vids.length; i < e; i++) {
2089      mediaEls.push(vids[i]);
2090    }
2091  }
2092
2093  if (audios && audios.length > 0) {
2094    for (var _i = 0, _e = audios.length; _i < _e; _i++) {
2095      mediaEls.push(audios[_i]);
2096    }
2097  }
2098
2099  // Check if any media elements exist
2100  if (mediaEls && mediaEls.length > 0) {
2101
2102    for (var _i2 = 0, _e2 = mediaEls.length; _i2 < _e2; _i2++) {
2103      var mediaEl = mediaEls[_i2];
2104
2105      // Check if element exists, has getAttribute func.
2106      // IE seems to consider typeof el.getAttribute == 'object' instead of
2107      // 'function' like expected, at least when loading the player immediately.
2108      if (mediaEl && mediaEl.getAttribute) {
2109
2110        // Make sure this player hasn't already been set up.
2111        if (mediaEl.player === undefined) {
2112          var options = mediaEl.getAttribute('data-setup');
2113
2114          // Check if data-setup attr exists.
2115          // We only auto-setup if they've added the data-setup attr.
2116          if (options !== null) {
2117            // Create new video.js instance.
2118            videojs$2(mediaEl);
2119          }
2120        }
2121
2122        // If getAttribute isn't defined, we need to wait for the DOM.
2123      } else {
2124        autoSetupTimeout(1);
2125        break;
2126      }
2127    }
2128
2129    // No videos were found, so keep looping unless page is finished loading.
2130  } else if (!_windowLoaded) {
2131    autoSetupTimeout(1);
2132  }
2133};
2134
2135/**
2136 * Wait until the page is loaded before running autoSetup. This will be called in
2137 * autoSetup if `hasLoaded` returns false.
2138 *
2139 * @param {number} wait
2140 *        How long to wait in ms
2141 *
2142 * @param {module:videojs} [vjs]
2143 *        The videojs library function
2144 */
2145function autoSetupTimeout(wait, vjs) {
2146  if (vjs) {
2147    videojs$2 = vjs;
2148  }
2149
2150  window_1.setTimeout(autoSetup, wait);
2151}
2152
2153if (isReal() && document_1.readyState === 'complete') {
2154  _windowLoaded = true;
2155} else {
2156  /**
2157   * Listen for the load event on window, and set _windowLoaded to true.
2158   *
2159   * @listens load
2160   */
2161  one(window_1, 'load', function () {
2162    _windowLoaded = true;
2163  });
2164}
2165
2166/**
2167 * @file stylesheet.js
2168 * @module stylesheet
2169 */
2170/**
2171 * Create a DOM syle element given a className for it.
2172 *
2173 * @param {string} className
2174 *        The className to add to the created style element.
2175 *
2176 * @return {Element}
2177 *         The element that was created.
2178 */
2179var createStyleElement = function createStyleElement(className) {
2180  var style = document_1.createElement('style');
2181
2182  style.className = className;
2183
2184  return style;
2185};
2186
2187/**
2188 * Add text to a DOM element.
2189 *
2190 * @param {Element} el
2191 *        The Element to add text content to.
2192 *
2193 * @param {string} content
2194 *        The text to add to the element.
2195 */
2196var setTextContent = function setTextContent(el, content) {
2197  if (el.styleSheet) {
2198    el.styleSheet.cssText = content;
2199  } else {
2200    el.textContent = content;
2201  }
2202};
2203
2204/**
2205 * @file fn.js
2206 * @module fn
2207 */
2208/**
2209 * Bind (a.k.a proxy or Context). A simple method for changing the context of a function
2210 * It also stores a unique id on the function so it can be easily removed from events.
2211 *
2212 * @param {Mixed} context
2213 *        The object to bind as scope.
2214 *
2215 * @param {Function} fn
2216 *        The function to be bound to a scope.
2217 *
2218 * @param {number} [uid]
2219 *        An optional unique ID for the function to be set
2220 *
2221 * @return {Function}
2222 *         The new function that will be bound into the context given
2223 */
2224var bind = function bind(context, fn, uid) {
2225  // Make sure the function has a unique ID
2226  if (!fn.guid) {
2227    fn.guid = newGUID();
2228  }
2229
2230  // Create the new function that changes the context
2231  var bound = function bound() {
2232    return fn.apply(context, arguments);
2233  };
2234
2235  // Allow for the ability to individualize this function
2236  // Needed in the case where multiple objects might share the same prototype
2237  // IF both items add an event listener with the same function, then you try to remove just one
2238  // it will remove both because they both have the same guid.
2239  // when using this, you need to use the bind method when you remove the listener as well.
2240  // currently used in text tracks
2241  bound.guid = uid ? uid + '_' + fn.guid : fn.guid;
2242
2243  return bound;
2244};
2245
2246/**
2247 * Wraps the given function, `fn`, with a new function that only invokes `fn`
2248 * at most once per every `wait` milliseconds.
2249 *
2250 * @param  {Function} fn
2251 *         The function to be throttled.
2252 *
2253 * @param  {Number}   wait
2254 *         The number of milliseconds by which to throttle.
2255 *
2256 * @return {Function}
2257 */
2258var throttle = function throttle(fn, wait) {
2259  var last = Date.now();
2260
2261  var throttled = function throttled() {
2262    var now = Date.now();
2263
2264    if (now - last >= wait) {
2265      fn.apply(undefined, arguments);
2266      last = now;
2267    }
2268  };
2269
2270  return throttled;
2271};
2272
2273/**
2274 * @file src/js/event-target.js
2275 */
2276/**
2277 * `EventTarget` is a class that can have the same API as the DOM `EventTarget`. It
2278 * adds shorthand functions that wrap around lengthy functions. For example:
2279 * the `on` function is a wrapper around `addEventListener`.
2280 *
2281 * @see [EventTarget Spec]{@link https://www.w3.org/TR/DOM-Level-2-Events/events.html#Events-EventTarget}
2282 * @class EventTarget
2283 */
2284var EventTarget = function EventTarget() {};
2285
2286/**
2287 * A Custom DOM event.
2288 *
2289 * @typedef {Object} EventTarget~Event
2290 * @see [Properties]{@link https://developer.mozilla.org/en-US/docs/Web/API/CustomEvent}
2291 */
2292
2293/**
2294 * All event listeners should follow the following format.
2295 *
2296 * @callback EventTarget~EventListener
2297 * @this {EventTarget}
2298 *
2299 * @param {EventTarget~Event} event
2300 *        the event that triggered this function
2301 *
2302 * @param {Object} [hash]
2303 *        hash of data sent during the event
2304 */
2305
2306/**
2307 * An object containing event names as keys and booleans as values.
2308 *
2309 * > NOTE: If an event name is set to a true value here {@link EventTarget#trigger}
2310 *         will have extra functionality. See that function for more information.
2311 *
2312 * @property EventTarget.prototype.allowedEvents_
2313 * @private
2314 */
2315EventTarget.prototype.allowedEvents_ = {};
2316
2317/**
2318 * Adds an `event listener` to an instance of an `EventTarget`. An `event listener` is a
2319 * function that will get called when an event with a certain name gets triggered.
2320 *
2321 * @param {string|string[]} type
2322 *        An event name or an array of event names.
2323 *
2324 * @param {EventTarget~EventListener} fn
2325 *        The function to call with `EventTarget`s
2326 */
2327EventTarget.prototype.on = function (type, fn) {
2328  // Remove the addEventListener alias before calling Events.on
2329  // so we don't get into an infinite type loop
2330  var ael = this.addEventListener;
2331
2332  this.addEventListener = function () {};
2333  on(this, type, fn);
2334  this.addEventListener = ael;
2335};
2336
2337/**
2338 * An alias of {@link EventTarget#on}. Allows `EventTarget` to mimic
2339 * the standard DOM API.
2340 *
2341 * @function
2342 * @see {@link EventTarget#on}
2343 */
2344EventTarget.prototype.addEventListener = EventTarget.prototype.on;
2345
2346/**
2347 * Removes an `event listener` for a specific event from an instance of `EventTarget`.
2348 * This makes it so that the `event listener` will no longer get called when the
2349 * named event happens.
2350 *
2351 * @param {string|string[]} type
2352 *        An event name or an array of event names.
2353 *
2354 * @param {EventTarget~EventListener} fn
2355 *        The function to remove.
2356 */
vendor: 6,590 bytes, lines 2357-2594
2357EventTarget.prototype.off = function (type, fn) {
2358  off(this, type, fn);
2359};
2360
2361/**
2362 * An alias of {@link EventTarget#off}. Allows `EventTarget` to mimic
2363 * the standard DOM API.
2364 *
2365 * @function
2366 * @see {@link EventTarget#off}
2367 */
2368EventTarget.prototype.removeEventListener = EventTarget.prototype.off;
2369
2370/**
2371 * This function will add an `event listener` that gets triggered only once. After the
2372 * first trigger it will get removed. This is like adding an `event listener`
2373 * with {@link EventTarget#on} that calls {@link EventTarget#off} on itself.
2374 *
2375 * @param {string|string[]} type
2376 *        An event name or an array of event names.
2377 *
2378 * @param {EventTarget~EventListener} fn
2379 *        The function to be called once for each event name.
2380 */
2381EventTarget.prototype.one = function (type, fn) {
2382  // Remove the addEventListener alialing Events.on
2383  // so we don't get into an infinite type loop
2384  var ael = this.addEventListener;
2385
2386  this.addEventListener = function () {};
2387  one(this, type, fn);
2388  this.addEventListener = ael;
2389};
2390
2391/**
2392 * This function causes an event to happen. This will then cause any `event listeners`
2393 * that are waiting for that event, to get called. If there are no `event listeners`
2394 * for an event then nothing will happen.
2395 *
2396 * If the name of the `Event` that is being triggered is in `EventTarget.allowedEvents_`.
2397 * Trigger will also call the `on` + `uppercaseEventName` function.
2398 *
2399 * Example:
2400 * 'click' is in `EventTarget.allowedEvents_`, so, trigger will attempt to call
2401 * `onClick` if it exists.
2402 *
2403 * @param {string|EventTarget~Event|Object} event
2404 *        The name of the event, an `Event`, or an object with a key of type set to
2405 *        an event name.
2406 */
2407EventTarget.prototype.trigger = function (event) {
2408  var type = event.type || event;
2409
2410  if (typeof event === 'string') {
2411    event = { type: type };
2412  }
2413  event = fixEvent(event);
2414
2415  if (this.allowedEvents_[type] && this['on' + type]) {
2416    this['on' + type](event);
2417  }
2418
2419  trigger(this, event);
2420};
2421
2422/**
2423 * An alias of {@link EventTarget#trigger}. Allows `EventTarget` to mimic
2424 * the standard DOM API.
2425 *
2426 * @function
2427 * @see {@link EventTarget#trigger}
2428 */
2429EventTarget.prototype.dispatchEvent = EventTarget.prototype.trigger;
2430
2431/**
2432 * @file mixins/evented.js
2433 * @module evented
2434 */
2435/**
2436 * Returns whether or not an object has had the evented mixin applied.
2437 *
2438 * @param  {Object} object
2439 *         An object to test.
2440 *
2441 * @return {boolean}
2442 *         Whether or not the object appears to be evented.
2443 */
2444var isEvented = function isEvented(object) {
2445  return object instanceof EventTarget || !!object.eventBusEl_ && ['on', 'one', 'off', 'trigger'].every(function (k) {
2446    return typeof object[k] === 'function';
2447  });
2448};
2449
2450/**
2451 * Whether a value is a valid event type - non-empty string or array.
2452 *
2453 * @private
2454 * @param  {string|Array} type
2455 *         The type value to test.
2456 *
2457 * @return {boolean}
2458 *         Whether or not the type is a valid event type.
2459 */
2460var isValidEventType = function isValidEventType(type) {
2461  return (
2462    // The regex here verifies that the `type` contains at least one non-
2463    // whitespace character.
2464    typeof type === 'string' && /\S/.test(type) || Array.isArray(type) && !!type.length
2465  );
2466};
2467
2468/**
2469 * Validates a value to determine if it is a valid event target. Throws if not.
2470 *
2471 * @private
2472 * @throws {Error}
2473 *         If the target does not appear to be a valid event target.
2474 *
2475 * @param  {Object} target
2476 *         The object to test.
2477 */
2478var validateTarget = function validateTarget(target) {
2479  if (!target.nodeName && !isEvented(target)) {
2480    throw new Error('Invalid target; must be a DOM node or evented object.');
2481  }
2482};
2483
2484/**
2485 * Validates a value to determine if it is a valid event target. Throws if not.
2486 *
2487 * @private
2488 * @throws {Error}
2489 *         If the type does not appear to be a valid event type.
2490 *
2491 * @param  {string|Array} type
2492 *         The type to test.
2493 */
2494var validateEventType = function validateEventType(type) {
2495  if (!isValidEventType(type)) {
2496    throw new Error('Invalid event type; must be a non-empty string or array.');
2497  }
2498};
2499
2500/**
2501 * Validates a value to determine if it is a valid listener. Throws if not.
2502 *
2503 * @private
2504 * @throws {Error}
2505 *         If the listener is not a function.
2506 *
2507 * @param  {Function} listener
2508 *         The listener to test.
2509 */
2510var validateListener = function validateListener(listener) {
2511  if (typeof listener !== 'function') {
2512    throw new Error('Invalid listener; must be a function.');
2513  }
2514};
2515
2516/**
2517 * Takes an array of arguments given to `on()` or `one()`, validates them, and
2518 * normalizes them into an object.
2519 *
2520 * @private
2521 * @param  {Object} self
2522 *         The evented object on which `on()` or `one()` was called. This
2523 *         object will be bound as the `this` value for the listener.
2524 *
2525 * @param  {Array} args
2526 *         An array of arguments passed to `on()` or `one()`.
2527 *
2528 * @return {Object}
2529 *         An object containing useful values for `on()` or `one()` calls.
2530 */
2531var normalizeListenArgs = function normalizeListenArgs(self, args) {
2532
2533  // If the number of arguments is less than 3, the target is always the
2534  // evented object itself.
2535  var isTargetingSelf = args.length < 3 || args[0] === self || args[0] === self.eventBusEl_;
2536  var target = void 0;
2537  var type = void 0;
2538  var listener = void 0;
2539
2540  if (isTargetingSelf) {
2541    target = self.eventBusEl_;
2542
2543    // Deal with cases where we got 3 arguments, but we are still listening to
2544    // the evented object itself.
2545    if (args.length >= 3) {
2546      args.shift();
2547    }
2548
2549    type = args[0];
2550    listener = args[1];
2551  } else {
2552    target = args[0];
2553    type = args[1];
2554    listener = args[2];
2555  }
2556
2557  validateTarget(target);
2558  validateEventType(type);
2559  validateListener(listener);
2560
2561  listener = bind(self, listener);
2562
2563  return { isTargetingSelf: isTargetingSelf, target: target, type: type, listener: listener };
2564};
2565
2566/**
2567 * Adds the listener to the event type(s) on the target, normalizing for
2568 * the type of target.
2569 *
2570 * @private
2571 * @param  {Element|Object} target
2572 *         A DOM node or evented object.
2573 *
2574 * @param  {string} method
2575 *         The event binding method to use ("on" or "one").
2576 *
2577 * @param  {string|Array} type
2578 *         One or more event type(s).
2579 *
2580 * @param  {Function} listener
2581 *         A listener function.
2582 */
2583var listen = function listen(target, method, type, listener) {
2584  validateTarget(target);
2585
2586  if (target.nodeName) {
2587    Events[method](target, type, listener);
2588  } else {
2589    target[method](type, listener);
2590  }
2591};
2592
2593/**
2594 * Contains methods that provide event capabilit
vendor: 8,378 bytes, lines 2594-2835
2594es to an object which is passed
2595 * to {@link module:evented|evented}.
2596 *
2597 * @mixin EventedMixin
2598 */
2599var EventedMixin = {
2600
2601  /**
2602   * Add a listener to an event (or events) on this object or another evented
2603   * object.
2604   *
2605   * @param  {string|Array|Element|Object} targetOrType
2606   *         If this is a string or array, it represents the event type(s)
2607   *         that will trigger the listener.
2608   *
2609   *         Another evented object can be passed here instead, which will
2610   *         cause the listener to listen for events on _that_ object.
2611   *
2612   *         In either case, the listener's `this` value will be bound to
2613   *         this object.
2614   *
2615   * @param  {string|Array|Function} typeOrListener
2616   *         If the first argument was a string or array, this should be the
2617   *         listener function. Otherwise, this is a string or array of event
2618   *         type(s).
2619   *
2620   * @param  {Function} [listener]
2621   *         If the first argument was another evented object, this will be
2622   *         the listener function.
2623   */
2624  on: function on$$1() {
2625    var _this = this;
2626
2627    for (var _len = arguments.length, args = Array(_len), _key = 0; _key < _len; _key++) {
2628      args[_key] = arguments[_key];
2629    }
2630
2631    var _normalizeListenArgs = normalizeListenArgs(this, args),
2632        isTargetingSelf = _normalizeListenArgs.isTargetingSelf,
2633        target = _normalizeListenArgs.target,
2634        type = _normalizeListenArgs.type,
2635        listener = _normalizeListenArgs.listener;
2636
2637    listen(target, 'on', type, listener);
2638
2639    // If this object is listening to another evented object.
2640    if (!isTargetingSelf) {
2641
2642      // If this object is disposed, remove the listener.
2643      var removeListenerOnDispose = function removeListenerOnDispose() {
2644        return _this.off(target, type, listener);
2645      };
2646
2647      // Use the same function ID as the listener so we can remove it later it
2648      // using the ID of the original listener.
2649      removeListenerOnDispose.guid = listener.guid;
2650
2651      // Add a listener to the target's dispose event as well. This ensures
2652      // that if the target is disposed BEFORE this object, we remove the
2653      // removal listener that was just added. Otherwise, we create a memory leak.
2654      var removeRemoverOnTargetDispose = function removeRemoverOnTargetDispose() {
2655        return _this.off('dispose', removeListenerOnDispose);
2656      };
2657
2658      // Use the same function ID as the listener so we can remove it later
2659      // it using the ID of the original listener.
2660      removeRemoverOnTargetDispose.guid = listener.guid;
2661
2662      listen(this, 'on', 'dispose', removeListenerOnDispose);
2663      listen(target, 'on', 'dispose', removeRemoverOnTargetDispose);
2664    }
2665  },
2666
2667
2668  /**
2669   * Add a listener to an event (or events) on this object or another evented
2670   * object. The listener will only be called once and then removed.
2671   *
2672   * @param  {string|Array|Element|Object} targetOrType
2673   *         If this is a string or array, it represents the event type(s)
2674   *         that will trigger the listener.
2675   *
2676   *         Another evented object can be passed here instead, which will
2677   *         cause the listener to listen for events on _that_ object.
2678   *
2679   *         In either case, the listener's `this` value will be bound to
2680   *         this object.
2681   *
2682   * @param  {string|Array|Function} typeOrListener
2683   *         If the first argument was a string or array, this should be the
2684   *         listener function. Otherwise, this is a string or array of event
2685   *         type(s).
2686   *
2687   * @param  {Function} [listener]
2688   *         If the first argument was another evented object, this will be
2689   *         the listener function.
2690   */
2691  one: function one$$1() {
2692    var _this2 = this;
2693
2694    for (var _len2 = arguments.length, args = Array(_len2), _key2 = 0; _key2 < _len2; _key2++) {
2695      args[_key2] = arguments[_key2];
2696    }
2697
2698    var _normalizeListenArgs2 = normalizeListenArgs(this, args),
2699        isTargetingSelf = _normalizeListenArgs2.isTargetingSelf,
2700        target = _normalizeListenArgs2.target,
2701        type = _normalizeListenArgs2.type,
2702        listener = _normalizeListenArgs2.listener;
2703
2704    // Targeting this evented object.
2705
2706
2707    if (isTargetingSelf) {
2708      listen(target, 'one', type, listener);
2709
2710      // Targeting another evented object.
2711    } else {
2712      var wrapper = function wrapper() {
2713        for (var _len3 = arguments.length, largs = Array(_len3), _key3 = 0; _key3 < _len3; _key3++) {
2714          largs[_key3] = arguments[_key3];
2715        }
2716
2717        _this2.off(target, type, wrapper);
2718        listener.apply(null, largs);
2719      };
2720
2721      // Use the same function ID as the listener so we can remove it later
2722      // it using the ID of the original listener.
2723      wrapper.guid = listener.guid;
2724      listen(target, 'one', type, wrapper);
2725    }
2726  },
2727
2728
2729  /**
2730   * Removes listener(s) from event(s) on an evented object.
2731   *
2732   * @param  {string|Array|Element|Object} [targetOrType]
2733   *         If this is a string or array, it represents the event type(s).
2734   *
2735   *         Another evented object can be passed here instead, in which case
2736   *         ALL 3 arguments are _required_.
2737   *
2738   * @param  {string|Array|Function} [typeOrListener]
2739   *         If the first argument was a string or array, this may be the
2740   *         listener function. Otherwise, this is a string or array of event
2741   *         type(s).
2742   *
2743   * @param  {Function} [listener]
2744   *         If the first argument was another evented object, this will be
2745   *         the listener function; otherwise, _all_ listeners bound to the
2746   *         event type(s) will be removed.
2747   */
2748  off: function off$$1(targetOrType, typeOrListener, listener) {
2749
2750    // Targeting this evented object.
2751    if (!targetOrType || isValidEventType(targetOrType)) {
2752      off(this.eventBusEl_, targetOrType, typeOrListener);
2753
2754      // Targeting another evented object.
2755    } else {
2756      var target = targetOrType;
2757      var type = typeOrListener;
2758
2759      // Fail fast and in a meaningful way!
2760      validateTarget(target);
2761      validateEventType(type);
2762      validateListener(listener);
2763
2764      // Ensure there's at least a guid, even if the function hasn't been used
2765      listener = bind(this, listener);
2766
2767      // Remove the dispose listener on this evented object, which was given
2768      // the same guid as the event listener in on().
2769      this.off('dispose', listener);
2770
2771      if (target.nodeName) {
2772        off(target, type, listener);
2773        off(target, 'dispose', listener);
2774      } else if (isEvented(target)) {
2775        target.off(type, listener);
2776        target.off('dispose', listener);
2777      }
2778    }
2779  },
2780
2781
2782  /**
2783   * Fire an event on this evented object, causing its listeners to be called.
2784   *
2785   * @param   {string|Object} event
2786   *          An event type or an object with a type property.
2787   *
2788   * @param   {Object} [hash]
2789   *          An additional object to pass along to listeners.
2790   *
2791   * @returns {boolean}
2792   *          Whether or not the default behavior was prevented.
2793   */
2794  trigger: function trigger$$1(event, hash) {
2795    return trigger(this.eventBusEl_, event, hash);
2796  }
2797};
2798
2799/**
2800 * Applies {@link module:evented~EventedMixin|EventedMixin} to a target object.
2801 *
2802 * @param  {Object} target
2803 *         The object to which to add event methods.
2804 *
2805 * @param  {Object} [options={}]
2806 *         Options for customizing the mixin behavior.
2807 *
2808 * @param  {String} [options.eventBusKey]
2809 *         By default, adds a `eventBusEl_` DOM element to the target object,
2810 *         which is used as an event bus. If the target object already has a
2811 *         DOM element that should be used, pass its key here.
2812 *
2813 * @return {Object}
2814 *         The target object.
2815 */
2816function evented(target) {
2817  var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
2818  var eventBusKey = options.eventBusKey;
2819
2820  // Set or create the eventBusEl_.
2821
2822  if (eventBusKey) {
2823    if (!target[eventBusKey].nodeName) {
2824      throw new Error('The eventBusKey "' + eventBusKey + '" does not refer to an element.');
2825    }
2826    target.eventBusEl_ = target[eventBusKey];
2827  } else {
2828    target.eventBusEl_ = createEl('span', { className: 'vjs-event-bus' });
2829  }
2830
2831  assign(target, EventedMixin);
2832
2833  // When any evented object is disposed, it removes all its listeners.
2834  target.on('dispose', function () {
2835    
vendor: 9,256 bytes, lines 2835-3186
2835return target.off();
2836  });
2837
2838  return target;
2839}
2840
2841/**
2842 * @file mixins/stateful.js
2843 * @module stateful
2844 */
2845/**
2846 * Contains methods that provide statefulness to an object which is passed
2847 * to {@link module:stateful}.
2848 *
2849 * @mixin StatefulMixin
2850 */
2851var StatefulMixin = {
2852
2853  /**
2854   * A hash containing arbitrary keys and values representing the state of
2855   * the object.
2856   *
2857   * @type {Object}
2858   */
2859  state: {},
2860
2861  /**
2862   * Set the state of an object by mutating its
2863   * {@link module:stateful~StatefulMixin.state|state} object in place.
2864   *
2865   * @fires   module:stateful~StatefulMixin#statechanged
2866   * @param   {Object|Function} stateUpdates
2867   *          A new set of properties to shallow-merge into the plugin state.
2868   *          Can be a plain object or a function returning a plain object.
2869   *
2870   * @returns {Object|undefined}
2871   *          An object containing changes that occurred. If no changes
2872   *          occurred, returns `undefined`.
2873   */
2874  setState: function setState(stateUpdates) {
2875    var _this = this;
2876
2877    // Support providing the `stateUpdates` state as a function.
2878    if (typeof stateUpdates === 'function') {
2879      stateUpdates = stateUpdates();
2880    }
2881
2882    var changes = void 0;
2883
2884    each(stateUpdates, function (value, key) {
2885
2886      // Record the change if the value is different from what's in the
2887      // current state.
2888      if (_this.state[key] !== value) {
2889        changes = changes || {};
2890        changes[key] = {
2891          from: _this.state[key],
2892          to: value
2893        };
2894      }
2895
2896      _this.state[key] = value;
2897    });
2898
2899    // Only trigger "statechange" if there were changes AND we have a trigger
2900    // function. This allows us to not require that the target object be an
2901    // evented object.
2902    if (changes && isEvented(this)) {
2903
2904      /**
2905       * An event triggered on an object that is both
2906       * {@link module:stateful|stateful} and {@link module:evented|evented}
2907       * indicating that its state has changed.
2908       *
2909       * @event    module:stateful~StatefulMixin#statechanged
2910       * @type     {Object}
2911       * @property {Object} changes
2912       *           A hash containing the properties that were changed and
2913       *           the values they were changed `from` and `to`.
2914       */
2915      this.trigger({
2916        changes: changes,
2917        type: 'statechanged'
2918      });
2919    }
2920
2921    return changes;
2922  }
2923};
2924
2925/**
2926 * Applies {@link module:stateful~StatefulMixin|StatefulMixin} to a target
2927 * object.
2928 *
2929 * If the target object is {@link module:evented|evented} and has a
2930 * `handleStateChanged` method, that method will be automatically bound to the
2931 * `statechanged` event on itself.
2932 *
2933 * @param   {Object} target
2934 *          The object to be made stateful.
2935 *
2936 * @param   {Object} [defaultState]
2937 *          A default set of properties to populate the newly-stateful object's
2938 *          `state` property.
2939 *
2940 * @returns {Object}
2941 *          Returns the `target`.
2942 */
2943function stateful(target, defaultState) {
2944  assign(target, StatefulMixin);
2945
2946  // This happens after the mixing-in because we need to replace the `state`
2947  // added in that step.
2948  target.state = assign({}, target.state, defaultState);
2949
2950  // Auto-bind the `handleStateChanged` method of the target object if it exists.
2951  if (typeof target.handleStateChanged === 'function' && isEvented(target)) {
2952    target.on('statechanged', target.handleStateChanged);
2953  }
2954
2955  return target;
2956}
2957
2958/**
2959 * @file to-title-case.js
2960 * @module to-title-case
2961 */
2962
2963/**
2964 * Uppercase the first letter of a string.
2965 *
2966 * @param {string} string
2967 *        String to be uppercased
2968 *
2969 * @return {string}
2970 *         The string with an uppercased first letter
2971 */
2972function toTitleCase(string) {
2973  if (typeof string !== 'string') {
2974    return string;
2975  }
2976
2977  return string.charAt(0).toUpperCase() + string.slice(1);
2978}
2979
2980/**
2981 * Compares the TitleCase versions of the two strings for equality.
2982 *
2983 * @param {string} str1
2984 *        The first string to compare
2985 *
2986 * @param {string} str2
2987 *        The second string to compare
2988 *
2989 * @return {boolean}
2990 *         Whether the TitleCase versions of the strings are equal
2991 */
2992function titleCaseEquals(str1, str2) {
2993  return toTitleCase(str1) === toTitleCase(str2);
2994}
2995
2996/**
2997 * @file merge-options.js
2998 * @module merge-options
2999 */
3000/**
3001 * Deep-merge one or more options objects, recursively merging **only** plain
3002 * object properties.
3003 *
3004 * @param   {Object[]} sources
3005 *          One or more objects to merge into a new object.
3006 *
3007 * @returns {Object}
3008 *          A new object that is the merged result of all sources.
3009 */
3010function mergeOptions() {
3011  var result = {};
3012
3013  for (var _len = arguments.length, sources = Array(_len), _key = 0; _key < _len; _key++) {
3014    sources[_key] = arguments[_key];
3015  }
3016
3017  sources.forEach(function (source) {
3018    if (!source) {
3019      return;
3020    }
3021
3022    each(source, function (value, key) {
3023      if (!isPlain(value)) {
3024        result[key] = value;
3025        return;
3026      }
3027
3028      if (!isPlain(result[key])) {
3029        result[key] = {};
3030      }
3031
3032      result[key] = mergeOptions(result[key], value);
3033    });
3034  });
3035
3036  return result;
3037}
3038
3039/**
3040 * Player Component - Base class for all UI objects
3041 *
3042 * @file component.js
3043 */
3044/**
3045 * Base class for all UI Components.
3046 * Components are UI objects which represent both a javascript object and an element
3047 * in the DOM. They can be children of other components, and can have
3048 * children themselves.
3049 *
3050 * Components can also use methods from {@link EventTarget}
3051 */
3052
3053var Component = function () {
3054
3055  /**
3056   * A callback that is called when a component is ready. Does not have any
3057   * paramters and any callback value will be ignored.
3058   *
3059   * @callback Component~ReadyCallback
3060   * @this Component
3061   */
3062
3063  /**
3064   * Creates an instance of this class.
3065   *
3066   * @param {Player} player
3067   *        The `Player` that this class should be attached to.
3068   *
3069   * @param {Object} [options]
3070   *        The key/value store of player options.
3071   *
3072   * @param {Object[]} [options.children]
3073   *        An array of children objects to intialize this component with. Children objects have
3074   *        a name property that will be used if more than one component of the same type needs to be
3075   *        added.
3076   *
3077   * @param {Component~ReadyCallback} [ready]
3078   *        Function that gets called when the `Component` is ready.
3079   */
3080  function Component(player, options, ready) {
3081    classCallCheck(this, Component);
3082
3083
3084    // The component might be the player itself and we can't pass `this` to super
3085    if (!player && this.play) {
3086      this.player_ = player = this; // eslint-disable-line
3087    } else {
3088      this.player_ = player;
3089    }
3090
3091    // Make a copy of prototype.options_ to protect against overriding defaults
3092    this.options_ = mergeOptions({}, this.options_);
3093
3094    // Updated options with supplied options
3095    options = this.options_ = mergeOptions(this.options_, options);
3096
3097    // Get ID from options or options element if one is supplied
3098    this.id_ = options.id || options.el && options.el.id;
3099
3100    // If there was no ID from the options, generate one
3101    if (!this.id_) {
3102      // Don't require the player ID function in the case of mock players
3103      var id = player && player.id && player.id() || 'no_player';
3104
3105      this.id_ = id + '_component_' + newGUID();
3106    }
3107
3108    this.name_ = options.name || null;
3109
3110    // Create element if one wasn't provided in options
3111    if (options.el) {
3112      this.el_ = options.el;
3113    } else if (options.createEl !== false) {
3114      this.el_ = this.createEl();
3115    }
3116
3117    // Make this an evented object and use `el_`, if available, as its event bus
3118    evented(this, { eventBusKey: this.el_ ? 'el_' : null });
3119    stateful(this, this.constructor.defaultState);
3120
3121    this.children_ = [];
3122    this.childIndex_ = {};
3123    this.childNameIndex_ = {};
3124
3125    // Add any child components in options
3126    if (options.initChildren !== false) {
3127      this.initChildren();
3128    }
3129
3130    this.ready(ready);
3131    // Don't want to trigger ready here or it will before init is actually
3132    // finished for all children that run this constructor
3133
3134    if (options.reportTouchActivity !== false) {
3135      this.enableTouchActivity();
3136    }
3137  }
3138
3139  /**
3140   * Dispose of the `Component` and all child components.
3141   *
3142   * @fires Component#dispose
3143   */
3144
3145
3146  Component.prototype.dispose = function dispose() {
3147
3148    /**
3149     * Triggered when a `Component` is disposed.
3150     *
3151     * @event Component#dispose
3152     * @type {EventTarget~Event}
3153     *
3154     * @property {boolean} [bubbles=false]
3155     *           set to false so that the close event does not
3156     *           bubble up
3157     */
3158    this.trigger({ type: 'dispose', bubbles: false });
3159
3160    // Dispose all children.
3161    if (this.children_) {
3162      for (var i = this.children_.length - 1; i >= 0; i--) {
3163        if (this.children_[i].dispose) {
3164          this.children_[i].dispose();
3165        }
3166      }
3167    }
3168
3169    // Delete child references
3170    this.children_ = null;
3171    this.childIndex_ = null;
3172    this.childNameIndex_ = null;
3173
3174    if (this.el_) {
3175      // Remove element from DOM
3176      if (this.el_.parentNode) {
3177        this.el_.parentNode.removeChild(this.el_);
3178      }
3179
3180      removeData(this.el_);
3181      this.el_ = null;
3182    }
3183  };
3184
3185  /**
3186   * Return the {@link Player}
3186 that the `Component` has attached to.
3187   *
3188   * @return {Player}
3189   *         The player that this `Component` has attached to.
3190   */
3191
3192
3193  Component.prototype.player = function player() {
3194    return this.player_;
3195  };
3196
3197  /**
3198   * Deep merge of options objects with new options.
3199   * > Note: When both `obj` and `options` contain properties whose values are objects.
3200   *         The two properties get merged using {@link module:mergeOptions}
3201   *
3202   * @param {Object} obj
3203   *        The object that contains new options.
3204   *
3205   * @return {Object}
3206   *         A new object of `this.options_` and `obj` merged together.
3207   *
3208   * @deprecated since version 5
3209   */
3210
3211
3212  Component.prototype.options = function options(obj) {
3213    log$1.warn('this.options() has been deprecated and will be moved to the constructor in 6.0');
3214
3215    if (!obj) {
3216      return this.options_;
3217    }
3218
3219    this.options_ = mergeOptions(this.options_, obj);
3220    return this.options_;
3221  };
3222
3223  /**
3224   * Get the `Component`s DOM element
3225   *
3226   * @return {Element}
3227   *         The DOM element for this `Component`.
3228   */
3229
3230
3231  Component.prototype.el = function el() {
3232    return this.el_;
3233  };
3234
3235  /**
3236   * Create the `Component`s DOM element.
3237   *
3238   * @param {string} [tagName]
3239   *        Element's DOM node type. e.g. 'div'
3240   *
3241   * @param {Object} [properties]
3242   *        An object of properties that should be set.
3243   *
3244   * @param {Object} [attributes]
3245   *        An object of attributes that should be set.
3246   *
3247   * @return {Element}
3248   *         The element that gets created.
3249   */
3250
3251
3252  Component.prototype.createEl = function createEl$$1(tagName, properties, attributes) {
3253    return createEl(tagName, properties, attributes);
3254  };
3255
3256  /**
3257   * Localize a string given the string in english.
3258   *
3259   * If tokens are provided, it'll try and run a simple token replacement on the provided string.
3260   * The tokens it lo
vendor: 11,969 bytes, lines 3260-3669
3260ooks for look like `{1}` with the index being 1-indexed into the tokens array.
3261   *
3262   * If a `defaultValue` is provided, it'll use that over `string`,
3263   * if a value isn't found in provided language files.
3264   * This is useful if you want to have a descriptive key for token replacement
3265   * but have a succinct localized string and not require `en.json` to be included.
3266   *
3267   * Currently, it is used for the progress bar timing.
3268   * ```js
3269   * {
3270   *   "progress bar timing: currentTime={1} duration={2}": "{1} of {2}"
3271   * }
3272   * ```
3273   * It is then used like so:
3274   * ```js
3275   * this.localize('progress bar timing: currentTime={1} duration{2}',
3276   *               [this.player_.currentTime(), this.player_.duration()],
3277   *               '{1} of {2}');
3278   * ```
3279   *
3280   * Which outputs something like: `01:23 of 24:56`.
3281   *
3282   *
3283   * @param {string} string
3284   *        The string to localize and the key to lookup in the language files.
3285   * @param {string[]} [tokens]
3286   *        If the current item has token replacements, provide the tokens here.
3287   * @param {string} [defaultValue]
3288   *        Defaults to `string`. Can be a default value to use for token replacement
3289   *        if the lookup key is needed to be separate.
3290   *
3291   * @return {string}
3292   *         The localized string or if no localization exists the english string.
3293   */
3294
3295
3296  Component.prototype.localize = function localize(string, tokens) {
3297    var defaultValue = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : string;
3298
3299    var code = this.player_.language && this.player_.language();
3300    var languages = this.player_.languages && this.player_.languages();
3301    var language = languages && languages[code];
3302    var primaryCode = code && code.split('-')[0];
3303    var primaryLang = languages && languages[primaryCode];
3304
3305    var localizedString = defaultValue;
3306
3307    if (language && language[string]) {
3308      localizedString = language[string];
3309    } else if (primaryLang && primaryLang[string]) {
3310      localizedString = primaryLang[string];
3311    }
3312
3313    if (tokens) {
3314      localizedString = localizedString.replace(/\{(\d+)\}/g, function (match, index) {
3315        var value = tokens[index - 1];
3316        var ret = value;
3317
3318        if (typeof value === 'undefined') {
3319          ret = match;
3320        }
3321
3322        return ret;
3323      });
3324    }
3325
3326    return localizedString;
3327  };
3328
3329  /**
3330   * Return the `Component`s DOM element. This is where children get inserted.
3331   * This will usually be the the same as the element returned in {@link Component#el}.
3332   *
3333   * @return {Element}
3334   *         The content element for this `Component`.
3335   */
3336
3337
3338  Component.prototype.contentEl = function contentEl() {
3339    return this.contentEl_ || this.el_;
3340  };
3341
3342  /**
3343   * Get this `Component`s ID
3344   *
3345   * @return {string}
3346   *         The id of this `Component`
3347   */
3348
3349
3350  Component.prototype.id = function id() {
3351    return this.id_;
3352  };
3353
3354  /**
3355   * Get the `Component`s name. The name gets used to reference the `Component`
3356   * and is set during registration.
3357   *
3358   * @return {string}
3359   *         The name of this `Component`.
3360   */
3361
3362
3363  Component.prototype.name = function name() {
3364    return this.name_;
3365  };
3366
3367  /**
3368   * Get an array of all child components
3369   *
3370   * @return {Array}
3371   *         The children
3372   */
3373
3374
3375  Component.prototype.children = function children() {
3376    return this.children_;
3377  };
3378
3379  /**
3380   * Returns the child `Component` with the given `id`.
3381   *
3382   * @param {string} id
3383   *        The id of the child `Component` to get.
3384   *
3385   * @return {Component|undefined}
3386   *         The child `Component` with the given `id` or undefined.
3387   */
3388
3389
3390  Component.prototype.getChildById = function getChildById(id) {
3391    return this.childIndex_[id];
3392  };
3393
3394  /**
3395   * Returns the child `Component` with the given `name`.
3396   *
3397   * @param {string} name
3398   *        The name of the child `Component` to get.
3399   *
3400   * @return {Component|undefined}
3401   *         The child `Component` with the given `name` or undefined.
3402   */
3403
3404
3405  Component.prototype.getChild = function getChild(name) {
3406    if (!name) {
3407      return;
3408    }
3409
3410    name = toTitleCase(name);
3411
3412    return this.childNameIndex_[name];
3413  };
3414
3415  /**
3416   * Add a child `Component` inside the current `Component`.
3417   *
3418   *
3419   * @param {string|Component} child
3420   *        The name or instance of a child to add.
3421   *
3422   * @param {Object} [options={}]
3423   *        The key/value store of options that will get passed to children of
3424   *        the child.
3425   *
3426   * @param {number} [index=this.children_.length]
3427   *        The index to attempt to add a child into.
3428   *
3429   * @return {Component}
3430   *         The `Component` that gets added as a child. When using a string the
3431   *         `Component` will get created by this process.
3432   */
3433
3434
3435  Component.prototype.addChild = function addChild(child) {
3436    var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
3437    var index = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : this.children_.length;
3438
3439    var component = void 0;
3440    var componentName = void 0;
3441
3442    // If child is a string, create component with options
3443    if (typeof child === 'string') {
3444      componentName = toTitleCase(child);
3445
3446      var componentClassName = options.componentClass || componentName;
3447
3448      // Set name through options
3449      options.name = componentName;
3450
3451      // Create a new object & element for this controls set
3452      // If there's no .player_, this is a player
3453      var ComponentClass = Component.getComponent(componentClassName);
3454
3455      if (!ComponentClass) {
3456        throw new Error('Component ' + componentClassName + ' does not exist');
3457      }
3458
3459      // data stored directly on the videojs object may be
3460      // misidentified as a component to retain
3461      // backwards-compatibility with 4.x. check to make sure the
3462      // component class can be instantiated.
3463      if (typeof ComponentClass !== 'function') {
3464        return null;
3465      }
3466
3467      component = new ComponentClass(this.player_ || this, options);
3468
3469      // child is a component instance
3470    } else {
3471      component = child;
3472    }
3473
3474    this.children_.splice(index, 0, component);
3475
3476    if (typeof component.id === 'function') {
3477      this.childIndex_[component.id()] = component;
3478    }
3479
3480    // If a name wasn't used to create the component, check if we can use the
3481    // name function of the component
3482    componentName = componentName || component.name && toTitleCase(component.name());
3483
3484    if (componentName) {
3485      this.childNameIndex_[componentName] = component;
3486    }
3487
3488    // Add the UI object's element to the container div (box)
3489    // Having an element is not required
3490    if (typeof component.el === 'function' && component.el()) {
3491      var childNodes = this.contentEl().children;
3492      var refNode = childNodes[index] || null;
3493
3494      this.contentEl().insertBefore(component.el(), refNode);
3495    }
3496
3497    // Return so it can stored on parent object if desired.
3498    return component;
3499  };
3500
3501  /**
3502   * Remove a child `Component` from this `Component`s list of children. Also removes
3503   * the child `Component`s element from this `Component`s element.
3504   *
3505   * @param {Component} component
3506   *        The child `Component` to remove.
3507   */
3508
3509
3510  Component.prototype.removeChild = function removeChild(component) {
3511    if (typeof component === 'string') {
3512      component = this.getChild(component);
3513    }
3514
3515    if (!component || !this.children_) {
3516      return;
3517    }
3518
3519    var childFound = false;
3520
3521    for (var i = this.children_.length - 1; i >= 0; i--) {
3522      if (this.children_[i] === component) {
3523        childFound = true;
3524        this.children_.splice(i, 1);
3525        break;
3526      }
3527    }
3528
3529    if (!childFound) {
3530      return;
3531    }
3532
3533    this.childIndex_[component.id()] = null;
3534    this.childNameIndex_[component.name()] = null;
3535
3536    var compEl = component.el();
3537
3538    if (compEl && compEl.parentNode === this.contentEl()) {
3539      this.contentEl().removeChild(component.el());
3540    }
3541  };
3542
3543  /**
3544   * Add and initialize default child `Component`s based upon options.
3545   */
3546
3547
3548  Component.prototype.initChildren = function initChildren() {
3549    var _this = this;
3550
3551    var children = this.options_.children;
3552
3553    if (children) {
3554      // `this` is `parent`
3555      var parentOptions = this.options_;
3556
3557      var handleAdd = function handleAdd(child) {
3558        var name = child.name;
3559        var opts = child.opts;
3560
3561        // Allow options for children to be set at the parent options
3562        // e.g. videojs(id, { controlBar: false });
3563        // instead of videojs(id, { children: { controlBar: false });
3564        if (parentOptions[name] !== undefined) {
3565          opts = parentOptions[name];
3566        }
3567
3568        // Allow for disabling default components
3569        // e.g. options['children']['posterImage'] = false
3570        if (opts === false) {
3571          return;
3572        }
3573
3574        // Allow options to be passed as a simple boolean if no configuration
3575        // is necessary.
3576        if (opts === true) {
3577          opts = {};
3578        }
3579
3580        // We also want to pass the original player options
3581        // to each component as well so they don't need to
3582        // reach back into the player for options later.
3583        opts.playerOptions = _this.options_.playerOptions;
3584
3585        // Create and add the child component.
3586        // Add a direct reference to the child by name on the parent instance.
3587        // If two of the same component are used, different names should be supplied
3588        // for each
3589        var newChild = _this.addChild(name, opts);
3590
3591        if (newChild) {
3592          _this[name] = newChild;
3593        }
3594      };
3595
3596      // Allow for an array of children details to passed in the options
3597      var workingChildren = void 0;
3598      var Tech = Component.getComponent('Tech');
3599
3600      if (Array.isArray(children)) {
3601        workingChildren = children;
3602      } else {
3603        workingChildren = Object.keys(children);
3604      }
3605
3606      workingChildren
3607      // children that are in this.options_ but also in workingChildren  would
3608      // give us extra children we do not want. So, we want to filter them out.
3609      .concat(Object.keys(this.options_).filter(function (child) {
3610        return !workingChildren.some(function (wchild) {
3611          if (typeof wchild === 'string') {
3612            return child === wchild;
3613          }
3614          return child === wchild.name;
3615        });
3616      })).map(function (child) {
3617        var name = void 0;
3618        var opts = void 0;
3619
3620        if (typeof child === 'string') {
3621          name = child;
3622          opts = children[name] || _this.options_[name] || {};
3623        } else {
3624          name = child.name;
3625          opts = child;
3626        }
3627
3628        return { name: name, opts: opts };
3629      }).filter(function (child) {
3630        // we have to make sure that child.name isn't in the techOrder since
3631        // techs are registerd as Components but can't aren't compatible
3632        // See https://github.com/videojs/video.js/issues/2772
3633        var c = Component.getComponent(child.opts.componentClass || toTitleCase(child.name));
3634
3635        return c && !Tech.isTech(c);
3636      }).forEach(handleAdd);
3637    }
3638  };
3639
3640  /**
3641   * Builds the default DOM class name. Should be overriden by sub-components.
3642   *
3643   * @return {string}
3644   *         The DOM class name for this object.
3645   *
3646   * @abstract
3647   */
3648
3649
3650  Component.prototype.buildCSSClass = function buildCSSClass() {
3651    // Child classes can include a function that does:
3652    // return 'CLASS NAME' + this._super();
3653    return '';
3654  };
3655
3656  /**
3657   * Bind a listener to the component's ready state.
3658   * Different from event listeners in that if the ready event has already happened
3659   * it will trigger the function immediately.
3660   *
3661   * @return {Component}
3662   *         Returns itself; method can be chained.
3663   */
3664
3665
3666  Component.prototype.ready = function ready(fn) {
3667    var sync = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false;
3668
3669    if (
3669fn) {
3670      if (this.isReady_) {
3671        if (sync) {
3672          fn.call(this);
3673        } else {
3674          // Call the function asynchronously by default for consistency
3675          this.setTimeout(fn, 1);
3676        }
3677      } else {
3678        this.readyQueue_ = this.readyQueue_ || [];
3679        this.readyQueue_.push(fn);
3680      }
3681    }
3682  };
3683
3684  /**
3685   * Trigger all the ready listeners for this `Component`.
3686   *
3687   * @fires Component#ready
3688   */
3689
3690
3691  Component.prototype.triggerReady = function triggerReady() {
3692    this.isReady_ = true;
3693
3694    // Ensure ready is trigger
vendor: 11,813 bytes, lines 3694-4077
3694d asynchronously
3695    this.setTimeout(function () {
3696      var readyQueue = this.readyQueue_;
3697
3698      // Reset Ready Queue
3699      this.readyQueue_ = [];
3700
3701      if (readyQueue && readyQueue.length > 0) {
3702        readyQueue.forEach(function (fn) {
3703          fn.call(this);
3704        }, this);
3705      }
3706
3707      // Allow for using event listeners also
3708      /**
3709       * Triggered when a `Component` is ready.
3710       *
3711       * @event Component#ready
3712       * @type {EventTarget~Event}
3713       */
3714      this.trigger('ready');
3715    }, 1);
3716  };
3717
3718  /**
3719   * Find a single DOM element matching a `selector`. This can be within the `Component`s
3720   * `contentEl()` or another custom context.
3721   *
3722   * @param {string} selector
3723   *        A valid CSS selector, which will be passed to `querySelector`.
3724   *
3725   * @param {Element|string} [context=this.contentEl()]
3726   *        A DOM element within which to query. Can also be a selector string in
3727   *        which case the first matching element will get used as context. If
3728   *        missing `this.contentEl()` gets used. If  `this.contentEl()` returns
3729   *        nothing it falls back to `document`.
3730   *
3731   * @return {Element|null}
3732   *         the dom element that was found, or null
3733   *
3734   * @see [Information on CSS Selectors](https://developer.mozilla.org/en-US/docs/Web/Guide/CSS/Getting_Started/Selectors)
3735   */
3736
3737
3738  Component.prototype.$ = function $$$1(selector, context) {
3739    return $(selector, context || this.contentEl());
3740  };
3741
3742  /**
3743   * Finds all DOM element matching a `selector`. This can be within the `Component`s
3744   * `contentEl()` or another custom context.
3745   *
3746   * @param {string} selector
3747   *        A valid CSS selector, which will be passed to `querySelectorAll`.
3748   *
3749   * @param {Element|string} [context=this.contentEl()]
3750   *        A DOM element within which to query. Can also be a selector string in
3751   *        which case the first matching element will get used as context. If
3752   *        missing `this.contentEl()` gets used. If  `this.contentEl()` returns
3753   *        nothing it falls back to `document`.
3754   *
3755   * @return {NodeList}
3756   *         a list of dom elements that were found
3757   *
3758   * @see [Information on CSS Selectors](https://developer.mozilla.org/en-US/docs/Web/Guide/CSS/Getting_Started/Selectors)
3759   */
3760
3761
3762  Component.prototype.$$ = function $$$$1(selector, context) {
3763    return $$(selector, context || this.contentEl());
3764  };
3765
3766  /**
3767   * Check if a component's element has a CSS class name.
3768   *
3769   * @param {string} classToCheck
3770   *        CSS class name to check.
3771   *
3772   * @return {boolean}
3773   *         - True if the `Component` has the class.
3774   *         - False if the `Component` does not have the class`
3775   */
3776
3777
3778  Component.prototype.hasClass = function hasClass$$1(classToCheck) {
3779    return hasClass(this.el_, classToCheck);
3780  };
3781
3782  /**
3783   * Add a CSS class name to the `Component`s element.
3784   *
3785   * @param {string} classToAdd
3786   *        CSS class name to add
3787   */
3788
3789
3790  Component.prototype.addClass = function addClass$$1(classToAdd) {
3791    addClass(this.el_, classToAdd);
3792  };
3793
3794  /**
3795   * Remove a CSS class name from the `Component`s element.
3796   *
3797   * @param {string} classToRemove
3798   *        CSS class name to remove
3799   */
3800
3801
3802  Component.prototype.removeClass = function removeClass$$1(classToRemove) {
3803    removeClass(this.el_, classToRemove);
3804  };
3805
3806  /**
3807   * Add or remove a CSS class name from the component's element.
3808   * - `classToToggle` gets added when {@link Component#hasClass} would return false.
3809   * - `classToToggle` gets removed when {@link Component#hasClass} would return true.
3810   *
3811   * @param  {string} classToToggle
3812   *         The class to add or remove based on (@link Component#hasClass}
3813   *
3814   * @param  {boolean|Dom~predicate} [predicate]
3815   *         An {@link Dom~predicate} function or a boolean
3816   */
3817
3818
3819  Component.prototype.toggleClass = function toggleClass$$1(classToToggle, predicate) {
3820    toggleClass(this.el_, classToToggle, predicate);
3821  };
3822
3823  /**
3824   * Show the `Component`s element if it is hidden by removing the
3825   * 'vjs-hidden' class name from it.
3826   */
3827
3828
3829  Component.prototype.show = function show() {
3830    this.removeClass('vjs-hidden');
3831  };
3832
3833  /**
3834   * Hide the `Component`s element if it is currently showing by adding the
3835   * 'vjs-hidden` class name to it.
3836   */
3837
3838
3839  Component.prototype.hide = function hide() {
3840    this.addClass('vjs-hidden');
3841  };
3842
3843  /**
3844   * Lock a `Component`s element in its visible state by adding the 'vjs-lock-showing'
3845   * class name to it. Used during fadeIn/fadeOut.
3846   *
3847   * @private
3848   */
3849
3850
3851  Component.prototype.lockShowing = function lockShowing() {
3852    this.addClass('vjs-lock-showing');
3853  };
3854
3855  /**
3856   * Unlock a `Component`s element from its visible state by removing the 'vjs-lock-showing'
3857   * class name from it. Used during fadeIn/fadeOut.
3858   *
3859   * @private
3860   */
3861
3862
3863  Component.prototype.unlockShowing = function unlockShowing() {
3864    this.removeClass('vjs-lock-showing');
3865  };
3866
3867  /**
3868   * Get the value of an attribute on the `Component`s element.
3869   *
3870   * @param {string} attribute
3871   *        Name of the attribute to get the value from.
3872   *
3873   * @return {string|null}
3874   *         - The value of the attribute that was asked for.
3875   *         - Can be an empty string on some browsers if the attribute does not exist
3876   *           or has no value
3877   *         - Most browsers will return null if the attibute does not exist or has
3878   *           no value.
3879   *
3880   * @see [DOM API]{@link https://developer.mozilla.org/en-US/docs/Web/API/Element/getAttribute}
3881   */
3882
3883
3884  Component.prototype.getAttribute = function getAttribute$$1(attribute) {
3885    return getAttribute(this.el_, attribute);
3886  };
3887
3888  /**
3889   * Set the value of an attribute on the `Component`'s element
3890   *
3891   * @param {string} attribute
3892   *        Name of the attribute to set.
3893   *
3894   * @param {string} value
3895   *        Value to set the attribute to.
3896   *
3897   * @see [DOM API]{@link https://developer.mozilla.org/en-US/docs/Web/API/Element/setAttribute}
3898   */
3899
3900
3901  Component.prototype.setAttribute = function setAttribute$$1(attribute, value) {
3902    setAttribute(this.el_, attribute, value);
3903  };
3904
3905  /**
3906   * Remove an attribute from the `Component`s element.
3907   *
3908   * @param {string} attribute
3909   *        Name of the attribute to remove.
3910   *
3911   * @see [DOM API]{@link https://developer.mozilla.org/en-US/docs/Web/API/Element/removeAttribute}
3912   */
3913
3914
3915  Component.prototype.removeAttribute = function removeAttribute$$1(attribute) {
3916    removeAttribute(this.el_, attribute);
3917  };
3918
3919  /**
3920   * Get or set the width of the component based upon the CSS styles.
3921   * See {@link Component#dimension} for more detailed information.
3922   *
3923   * @param {number|string} [num]
3924   *        The width that you want to set postfixed with '%', 'px' or nothing.
3925   *
3926   * @param {boolean} [skipListeners]
3927   *        Skip the componentresize event trigger
3928   *
3929   * @return {number|string}
3930   *         The width when getting, zero if there is no width. Can be a string
3931   *           postpixed with '%' or 'px'.
3932   */
3933
3934
3935  Component.prototype.width = function width(num, skipListeners) {
3936    return this.dimension('width', num, skipListeners);
3937  };
3938
3939  /**
3940   * Get or set the height of the component based upon the CSS styles.
3941   * See {@link Component#dimension} for more detailed information.
3942   *
3943   * @param {number|string} [num]
3944   *        The height that you want to set postfixed with '%', 'px' or nothing.
3945   *
3946   * @param {boolean} [skipListeners]
3947   *        Skip the componentresize event trigger
3948   *
3949   * @return {number|string}
3950   *         The width when getting, zero if there is no width. Can be a string
3951   *         postpixed with '%' or 'px'.
3952   */
3953
3954
3955  Component.prototype.height = function height(num, skipListeners) {
3956    return this.dimension('height', num, skipListeners);
3957  };
3958
3959  /**
3960   * Set both the width and height of the `Component` element at the same time.
3961   *
3962   * @param  {number|string} width
3963   *         Width to set the `Component`s element to.
3964   *
3965   * @param  {number|string} height
3966   *         Height to set the `Component`s element to.
3967   */
3968
3969
3970  Component.prototype.dimensions = function dimensions(width, height) {
3971    // Skip componentresize listeners on width for optimization
3972    this.width(width, true);
3973    this.height(height);
3974  };
3975
3976  /**
3977   * Get or set width or height of the `Component` element. This is the shared code
3978   * for the {@link Component#width} and {@link Component#height}.
3979   *
3980   * Things to know:
3981   * - If the width or height in an number this will return the number postfixed with 'px'.
3982   * - If the width/height is a percent this will return the percent postfixed with '%'
3983   * - Hidden elements have a width of 0 with `window.getComputedStyle`. This function
3984   *   defaults to the `Component`s `style.width` and falls back to `window.getComputedStyle`.
3985   *   See [this]{@link http://www.foliotek.com/devblog/getting-the-width-of-a-hidden-element-with-jquery-using-width/}
3986   *   for more information
3987   * - If you want the computed style of the component, use {@link Component#currentWidth}
3988   *   and {@link {Component#currentHeight}
3989   *
3990   * @fires Component#componentresize
3991   *
3992   * @param {string} widthOrHeight
3993   8        'width' or 'height'
3994   *
3995   * @param  {number|string} [num]
3996   8         New dimension
3997   *
3998   * @param  {boolean} [skipListeners]
3999   *         Skip componentresize event trigger
4000   *
4001   * @return {number}
4002   *         The dimension when getting or 0 if unset
4003   */
4004
4005
4006  Component.prototype.dimension = function dimension(widthOrHeight, num, skipListeners) {
4007    if (num !== undefined) {
4008      // Set to zero if null or literally NaN (NaN !== NaN)
4009      if (num === null || num !== num) {
4010        num = 0;
4011      }
4012
4013      // Check if using css width/height (% or px) and adjust
4014      if (('' + num).indexOf('%') !== -1 || ('' + num).indexOf('px') !== -1) {
4015        this.el_.style[widthOrHeight] = num;
4016      } else if (num === 'auto') {
4017        this.el_.style[widthOrHeight] = '';
4018      } else {
4019        this.el_.style[widthOrHeight] = num + 'px';
4020      }
4021
4022      // skipListeners allows us to avoid triggering the resize event when setting both width and height
4023      if (!skipListeners) {
4024        /**
4025         * Triggered when a component is resized.
4026         *
4027         * @event Component#componentresize
4028         * @type {EventTarget~Event}
4029         */
4030        this.trigger('componentresize');
4031      }
4032
4033      return;
4034    }
4035
4036    // Not setting a value, so getting it
4037    // Make sure element exists
4038    if (!this.el_) {
4039      return 0;
4040    }
4041
4042    // Get dimension value from style
4043    var val = this.el_.style[widthOrHeight];
4044    var pxIndex = val.indexOf('px');
4045
4046    if (pxIndex !== -1) {
4047      // Return the pixel value with no 'px'
4048      return parseInt(val.slice(0, pxIndex), 10);
4049    }
4050
4051    // No px so using % or no style was set, so falling back to offsetWidth/height
4052    // If component has display:none, offset will return 0
4053    // TODO: handle display:none and no dimension style using px
4054    return parseInt(this.el_['offset' + toTitleCase(widthOrHeight)], 10);
4055  };
4056
4057  /**
4058   * Get the width or the height of the `Component` elements computed style. Uses
4059   * `window.getComputedStyle`.
4060   *
4061   * @param {string} widthOrHeight
4062   *        A string containing 'width' or 'height'. Whichever one you want to get.
4063   *
4064   * @return {number}
4065   *         The dimension that gets asked for or 0 if nothing was set
4066   *         for that dimension.
4067   */
4068
4069
4070  Component.prototype.currentDimension = function currentDimension(widthOrHeight) {
4071    var computedWidthOrHeight = 0;
4072
4073    if (widthOrHeight !== 'width' && widthOrHeight !== 'height') {
4074      throw new Error('currentDimension only accepts width or height value');
4075    }
4076
4077    if (typeof 
vendor: 11,564 bytes, lines 4077-4416
4077window_1.getComputedStyle === 'function') {
4078      var computedStyle = window_1.getComputedStyle(this.el_);
4079
4080      computedWidthOrHeight = computedStyle.getPropertyValue(widthOrHeight) || computedStyle[widthOrHeight];
4081    }
4082
4083    // remove 'px' from variable and parse as integer
4084    computedWidthOrHeight = parseFloat(computedWidthOrHeight);
4085
4086    // if the computed value is still 0, it's possible that the browser is lying
4087    // and we want to check the offset values.
4088    // This code also runs on IE8 and wherever getComputedStyle doesn't exist.
4089    if (computedWidthOrHeight === 0) {
4090      var rule = 'offset' + toTitleCase(widthOrHeight);
4091
4092      computedWidthOrHeight = this.el_[rule];
4093    }
4094
4095    return computedWidthOrHeight;
4096  };
4097
4098  /**
4099   * An object that contains width and height values of the `Component`s
4100   * computed style. Uses `window.getComputedStyle`.
4101   *
4102   * @typedef {Object} Component~DimensionObject
4103   *
4104   * @property {number} width
4105   *           The width of the `Component`s computed style.
4106   *
4107   * @property {number} height
4108   *           The height of the `Component`s computed style.
4109   */
4110
4111  /**
4112   * Get an object that contains width and height values of the `Component`s
4113   * computed style.
4114   *
4115   * @return {Component~DimensionObject}
4116   *         The dimensions of the components element
4117   */
4118
4119
4120  Component.prototype.currentDimensions = function currentDimensions() {
4121    return {
4122      width: this.currentDimension('width'),
4123      height: this.currentDimension('height')
4124    };
4125  };
4126
4127  /**
4128   * Get the width of the `Component`s computed style. Uses `window.getComputedStyle`.
4129   *
4130   * @return {number} width
4131   *           The width of the `Component`s computed style.
4132   */
4133
4134
4135  Component.prototype.currentWidth = function currentWidth() {
4136    return this.currentDimension('width');
4137  };
4138
4139  /**
4140   * Get the height of the `Component`s computed style. Uses `window.getComputedStyle`.
4141   *
4142   * @return {number} height
4143   *           The height of the `Component`s computed style.
4144   */
4145
4146
4147  Component.prototype.currentHeight = function currentHeight() {
4148    return this.currentDimension('height');
4149  };
4150
4151  /**
4152   * Set the focus to this component
4153   */
4154
4155
4156  Component.prototype.focus = function focus() {
4157    this.el_.focus();
4158  };
4159
4160  /**
4161   * Remove the focus from this component
4162   */
4163
4164
4165  Component.prototype.blur = function blur() {
4166    this.el_.blur();
4167  };
4168
4169  /**
4170   * Emit a 'tap' events when touch event support gets detected. This gets used to
4171   * support toggling the controls through a tap on the video. They get enabled
4172   * because every sub-component would have extra overhead otherwise.
4173   *
4174   * @private
4175   * @fires Component#tap
4176   * @listens Component#touchstart
4177   * @listens Component#touchmove
4178   * @listens Component#touchleave
4179   * @listens Component#touchcancel
4180   * @listens Component#touchend
4181    */
4182
4183
4184  Component.prototype.emitTapEvents = function emitTapEvents() {
4185    // Track the start time so we can determine how long the touch lasted
4186    var touchStart = 0;
4187    var firstTouch = null;
4188
4189    // Maximum movement allowed during a touch event to still be considered a tap
4190    // Other popular libs use anywhere from 2 (hammer.js) to 15,
4191    // so 10 seems like a nice, round number.
4192    var tapMovementThreshold = 10;
4193
4194    // The maximum length a touch can be while still being considered a tap
4195    var touchTimeThreshold = 200;
4196
4197    var couldBeTap = void 0;
4198
4199    this.on('touchstart', function (event) {
4200      // If more than one finger, don't consider treating this as a click
4201      if (event.touches.length === 1) {
4202        // Copy pageX/pageY from the object
4203        firstTouch = {
4204          pageX: event.touches[0].pageX,
4205          pageY: event.touches[0].pageY
4206        };
4207        // Record start time so we can detect a tap vs. "touch and hold"
4208        touchStart = new Date().getTime();
4209        // Reset couldBeTap tracking
4210        couldBeTap = true;
4211      }
4212    });
4213
4214    this.on('touchmove', function (event) {
4215      // If more than one finger, don't consider treating this as a click
4216      if (event.touches.length > 1) {
4217        couldBeTap = false;
4218      } else if (firstTouch) {
4219        // Some devices will throw touchmoves for all but the slightest of taps.
4220        // So, if we moved only a small distance, this could still be a tap
4221        var xdiff = event.touches[0].pageX - firstTouch.pageX;
4222        var ydiff = event.touches[0].pageY - firstTouch.pageY;
4223        var touchDistance = Math.sqrt(xdiff * xdiff + ydiff * ydiff);
4224
4225        if (touchDistance > tapMovementThreshold) {
4226          couldBeTap = false;
4227        }
4228      }
4229    });
4230
4231    var noTap = function noTap() {
4232      couldBeTap = false;
4233    };
4234
4235    // TODO: Listen to the original target. http://youtu.be/DujfpXOKUp8?t=13m8s
4236    this.on('touchleave', noTap);
4237    this.on('touchcancel', noTap);
4238
4239    // When the touch ends, measure how long it took and trigger the appropriate
4240    // event
4241    this.on('touchend', function (event) {
4242      firstTouch = null;
4243      // Proceed only if the touchmove/leave/cancel event didn't happen
4244      if (couldBeTap === true) {
4245        // Measure how long the touch lasted
4246        var touchTime = new Date().getTime() - touchStart;
4247
4248        // Make sure the touch was less than the threshold to be considered a tap
4249        if (touchTime < touchTimeThreshold) {
4250          // Don't let browser turn this into a click
4251          event.preventDefault();
4252          /**
4253           * Triggered when a `Component` is tapped.
4254           *
4255           * @event Component#tap
4256           * @type {EventTarget~Event}
4257           */
4258          this.trigger('tap');
4259          // It may be good to copy the touchend event object and change the
4260          // type to tap, if the other event properties aren't exact after
4261          // Events.fixEvent runs (e.g. event.target)
4262        }
4263      }
4264    });
4265  };
4266
4267  /**
4268   * This function reports user activity whenever touch events happen. This can get
4269   * turned off by any sub-components that wants touch events to act another way.
4270   *
4271   * Report user touch activity when touch events occur. User activity gets used to
4272   * determine when controls should show/hide. It is simple when it comes to mouse
4273   * events, because any mouse event should show the controls. So we capture mouse
4274   * events that bubble up to the player and report activity when that happens.
4275   * With touch events it isn't as easy as `touchstart` and `touchend` toggle player
4276   * controls. So touch events can't help us at the player level either.
4277   *
4278   * User activity gets checked asynchronously. So what could happen is a tap event
4279   * on the video turns the controls off. Then the `touchend` event bubbles up to
4280   * the player. Which, if it reported user activity, would turn the controls right
4281   * back on. We also don't want to completely block touch events from bubbling up.
4282   * Furthermore a `touchmove` event and anything other than a tap, should not turn
4283   * controls back on.
4284   *
4285   * @listens Component#touchstart
4286   * @listens Component#touchmove
4287   * @listens Component#touchend
4288   * @listens Component#touchcancel
4289   */
4290
4291
4292  Component.prototype.enableTouchActivity = function enableTouchActivity() {
4293    // Don't continue if the root player doesn't support reporting user activity
4294    if (!this.player() || !this.player().reportUserActivity) {
4295      return;
4296    }
4297
4298    // listener for reporting that the user is active
4299    var report = bind(this.player(), this.player().reportUserActivity);
4300
4301    var touchHolding = void 0;
4302
4303    this.on('touchstart', function () {
4304      report();
4305      // For as long as the they are touching the device or have their mouse down,
4306      // we consider them active even if they're not moving their finger or mouse.
4307      // So we want to continue to update that they are active
4308      this.clearInterval(touchHolding);
4309      // report at the same interval as activityCheck
4310      touchHolding = this.setInterval(report, 250);
4311    });
4312
4313    var touchEnd = function touchEnd(event) {
4314      report();
4315      // stop the interval that maintains activity if the touch is holding
4316      this.clearInterval(touchHolding);
4317    };
4318
4319    this.on('touchmove', report);
4320    this.on('touchend', touchEnd);
4321    this.on('touchcancel', touchEnd);
4322  };
4323
4324  /**
4325   * A callback that has no parameters and is bound into `Component`s context.
4326   *
4327   * @callback Component~GenericCallback
4328   * @this Component
4329   */
4330
4331  /**
4332   * Creates a function that runs after an `x` millisecond timeout. This function is a
4333   * wrapper around `window.setTimeout`. There are a few reasons to use this one
4334   * instead though:
4335   * 1. It gets cleared via  {@link Component#clearTimeout} when
4336   *    {@link Component#dispose} gets called.
4337   * 2. The function callback will gets turned into a {@link Component~GenericCallback}
4338   *
4339   * > Note: You can use `window.clearTimeout` on the id returned by this function. This
4340   *         will cause its dispose listener not to get cleaned up! Please use
4341   *         {@link Component#clearTimeout} or {@link Component#dispose}.
4342   *
4343   * @param {Component~GenericCallback} fn
4344   *        The function that will be run after `timeout`.
4345   *
4346   * @param {number} timeout
4347   *        Timeout in milliseconds to delay before executing the specified function.
4348   *
4349   * @return {number}
4350   *         Returns a timeout ID that gets used to identify the timeout. It can also
4351   *         get used in {@link Component#clearTimeout} to clear the timeout that
4352   *         was set.
4353   *
4354   * @listens Component#dispose
4355   * @see [Similar to]{@link https://developer.mozilla.org/en-US/docs/Web/API/WindowTimers/setTimeout}
4356   */
4357
4358
4359  Component.prototype.setTimeout = function setTimeout(fn, timeout) {
4360    fn = bind(this, fn);
4361
4362    var timeoutId = window_1.setTimeout(fn, timeout);
4363    var disposeFn = function disposeFn() {
4364      this.clearTimeout(timeoutId);
4365    };
4366
4367    disposeFn.guid = 'vjs-timeout-' + timeoutId;
4368
4369    this.on('dispose', disposeFn);
4370
4371    return timeoutId;
4372  };
4373
4374  /**
4375   * Clears a timeout that gets created via `window.setTimeout` or
4376   * {@link Component#setTimeout}. If you set a timeout via {@link Component#setTimeout}
4377   * use this function instead of `window.clearTimout`. If you don't your dispose
4378   * listener will not get cleaned up until {@link Component#dispose}!
4379   *
4380   * @param {number} timeoutId
4381   *        The id of the timeout to clear. The return value of
4382   *        {@link Component#setTimeout} or `window.setTimeout`.
4383   *
4384   * @return {number}
4385   *         Returns the timeout id that was cleared.
4386   *
4387   * @see [Similar to]{@link https://developer.mozilla.org/en-US/docs/Web/API/WindowTimers/clearTimeout}
4388   */
4389
4390
4391  Component.prototype.clearTimeout = function clearTimeout(timeoutId) {
4392    window_1.clearTimeout(timeoutId);
4393
4394    var disposeFn = function disposeFn() {};
4395
4396    disposeFn.guid = 'vjs-timeout-' + timeoutId;
4397
4398    this.off('dispose', disposeFn);
4399
4400    return timeoutId;
4401  };
4402
4403  /**
4404   * Creates a function that gets run every `x` milliseconds. This function is a wrapper
4405   * around `window.setInterval`. There are a few reasons to use this one instead though.
4406   * 1. It gets cleared via  {@link Component#clearInterval} when
4407   *    {@link Component#dispose} gets called.
4408   * 2. The function callback will be a {@link Component~GenericCallback}
4409   *
4410   * @param {Component~GenericCallback} fn
4411   *        The function to run every `x` seconds.
4412   *
4413   * @param {number} interval
4414   *        Execute the specified function every `x` milliseconds.
4415   *
4416   * @return {number}
4417   *         Returns an id that can be used to identify the interval. It can also be be used in
4418   *         {@link Component#clearInterval} to clear the interval.
4419   *
4420   * @listens Component#dispose
4421   * @see [Similar to]{@link https://developer.mozilla.org/en-US/docs/Web/API/WindowTimers/setInterval}
4422   */
4423
4424
4425  Component.prototype.setInterval = function setInterval(fn, interval) {
4426    fn = bind(this, fn);
4427
4428    var intervalId = window_1.setInterval(fn, interval);
4429
4430    var disposeFn = function disposeFn() {
4431      this.clearInterval(intervalId);
4432    };
4433
4434    disposeFn.guid = 'vjs-interval-' + intervalId;
4435
4436    this.on('dispose', disposeFn);
4437
4438    return intervalId;
4439  };
4440
4441  /**
4442   * Clears an interval that gets created via `window.setInterval` or
4443   * {@link Component#setInterval}. If you set an inteval via {@link Component#setInterval}
4444   * use this function instead of `window.clearInterval`. If you don't your dispose
4445   * listener will not get cleaned up until {@link Component#dispose}!
4446   *
4447   * @param {number} intervalId
4448   *        The id of the interval to clear. The return value of
4449   *        {@link Component#setInterval} or `window.setInterval`.
4450   *
4451   * @return {number}
4452   *         Returns the interval id that was cleared.
4453   *
4454   * @see [Similar to]{@link https://developer.mozilla.org/en-US/docs/Web/API/WindowTimers/clearInterval}
4455   */
4456
4457
4458  Component.prototype.clearInterval = function clearInterval(intervalId) {
4459    window_1.clearInterval(intervalId);
4460
4461    var disposeFn = function disposeFn() {};
4462
4463    disposeFn.guid = 'vjs-interval-' + intervalId;
4464
4465    this.off('dispose', disposeFn);
4466
4467    return intervalId;
4468  };
4469
4470  /**
4471   * Queues up a callback to be passed to requestAnimationFrame (rAF), but
4472   * with a few extra bonuses:
4473   *
4474   * - Supports browsers that do not support rAF by falling back to
4475   *   {@link Component#setTimeout}.
4476   *
4477   * - The callback is turned into a {@link Component~GenericCallback} (i.e.
4478   *   bound to the component).
4479   *
4480   * - Automatic cancellation of the rAF callback is handled if the component
4481   *   is disposed before it is called.
4482   *
4483   * @param  {Component~GenericCallback} fn
4484   *         A function that will be bound to this component and executed just
4485   *         before the browser's next repaint.
4486   *
4487   * @return {number}
4488   *         Returns an rAF ID that gets used to identify the timeout. It can
4489   *         also be used in {@link Component#cancelAnimationFrame} to cancel
4490   *         the animation frame callback.
4491   *
4492   * @listens Component#dispose
4493   * @see [Similar to]{@link https://developer.mozilla.org/en-US/docs/Web/API/window/requestAnimationFrame}
4494   */
4495
4496
4497  Component.prototype.requestAnimationFrame = function requestAnimationFrame(fn) {
4498    var _this2 = this;
4499
4500    if (this.supportsRaf_) {
4501      fn = bind(this, fn);
4502
4503      var id = window_1.requestAnimationFrame(fn);
4504      var disposeFn = function disposeFn() {
4505        return _this2.cancelAnimationFrame(id);
4506      };
4507
4508      disposeFn.guid = 'vjs-raf-' + id;
4509      this.on('dispose', disposeFn);
4510
4511      return id;
4512    }
4513
4514    // Fall back to using a timer.
4515    return this.setTimeout(fn, 1000 / 60);
4516  };
4517
4518  /**
4519   * Cancels a queued callback passed to {@link Component#requestAnimationFrame}
4520   * (rAF).
4521   *
4522   * If you queue an rAF callback via {@link Component#requestAnimationFrame},
4523   * use this function instead of `window.cancelAnimationFrame`. If you don't,
4524   * your dispose listener will not get cleaned up until {@link Component#dispose}!
4525   *
4526   * @param {number} id
4527   *        The rAF ID to clear. The return value of {@link Component#requestAnimationFrame}.
4528   *
4529   * @return {number}
4530   *         Returns the rAF ID that was cleared.
4531   *
4532   * @see [Similar to]{@link https://developer.mozilla.org/en-US/docs/Web/API/window/cancelAnimationFrame}
4533   */
4534
4535
4536  Component.prototype.cancelAnimationFrame = function cancelAnimationFrame(id) {
4537    if (this.supportsRaf_) {
4538      window_1.cancelAnimationFrame(id);
4539
4540      var disposeFn = function disposeFn() {};
4541
4542      disposeFn.guid = 'vjs-raf-' + id;
4543
4544      this.off('dispose', disposeFn);
4545
4546      return id;
4547    }
4548
4549    // Fall back to using a timer.
4550    return this.clearTimeout(id);
4551  };
4552
4553  /**
4554   * Register a `Component` with `videojs` given the name and the component.
4555   *
4556   * > NOTE: {@link Tech}s should not be registered as a `Component`. {@link Tech}s
4557   *         should be registered using {@link Tech.registerTech} or
4558   *         {@link videojs:videojs.registerTech}.
4559   *
4560   * > NOTE: This function can also be seen on videojs as
4561   *         {@link videojs:videojs.registerComponent}.
4562   *
4563   * @param {string} name
4564   *        The name of the `Component` to register.
4565   *
4566   * @param {Component} ComponentToRegister
4567   *        The `Component` class to register.
4568   *
4569   * @return {Component}
4570   *         The `Component` that was registered.
4571   */
4572
4573
4574  Component.registerComponent = function registerComponent(name, ComponentToRegister) {
4575    if (typeof name !== 'string' || !name) {
4576      throw new Error('Illegal component name, "' + name + '"; must be a non-empty string.');
4577    }
4578
4579    var Tech = Component.getComponent('Tech');
4580
4581    // We need to make sure this check is only done if Tech has been registered.
4582    var isTech = Tech && Tech.isTech(ComponentToRegister);
4583    var isComp = Component === ComponentToRegister || Component.prototype.isPrototypeOf(ComponentToRegister.prototype);
4584
4585    if (isTech || !isComp) {
4586      var reason = void 0;
4587
4588      if (isTech) {
4589        reason = 'techs must be registered using Tech.registerTech()';
4590      } else {
4591        reason = 'must be a Component subclass';
4592      }
4593
4594      throw new Error('Illegal component, "' + name + '"; ' + reason + '.');
4595    }
4596
4597    name = toTitleCase(name);
4598
4599    if (!Component.components_) {
4600      Component.components_ = {};
4601    }
4602
4603    var Player = Component.getComponent('Player');
4604
4605    if (name === 'Player' && Player && Player.players) {
4606      var players = Player.players;
4607      var playerNames = Object.keys(players);
4608
4609      // If we have players that were disposed, then their name will still be
4610      // in Players.players. So, we must loop through and verify that the value
4611      // for each item is not null. This allows registration of the Player component
4612      // after all players have been disposed or before any were created.
4613      if (players && playerNames.length > 0 && playerNames.map(function (pname) {
4614        return players[pname];
4615      }).every(Boolean)) {
4616        throw new Error('Can not register Player component after player has been created.');
4617      }
4618    }
4619
4620    Component.components_[name] = ComponentToRegister;
4621
4622    return ComponentToRegister;
4623  };
4624
4625  /**
4626   * Get a `Component` based on the name it was registered with.
4627   *
4628   * @param {string} name
4629   *        The Name of the component to get.
4630   *
4631   * @return {Component}
4632   *         The `Component` that got registered under the given name.
4633   *
4634   * @deprecated In `videojs` 6 this will not return `Component`s that were not
4635   *             registered using {@link Component.registerComponent}. Currently we
4636   *             check the global `videojs` object for a `Component` name and
4637   *             return that if it exists.
4638   */
4639
4640
4641  Component.getComponent = function getComponent(name) {
4642    if (!name) {
4643      return;
4644    }
4645
4646    name = toTitleCase(name);
4647
4648    if (Component.components_ && Component.components_[name]) {
4649      return Component.components_[name];
4650    }
4651  };
4652
4653  return Component;
4654}();
4655
4656/**
4657 * Whether or not this component supports `requestAnimationFrame`.
4658 *
4659 * This is exposed primarily for testing purposes.
4660 *
4661 * @private
4662 * @type {Boolean}
4663 */
4664
4665
4666Component.prototype.supportsRaf_ = typeof window_1.requestAnimationFrame === 'function' && typeof window_1.cancelAnimationFrame === 'function';
4667
4668Component.registerComponent('Component', Component);
4669
4670/**
4671 * @file time-ranges.js
4672 * @module time-ranges
4673 */
4674
4675/**
4676 * Returns the time for the specified index at the start or end
4677 * of a TimeRange object.
4678 *
4679 * @function time-ranges:indexFunction
4680 *
4681 * @param {number} [index=0]
4682 *        The range number to return the time for.
4683 *
4684 * @return {number}
4685 *         The time that offset at the specified index.
4686 *
4687 * @depricated index must be set to a value, in the future this will throw an error.
4688 */
4689
4690/**
4691 * An object that contains ranges of time for various reasons.
4692 *
4693 * @typedef {Object} TimeRange
4694 *
4695 * @property {number} length
4696 *           The number of time ranges represented by this Object
4697 *
4698 * @property {time-ranges:indexFunction} start
4699 *           Returns the time offset at which a specified time range begins.
4700 *
4701 * @property {time-ranges:indexFunction} end
4702 *           Returns the time offset at which a specified time range begins.
4703 *
4704 * @see https://developer.mozilla.org/en-US/docs/Web/API/TimeRanges
4705 */
4706
vendor: 6,212 bytes, lines 4707-4917
4707/**
4708 * Check if any of the time ranges are over the maximum index.
4709 *
4710 * @param {string} fnName
4711 *        The function name to use for logging
4712 *
4713 * @param {number} index
4714 *        The index to check
4715 *
4716 * @param {number} maxIndex
4717 *        The maximum possible index
4718 *
4719 * @throws {Error} if the timeRanges provided are over the maxIndex
4720 */
4721function rangeCheck(fnName, index, maxIndex) {
4722  if (typeof index !== 'number' || index < 0 || index > maxIndex) {
4723    throw new Error('Failed to execute \'' + fnName + '\' on \'TimeRanges\': The index provided (' + index + ') is non-numeric or out of bounds (0-' + maxIndex + ').');
4724  }
4725}
4726
4727/**
4728 * Check if any of the time ranges are over the maximum index.
4729 *
4730 * @param {string} fnName
4731 *        The function name to use for logging
4732 *
4733 * @param {string} valueIndex
4734 *        The proprety that should be used to get the time. should be 'start' or 'end'
4735 *
4736 * @param {Array} ranges
4737 *        An array of time ranges
4738 *
4739 * @param {Array} [rangeIndex=0]
4740 *        The index to start the search at
4741 *
4742 * @return {number}
4743 *         The time that offset at the specified index.
4744 *
4745 *
4746 * @depricated rangeIndex must be set to a value, in the future this will throw an error.
4747 * @throws {Error} if rangeIndex is more than the length of ranges
4748 */
4749function getRange(fnName, valueIndex, ranges, rangeIndex) {
4750  rangeCheck(fnName, rangeIndex, ranges.length - 1);
4751  return ranges[rangeIndex][valueIndex];
4752}
4753
4754/**
4755 * Create a time range object givent ranges of time.
4756 *
4757 * @param {Array} [ranges]
4758 *        An array of time ranges.
4759 */
4760function createTimeRangesObj(ranges) {
4761  if (ranges === undefined || ranges.length === 0) {
4762    return {
4763      length: 0,
4764      start: function start() {
4765        throw new Error('This TimeRanges object is empty');
4766      },
4767      end: function end() {
4768        throw new Error('This TimeRanges object is empty');
4769      }
4770    };
4771  }
4772  return {
4773    length: ranges.length,
4774    start: getRange.bind(null, 'start', 0, ranges),
4775    end: getRange.bind(null, 'end', 1, ranges)
4776  };
4777}
4778
4779/**
4780 * Should create a fake `TimeRange` object which mimics an HTML5 time range instance.
4781 *
4782 * @param {number|Array} start
4783 *        The start of a single range or an array of ranges
4784 *
4785 * @param {number} end
4786 *        The end of a single range.
4787 *
4788 * @private
4789 */
4790function createTimeRanges(start, end) {
4791  if (Array.isArray(start)) {
4792    return createTimeRangesObj(start);
4793  } else if (start === undefined || end === undefined) {
4794    return createTimeRangesObj();
4795  }
4796  return createTimeRangesObj([[start, end]]);
4797}
4798
4799/**
4800 * @file buffer.js
4801 * @module buffer
4802 */
4803/**
4804 * Compute the percentage of the media that has been buffered.
4805 *
4806 * @param {TimeRange} buffered
4807 *        The current `TimeRange` object representing buffered time ranges
4808 *
4809 * @param {number} duration
4810 *        Total duration of the media
4811 *
4812 * @return {number}
4813 *         Percent buffered of the total duration in decimal form.
4814 */
4815function bufferedPercent(buffered, duration) {
4816  var bufferedDuration = 0;
4817  var start = void 0;
4818  var end = void 0;
4819
4820  if (!duration) {
4821    return 0;
4822  }
4823
4824  if (!buffered || !buffered.length) {
4825    buffered = createTimeRanges(0, 0);
4826  }
4827
4828  for (var i = 0; i < buffered.length; i++) {
4829    start = buffered.start(i);
4830    end = buffered.end(i);
4831
4832    // buffered end can be bigger than duration by a very small fraction
4833    if (end > duration) {
4834      end = duration;
4835    }
4836
4837    bufferedDuration += end - start;
4838  }
4839
4840  return bufferedDuration / duration;
4841}
4842
4843/**
4844 * @file fullscreen-api.js
4845 * @module fullscreen-api
4846 * @private
4847 */
4848/**
4849 * Store the browser-specific methods for the fullscreen API.
4850 *
4851 * @type {Object}
4852 * @see [Specification]{@link https://fullscreen.spec.whatwg.org}
4853 * @see [Map Approach From Screenfull.js]{@link https://github.com/sindresorhus/screenfull.js}
4854 */
4855var FullscreenApi = {};
4856
4857// browser API methods
4858var apiMap = [['requestFullscreen', 'exitFullscreen', 'fullscreenElement', 'fullscreenEnabled', 'fullscreenchange', 'fullscreenerror'],
4859// WebKit
4860['webkitRequestFullscreen', 'webkitExitFullscreen', 'webkitFullscreenElement', 'webkitFullscreenEnabled', 'webkitfullscreenchange', 'webkitfullscreenerror'],
4861// Old WebKit (Safari 5.1)
4862['webkitRequestFullScreen', 'webkitCancelFullScreen', 'webkitCurrentFullScreenElement', 'webkitCancelFullScreen', 'webkitfullscreenchange', 'webkitfullscreenerror'],
4863// Mozilla
4864['mozRequestFullScreen', 'mozCancelFullScreen', 'mozFullScreenElement', 'mozFullScreenEnabled', 'mozfullscreenchange', 'mozfullscreenerror'],
4865// Microsoft
4866['msRequestFullscreen', 'msExitFullscreen', 'msFullscreenElement', 'msFullscreenEnabled', 'MSFullscreenChange', 'MSFullscreenError']];
4867
4868var specApi = apiMap[0];
4869var browserApi = void 0;
4870
4871// determine the supported set of functions
4872for (var i = 0; i < apiMap.length; i++) {
4873  // check for exitFullscreen function
4874  if (apiMap[i][1] in document_1) {
4875    browserApi = apiMap[i];
4876    break;
4877  }
4878}
4879
4880// map the browser API names to the spec API names
4881if (browserApi) {
4882  for (var _i = 0; _i < browserApi.length; _i++) {
4883    FullscreenApi[specApi[_i]] = browserApi[_i];
4884  }
4885}
4886
4887/**
4888 * @file media-error.js
4889 */
4890/**
4891 * A Custom `MediaError` class which mimics the standard HTML5 `MediaError` class.
4892 *
4893 * @param {number|string|Object|MediaError} value
4894 *        This can be of multiple types:
4895 *        - number: should be a standard error code
4896 *        - string: an error message (the code will be 0)
4897 *        - Object: arbitrary properties
4898 *        - `MediaError` (native): used to populate a video.js `MediaError` object
4899 *        - `MediaError` (video.js): will return itself if it's already a
4900 *          video.js `MediaError` object.
4901 *
4902 * @see [MediaError Spec]{@link https://dev.w3.org/html5/spec-author-view/video.html#mediaerror}
4903 * @see [Encrypted MediaError Spec]{@link https://www.w3.org/TR/2013/WD-encrypted-media-20130510/#error-codes}
4904 *
4905 * @class MediaError
4906 */
4907function MediaError(value) {
4908
4909  // Allow redundant calls to this constructor to avoid having `instanceof`
4910  // checks peppered around the code.
4911  if (value instanceof MediaError) {
4912    return value;
4913  }
4914
4915  if (typeof value === 'number') {
4916    this.code = value;
4917  } else if (typeof value === 'string') {
vendor: 5,604 bytes, lines 4918-5102
4918    // default code is zero, so this is a custom error
4919    this.message = value;
4920  } else if (isObject(value)) {
4921
4922    // We assign the `code` property manually because native `MediaError` objects
4923    // do not expose it as an own/enumerable property of the object.
4924    if (typeof value.code === 'number') {
4925      this.code = value.code;
4926    }
4927
4928    assign(this, value);
4929  }
4930
4931  if (!this.message) {
4932    this.message = MediaError.defaultMessages[this.code] || '';
4933  }
4934}
4935
4936/**
4937 * The error code that refers two one of the defined `MediaError` types
4938 *
4939 * @type {Number}
4940 */
4941MediaError.prototype.code = 0;
4942
4943/**
4944 * An optional message that to show with the error. Message is not part of the HTML5
4945 * video spec but allows for more informative custom errors.
4946 *
4947 * @type {String}
4948 */
4949MediaError.prototype.message = '';
4950
4951/**
4952 * An optional status code that can be set by plugins to allow even more detail about
4953 * the error. For example a plugin might provide a specific HTTP status code and an
4954 * error message for that code. Then when the plugin gets that error this class will
4955 * know how to display an error message for it. This allows a custom message to show
4956 * up on the `Player` error overlay.
4957 *
4958 * @type {Array}
4959 */
4960MediaError.prototype.status = null;
4961
4962/**
4963 * Errors indexed by the W3C standard. The order **CANNOT CHANGE**! See the
4964 * specification listed under {@link MediaError} for more information.
4965 *
4966 * @enum {array}
4967 * @readonly
4968 * @property {string} 0 - MEDIA_ERR_CUSTOM
4969 * @property {string} 1 - MEDIA_ERR_CUSTOM
4970 * @property {string} 2 - MEDIA_ERR_ABORTED
4971 * @property {string} 3 - MEDIA_ERR_NETWORK
4972 * @property {string} 4 - MEDIA_ERR_SRC_NOT_SUPPORTED
4973 * @property {string} 5 - MEDIA_ERR_ENCRYPTED
4974 */
4975MediaError.errorTypes = ['MEDIA_ERR_CUSTOM', 'MEDIA_ERR_ABORTED', 'MEDIA_ERR_NETWORK', 'MEDIA_ERR_DECODE', 'MEDIA_ERR_SRC_NOT_SUPPORTED', 'MEDIA_ERR_ENCRYPTED'];
4976
4977/**
4978 * The default `MediaError` messages based on the {@link MediaError.errorTypes}.
4979 *
4980 * @type {Array}
4981 * @constant
4982 */
4983MediaError.defaultMessages = {
4984  1: 'You aborted the media playback',
4985  2: 'A network error caused the media download to fail part-way.',
4986  3: 'The media playback was aborted due to a corruption problem or because the media used features your browser did not support.',
4987  4: 'The media could not be loaded, either because the server or network failed or because the format is not supported.',
4988  5: 'The media is encrypted and we do not have the keys to decrypt it.'
4989};
4990
4991// Add types as properties on MediaError
4992// e.g. MediaError.MEDIA_ERR_SRC_NOT_SUPPORTED = 4;
4993for (var errNum = 0; errNum < MediaError.errorTypes.length; errNum++) {
4994  MediaError[MediaError.errorTypes[errNum]] = errNum;
4995  // values should be accessible on both the class and instance
4996  MediaError.prototype[MediaError.errorTypes[errNum]] = errNum;
4997}
4998
4999var tuple = SafeParseTuple;
5000
5001function SafeParseTuple(obj, reviver) {
5002    var json;
5003    var error = null;
5004
5005    try {
5006        json = JSON.parse(obj, reviver);
5007    } catch (err) {
5008        error = err;
5009    }
5010
5011    return [error, json]
5012}
5013
5014/**
5015 * @file text-track-list-converter.js Utilities for capturing text track state and
5016 * re-creating tracks based on a capture.
5017 *
5018 * @module text-track-list-converter
5019 */
5020
5021/**
5022 * Examine a single {@link TextTrack} and return a JSON-compatible javascript object that
5023 * represents the {@link TextTrack}'s state.
5024 *
5025 * @param {TextTrack} track
5026 *        The text track to query.
5027 *
5028 * @return {Object}
5029 *         A serializable javascript representation of the TextTrack.
5030 * @private
5031 */
5032var trackToJson_ = function trackToJson_(track) {
5033  var ret = ['kind', 'label', 'language', 'id', 'inBandMetadataTrackDispatchType', 'mode', 'src'].reduce(function (acc, prop, i) {
5034
5035    if (track[prop]) {
5036      acc[prop] = track[prop];
5037    }
5038
5039    return acc;
5040  }, {
5041    cues: track.cues && Array.prototype.map.call(track.cues, function (cue) {
5042      return {
5043        startTime: cue.startTime,
5044        endTime: cue.endTime,
5045        text: cue.text,
5046        id: cue.id
5047      };
5048    })
5049  });
5050
5051  return ret;
5052};
5053
5054/**
5055 * Examine a {@link Tech} and return a JSON-compatible javascript array that represents the
5056 * state of all {@link TextTrack}s currently configured. The return array is compatible with
5057 * {@link text-track-list-converter:jsonToTextTracks}.
5058 *
5059 * @param {Tech} tech
5060 *        The tech object to query
5061 *
5062 * @return {Array}
5063 *         A serializable javascript representation of the {@link Tech}s
5064 *         {@link TextTrackList}.
5065 */
5066var textTracksToJson = function textTracksToJson(tech) {
5067
5068  var trackEls = tech.$$('track');
5069
5070  var trackObjs = Array.prototype.map.call(trackEls, function (t) {
5071    return t.track;
5072  });
5073  var tracks = Array.prototype.map.call(trackEls, function (trackEl) {
5074    var json = trackToJson_(trackEl.track);
5075
5076    if (trackEl.src) {
5077      json.src = trackEl.src;
5078    }
5079    return json;
5080  });
5081
5082  return tracks.concat(Array.prototype.filter.call(tech.textTracks(), function (track) {
5083    return trackObjs.indexOf(track) === -1;
5084  }).map(trackToJson_));
5085};
5086
5087/**
5088 * Create a set of remote {@link TextTrack}s on a {@link Tech} based on an array of javascript
5089 * object {@link TextTrack} representations.
5090 *
5091 * @param {Array} json
5092 *        An array of `TextTrack` representation objects, like those that would be
5093 *        produced by `textTracksToJson`.
5094 *
5095 * @param {Tech} tech
5096 *        The `Tech` to create the `TextTrack`s on.
5097 */
5098var jsonToTextTracks = function jsonToTextTracks(json, tech) {
5099  json.forEach(function (track) {
5100    var addedTrack = tech.addRemoteTextTrack(track).track;
5101
5102    if (!track.src && track.cues) {
vendor: 14,117 bytes, lines 5103-5634
5103      track.cues.forEach(function (cue) {
5104        return addedTrack.addCue(cue);
5105      });
5106    }
5107  });
5108
5109  return tech.textTracks();
5110};
5111
5112var textTrackConverter = { textTracksToJson: textTracksToJson, jsonToTextTracks: jsonToTextTracks, trackToJson_: trackToJson_ };
5113
5114/**
5115 * @file modal-dialog.js
5116 */
5117var MODAL_CLASS_NAME = 'vjs-modal-dialog';
5118var ESC = 27;
5119
5120/**
5121 * The `ModalDialog` displays over the video and its controls, which blocks
5122 * interaction with the player until it is closed.
5123 *
5124 * Modal dialogs include a "Close" button and will close when that button
5125 * is activated - or when ESC is pressed anywhere.
5126 *
5127 * @extends Component
5128 */
5129
5130var ModalDialog = function (_Component) {
5131  inherits(ModalDialog, _Component);
5132
5133  /**
5134   * Create an instance of this class.
5135   *
5136   * @param {Player} player
5137   *        The `Player` that this class should be attached to.
5138   *
5139   * @param {Object} [options]
5140   *        The key/value store of player options.
5141   *
5142   * @param {Mixed} [options.content=undefined]
5143   *        Provide customized content for this modal.
5144   *
5145   * @param {string} [options.description]
5146   *        A text description for the modal, primarily for accessibility.
5147   *
5148   * @param {boolean} [options.fillAlways=false]
5149   *        Normally, modals are automatically filled only the first time
5150   *        they open. This tells the modal to refresh its content
5151   *        every time it opens.
5152   *
5153   * @param {string} [options.label]
5154   *        A text label for the modal, primarily for accessibility.
5155   *
5156   * @param {boolean} [options.temporary=true]
5157   *        If `true`, the modal can only be opened once; it will be
5158   *        disposed as soon as it's closed.
5159   *
5160   * @param {boolean} [options.uncloseable=false]
5161   *        If `true`, the user will not be able to close the modal
5162   *        through the UI in the normal ways. Programmatic closing is
5163   *        still possible.
5164   */
5165  function ModalDialog(player, options) {
5166    classCallCheck(this, ModalDialog);
5167
5168    var _this = possibleConstructorReturn(this, _Component.call(this, player, options));
5169
5170    _this.opened_ = _this.hasBeenOpened_ = _this.hasBeenFilled_ = false;
5171
5172    _this.closeable(!_this.options_.uncloseable);
5173    _this.content(_this.options_.content);
5174
5175    // Make sure the contentEl is defined AFTER any children are initialized
5176    // because we only want the contents of the modal in the contentEl
5177    // (not the UI elements like the close button).
5178    _this.contentEl_ = createEl('div', {
5179      className: MODAL_CLASS_NAME + '-content'
5180    }, {
5181      role: 'document'
5182    });
5183
5184    _this.descEl_ = createEl('p', {
5185      className: MODAL_CLASS_NAME + '-description vjs-control-text',
5186      id: _this.el().getAttribute('aria-describedby')
5187    });
5188
5189    textContent(_this.descEl_, _this.description());
5190    _this.el_.appendChild(_this.descEl_);
5191    _this.el_.appendChild(_this.contentEl_);
5192    return _this;
5193  }
5194
5195  /**
5196   * Create the `ModalDialog`'s DOM element
5197   *
5198   * @return {Element}
5199   *         The DOM element that gets created.
5200   */
5201
5202
5203  ModalDialog.prototype.createEl = function createEl$$1() {
5204    return _Component.prototype.createEl.call(this, 'div', {
5205      className: this.buildCSSClass(),
5206      tabIndex: -1
5207    }, {
5208      'aria-describedby': this.id() + '_description',
5209      'aria-hidden': 'true',
5210      'aria-label': this.label(),
5211      'role': 'dialog'
5212    });
5213  };
5214
5215  /**
5216   * Builds the default DOM `className`.
5217   *
5218   * @return {string}
5219   *         The DOM `className` for this object.
5220   */
5221
5222
5223  ModalDialog.prototype.buildCSSClass = function buildCSSClass() {
5224    return MODAL_CLASS_NAME + ' vjs-hidden ' + _Component.prototype.buildCSSClass.call(this);
5225  };
5226
5227  /**
5228   * Handles `keydown` events on the document, looking for ESC, which closes
5229   * the modal.
5230   *
5231   * @param {EventTarget~Event} e
5232   *        The keypress that triggered this event.
5233   *
5234   * @listens keydown
5235   */
5236
5237
5238  ModalDialog.prototype.handleKeyPress = function handleKeyPress(e) {
5239    if (e.which === ESC && this.closeable()) {
5240      this.close();
5241    }
5242  };
5243
5244  /**
5245   * Returns the label string for this modal. Primarily used for accessibility.
5246   *
5247   * @return {string}
5248   *         the localized or raw label of this modal.
5249   */
5250
5251
5252  ModalDialog.prototype.label = function label() {
5253    return this.localize(this.options_.label || 'Modal Window');
5254  };
5255
5256  /**
5257   * Returns the description string for this modal. Primarily used for
5258   * accessibility.
5259   *
5260   * @return {string}
5261   *         The localized or raw description of this modal.
5262   */
5263
5264
5265  ModalDialog.prototype.description = function description() {
5266    var desc = this.options_.description || this.localize('This is a modal window.');
5267
5268    // Append a universal closeability message if the modal is closeable.
5269    if (this.closeable()) {
5270      desc += ' ' + this.localize('This modal can be closed by pressing the Escape key or activating the close button.');
5271    }
5272
5273    return desc;
5274  };
5275
5276  /**
5277   * Opens the modal.
5278   *
5279   * @fires ModalDialog#beforemodalopen
5280   * @fires ModalDialog#modalopen
5281   */
5282
5283
5284  ModalDialog.prototype.open = function open() {
5285    if (!this.opened_) {
5286      var player = this.player();
5287
5288      /**
5289        * Fired just before a `ModalDialog` is opened.
5290        *
5291        * @event ModalDialog#beforemodalopen
5292        * @type {EventTarget~Event}
5293        */
5294      this.trigger('beforemodalopen');
5295      this.opened_ = true;
5296
5297      // Fill content if the modal has never opened before and
5298      // never been filled.
5299      if (this.options_.fillAlways || !this.hasBeenOpened_ && !this.hasBeenFilled_) {
5300        this.fill();
5301      }
5302
5303      // If the player was playing, pause it and take note of its previously
5304      // playing state.
5305      this.wasPlaying_ = !player.paused();
5306
5307      if (this.options_.pauseOnOpen && this.wasPlaying_) {
5308        player.pause();
5309      }
5310
5311      if (this.closeable()) {
5312        this.on(this.el_.ownerDocument, 'keydown', bind(this, this.handleKeyPress));
5313      }
5314
5315      player.controls(false);
5316      this.show();
5317      this.conditionalFocus_();
5318      this.el().setAttribute('aria-hidden', 'false');
5319
5320      /**
5321        * Fired just after a `ModalDialog` is opened.
5322        *
5323        * @event ModalDialog#modalopen
5324        * @type {EventTarget~Event}
5325        */
5326      this.trigger('modalopen');
5327      this.hasBeenOpened_ = true;
5328    }
5329  };
5330
5331  /**
5332   * If the `ModalDialog` is currently open or closed.
5333   *
5334   * @param  {boolean} [value]
5335   *         If given, it will open (`true`) or close (`false`) the modal.
5336   *
5337   * @return {boolean}
5338   *         the current open state of the modaldialog
5339   */
5340
5341
5342  ModalDialog.prototype.opened = function opened(value) {
5343    if (typeof value === 'boolean') {
5344      this[value ? 'open' : 'close']();
5345    }
5346    return this.opened_;
5347  };
5348
5349  /**
5350   * Closes the modal, does nothing if the `ModalDialog` is
5351   * not open.
5352   *
5353   * @fires ModalDialog#beforemodalclose
5354   * @fires ModalDialog#modalclose
5355   */
5356
5357
5358  ModalDialog.prototype.close = function close() {
5359    if (!this.opened_) {
5360      return;
5361    }
5362    var player = this.player();
5363
5364    /**
5365      * Fired just before a `ModalDialog` is closed.
5366      *
5367      * @event ModalDialog#beforemodalclose
5368      * @type {EventTarget~Event}
5369      */
5370    this.trigger('beforemodalclose');
5371    this.opened_ = false;
5372
5373    if (this.wasPlaying_ && this.options_.pauseOnOpen) {
5374      player.play();
5375    }
5376
5377    if (this.closeable()) {
5378      this.off(this.el_.ownerDocument, 'keydown', bind(this, this.handleKeyPress));
5379    }
5380
5381    player.controls(true);
5382    this.hide();
5383    this.el().setAttribute('aria-hidden', 'true');
5384
5385    /**
5386      * Fired just after a `ModalDialog` is closed.
5387      *
5388      * @event ModalDialog#modalclose
5389      * @type {EventTarget~Event}
5390      */
5391    this.trigger('modalclose');
5392    this.conditionalBlur_();
5393
5394    if (this.options_.temporary) {
5395      this.dispose();
5396    }
5397  };
5398
5399  /**
5400   * Check to see if the `ModalDialog` is closeable via the UI.
5401   *
5402   * @param  {boolean} [value]
5403   *         If given as a boolean, it will set the `closeable` option.
5404   *
5405   * @return {boolean}
5406   *         Returns the final value of the closable option.
5407   */
5408
5409
5410  ModalDialog.prototype.closeable = function closeable(value) {
5411    if (typeof value === 'boolean') {
5412      var closeable = this.closeable_ = !!value;
5413      var close = this.getChild('closeButton');
5414
5415      // If this is being made closeable and has no close button, add one.
5416      if (closeable && !close) {
5417
5418        // The close button should be a child of the modal - not its
5419        // content element, so temporarily change the content element.
5420        var temp = this.contentEl_;
5421
5422        this.contentEl_ = this.el_;
5423        close = this.addChild('closeButton', { controlText: 'Close Modal Dialog' });
5424        this.contentEl_ = temp;
5425        this.on(close, 'close', this.close);
5426      }
5427
5428      // If this is being made uncloseable and has a close button, remove it.
5429      if (!closeable && close) {
5430        this.off(close, 'close', this.close);
5431        this.removeChild(close);
5432        close.dispose();
5433      }
5434    }
5435    return this.closeable_;
5436  };
5437
5438  /**
5439   * Fill the modal's content element with the modal's "content" option.
5440   * The content element will be emptied before this change takes place.
5441   */
5442
5443
5444  ModalDialog.prototype.fill = function fill() {
5445    this.fillWith(this.content());
5446  };
5447
5448  /**
5449   * Fill the modal's content element with arbitrary content.
5450   * The content element will be emptied before this change takes place.
5451   *
5452   * @fires ModalDialog#beforemodalfill
5453   * @fires ModalDialog#modalfill
5454   *
5455   * @param {Mixed} [content]
5456   *        The same rules apply to this as apply to the `content` option.
5457   */
5458
5459
5460  ModalDialog.prototype.fillWith = function fillWith(content) {
5461    var contentEl = this.contentEl();
5462    var parentEl = contentEl.parentNode;
5463    var nextSiblingEl = contentEl.nextSibling;
5464
5465    /**
5466     * Fired just before a `ModalDialog` is filled with content.
5467     *
5468     * @event ModalDialog#beforemodalfill
5469     * @type {EventTarget~Event}
5470     */
5471    this.trigger('beforemodalfill');
5472    this.hasBeenFilled_ = true;
5473
5474    // Detach the content element from the DOM before performing
5475    // manipulation to avoid modifying the live DOM multiple times.
5476    parentEl.removeChild(contentEl);
5477    this.empty();
5478    insertContent(contentEl, content);
5479    /**
5480     * Fired just after a `ModalDialog` is filled with content.
5481     *
5482     * @event ModalDialog#modalfill
5483     * @type {EventTarget~Event}
5484     */
5485    this.trigger('modalfill');
5486
5487    // Re-inject the re-filled content element.
5488    if (nextSiblingEl) {
5489      parentEl.insertBefore(contentEl, nextSiblingEl);
5490    } else {
5491      parentEl.appendChild(contentEl);
5492    }
5493
5494    // make sure that the close button is last in the dialog DOM
5495    var closeButton = this.getChild('closeButton');
5496
5497    if (closeButton) {
5498      parentEl.appendChild(closeButton.el_);
5499    }
5500  };
5501
5502  /**
5503   * Empties the content element. This happens anytime the modal is filled.
5504   *
5505   * @fires ModalDialog#beforemodalempty
5506   * @fires ModalDialog#modalempty
5507   */
5508
5509
5510  ModalDialog.prototype.empty = function empty() {
5511    /**
5512     * Fired just before a `ModalDialog` is emptied.
5513     *
5514     * @event ModalDialog#beforemodalempty
5515     * @type {EventTarget~Event}
5516     */
5517    this.trigger('beforemodalempty');
5518    emptyEl(this.contentEl());
5519
5520    /**
5521     * Fired just after a `ModalDialog` is emptied.
5522     *
5523     * @event ModalDialog#modalempty
5524     * @type {EventTarget~Event}
5525     */
5526    this.trigger('modalempty');
5527  };
5528
5529  /**
5530   * Gets or sets the modal content, which gets normalized before being
5531   * rendered into the DOM.
5532   *
5533   * This does not update the DOM or fill the modal, but it is called during
5534   * that process.
5535   *
5536   * @param  {Mixed} [value]
5537   *         If defined, sets the internal content value to be used on the
5538   *         next call(s) to `fill`. This value is normalized before being
5539   *         inserted. To "clear" the internal content value, pass `null`.
5540   *
5541   * @return {Mixed}
5542   *         The current content of the modal dialog
5543   */
5544
5545
5546  ModalDialog.prototype.content = function content(value) {
5547    if (typeof value !== 'undefined') {
5548      this.content_ = value;
5549    }
5550    return this.content_;
5551  };
5552
5553  /**
5554   * conditionally focus the modal dialog if focus was previously on the player.
5555   *
5556   * @private
5557   */
5558
5559
5560  ModalDialog.prototype.conditionalFocus_ = function conditionalFocus_() {
5561    var activeEl = document_1.activeElement;
5562    var playerEl = this.player_.el_;
5563
5564    this.previouslyActiveEl_ = null;
5565
5566    if (playerEl.contains(activeEl) || playerEl === activeEl) {
5567      this.previouslyActiveEl_ = activeEl;
5568
5569      this.focus();
5570
5571      this.on(document_1, 'keydown', this.handleKeyDown);
5572    }
5573  };
5574
5575  /**
5576   * conditionally blur the element and refocus the last focused element
5577   *
5578   * @private
5579   */
5580
5581
5582  ModalDialog.prototype.conditionalBlur_ = function conditionalBlur_() {
5583    if (this.previouslyActiveEl_) {
5584      this.previouslyActiveEl_.focus();
5585      this.previouslyActiveEl_ = null;
5586    }
5587
5588    this.off(document_1, 'keydown', this.handleKeyDown);
5589  };
5590
5591  /**
5592   * Keydown handler. Attached when modal is focused.
5593   *
5594   * @listens keydown
5595   */
5596
5597
5598  ModalDialog.prototype.handleKeyDown = function handleKeyDown(event) {
5599    // exit early if it isn't a tab key
5600    if (event.which !== 9) {
5601      return;
5602    }
5603
5604    var focusableEls = this.focusableEls_();
5605    var activeEl = this.el_.querySelector(':focus');
5606    var focusIndex = void 0;
5607
5608    for (var i = 0; i < focusableEls.length; i++) {
5609      if (activeEl === focusableEls[i]) {
5610        focusIndex = i;
5611        break;
5612      }
5613    }
5614
5615    if (document_1.activeElement === this.el_) {
5616      focusIndex = 0;
5617    }
5618
5619    if (event.shiftKey && focusIndex === 0) {
5620      focusableEls[focusableEls.length - 1].focus();
5621      event.preventDefault();
5622    } else if (!event.shiftKey && focusIndex === focusableEls.length - 1) {
5623      focusableEls[0].focus();
5624      event.preventDefault();
5625    }
5626  };
5627
5628  /**
5629   * get all focusable elements
5630   *
5631   * @private
5632   */
5633
5634
vendor: 6,242 bytes, lines 5635-5888
5635  ModalDialog.prototype.focusableEls_ = function focusableEls_() {
5636    var allChildren = this.el_.querySelectorAll('*');
5637
5638    return Array.prototype.filter.call(allChildren, function (child) {
5639      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');
5640    });
5641  };
5642
5643  return ModalDialog;
5644}(Component);
5645
5646/**
5647 * Default options for `ModalDialog` default options.
5648 *
5649 * @type {Object}
5650 * @private
5651 */
5652
5653
5654ModalDialog.prototype.options_ = {
5655  pauseOnOpen: true,
5656  temporary: true
5657};
5658
5659Component.registerComponent('ModalDialog', ModalDialog);
5660
5661/**
5662 * @file track-list.js
5663 */
5664/**
5665 * Common functionaliy between {@link TextTrackList}, {@link AudioTrackList}, and
5666 * {@link VideoTrackList}
5667 *
5668 * @extends EventTarget
5669 */
5670
5671var TrackList = function (_EventTarget) {
5672  inherits(TrackList, _EventTarget);
5673
5674  /**
5675   * Create an instance of this class
5676   *
5677   * @param {Track[]} tracks
5678   *        A list of tracks to initialize the list with.
5679   *
5680   * @param {Object} [list]
5681   *        The child object with inheritance done manually for ie8.
5682   *
5683   * @abstract
5684   */
5685  function TrackList() {
5686    var tracks = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : [];
5687
5688    var _ret;
5689
5690    var list = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : null;
5691    classCallCheck(this, TrackList);
5692
5693    var _this = possibleConstructorReturn(this, _EventTarget.call(this));
5694
5695    if (!list) {
5696      list = _this; // eslint-disable-line
5697      if (IS_IE8) {
5698        list = document_1.createElement('custom');
5699        for (var prop in TrackList.prototype) {
5700          if (prop !== 'constructor') {
5701            list[prop] = TrackList.prototype[prop];
5702          }
5703        }
5704      }
5705    }
5706
5707    list.tracks_ = [];
5708
5709    /**
5710     * @memberof TrackList
5711     * @member {number} length
5712     *         The current number of `Track`s in the this Trackist.
5713     * @instance
5714     */
5715    Object.defineProperty(list, 'length', {
5716      get: function get$$1() {
5717        return this.tracks_.length;
5718      }
5719    });
5720
5721    for (var i = 0; i < tracks.length; i++) {
5722      list.addTrack(tracks[i]);
5723    }
5724
5725    // must return the object, as for ie8 it will not be this
5726    // but a reference to a document object
5727    return _ret = list, possibleConstructorReturn(_this, _ret);
5728  }
5729
5730  /**
5731   * Add a {@link Track} to the `TrackList`
5732   *
5733   * @param {Track} track
5734   *        The audio, video, or text track to add to the list.
5735   *
5736   * @fires TrackList#addtrack
5737   */
5738
5739
5740  TrackList.prototype.addTrack = function addTrack(track) {
5741    var index = this.tracks_.length;
5742
5743    if (!('' + index in this)) {
5744      Object.defineProperty(this, index, {
5745        get: function get$$1() {
5746          return this.tracks_[index];
5747        }
5748      });
5749    }
5750
5751    // Do not add duplicate tracks
5752    if (this.tracks_.indexOf(track) === -1) {
5753      this.tracks_.push(track);
5754      /**
5755       * Triggered when a track is added to a track list.
5756       *
5757       * @event TrackList#addtrack
5758       * @type {EventTarget~Event}
5759       * @property {Track} track
5760       *           A reference to track that was added.
5761       */
5762      this.trigger({
5763        track: track,
5764        type: 'addtrack'
5765      });
5766    }
5767  };
5768
5769  /**
5770   * Remove a {@link Track} from the `TrackList`
5771   *
5772   * @param {Track} rtrack
5773   *        The audio, video, or text track to remove from the list.
5774   *
5775   * @fires TrackList#removetrack
5776   */
5777
5778
5779  TrackList.prototype.removeTrack = function removeTrack(rtrack) {
5780    var track = void 0;
5781
5782    for (var i = 0, l = this.length; i < l; i++) {
5783      if (this[i] === rtrack) {
5784        track = this[i];
5785        if (track.off) {
5786          track.off();
5787        }
5788
5789        this.tracks_.splice(i, 1);
5790
5791        break;
5792      }
5793    }
5794
5795    if (!track) {
5796      return;
5797    }
5798
5799    /**
5800     * Triggered when a track is removed from track list.
5801     *
5802     * @event TrackList#removetrack
5803     * @type {EventTarget~Event}
5804     * @property {Track} track
5805     *           A reference to track that was removed.
5806     */
5807    this.trigger({
5808      track: track,
5809      type: 'removetrack'
5810    });
5811  };
5812
5813  /**
5814   * Get a Track from the TrackList by a tracks id
5815   *
5816   * @param {String} id - the id of the track to get
5817   * @method getTrackById
5818   * @return {Track}
5819   * @private
5820   */
5821
5822
5823  TrackList.prototype.getTrackById = function getTrackById(id) {
5824    var result = null;
5825
5826    for (var i = 0, l = this.length; i < l; i++) {
5827      var track = this[i];
5828
5829      if (track.id === id) {
5830        result = track;
5831        break;
5832      }
5833    }
5834
5835    return result;
5836  };
5837
5838  return TrackList;
5839}(EventTarget);
5840
5841/**
5842 * Triggered when a different track is selected/enabled.
5843 *
5844 * @event TrackList#change
5845 * @type {EventTarget~Event}
5846 */
5847
5848/**
5849 * Events that can be called with on + eventName. See {@link EventHandler}.
5850 *
5851 * @property {Object} TrackList#allowedEvents_
5852 * @private
5853 */
5854
5855
5856TrackList.prototype.allowedEvents_ = {
5857  change: 'change',
5858  addtrack: 'addtrack',
5859  removetrack: 'removetrack'
5860};
5861
5862// emulate attribute EventHandler support to allow for feature detection
5863for (var event in TrackList.prototype.allowedEvents_) {
5864  TrackList.prototype['on' + event] = null;
5865}
5866
5867/**
5868 * @file audio-track-list.js
5869 */
5870/**
5871 * Anywhere we call this function we diverge from the spec
5872 * as we only support one enabled audiotrack at a time
5873 *
5874 * @param {AudioTrackList} list
5875 *        list to work on
5876 *
5877 * @param {AudioTrack} track
5878 *        The track to skip
5879 *
5880 * @private
5881 */
5882var disableOthers = function disableOthers(list, track) {
5883  for (var i = 0; i < list.length; i++) {
5884    if (!Object.keys(list[i]).length || track.id === list[i].id) {
5885      continue;
5886    }
5887    // another audio track is enabled, disable it
5888    list[i].enabled = false;
5889  }
5890};
5891
5892/**
5893 * The current list of {@link AudioTrack} for a media file.
5894 *
5895 * @see [Spec]{@link https://html.spec.whatwg.org/multipage/embedded-content.html#audiotracklist}
5896 * @extends TrackList
5897 */
5898
5899var AudioTrackList = function (_TrackList) {
5900  inherits(AudioTrackList, _TrackList);
5901
5902  /**
5903   * Create an instance of this class.
5904   *
5905   * @param {AudioTrack[]} [tracks=[]]
5906   *        A list of `AudioTrack` to instantiate the list with.
5907   */
5908  function AudioTrackList() {
5909    var _this, _ret;
5910
5911    var tracks = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : [];
5912    classCallCheck(this, AudioTrackList);
5913
5914    var list = void 0;
5915
5916    // make sure only 1 track is enabled
5917    // sorted from last index to first index
5918    for (var i = tracks.length - 1; i >= 0; i--) {
5919      if (tracks[i].enabled) {
5920        disableOthers(tracks, tracks[i]);
5921        break;
5922      }
5923    }
5924
5925    // IE8 forces us to implement inheritance ourselves
5926    // as it does not support Object.defineProperty properly
5927    if (IS_IE8) {
5928      list = document_1.createElement('custom');
5929      for (var prop in TrackList.prototype) {
5930        if (prop !== 'constructor') {
5931          list[prop] = TrackList.prototype[prop];
5932        }
5933      }
5934      for (var _prop in AudioTrackList.prototype) {
5935        if (_prop !== 'constructor') {
5936          list[_prop] = AudioTrackList.prototype[_prop];
5937        }
5938      }
5939    }
5940
5941    list = (_this = possibleConstructorReturn(this, _TrackList.call(this, tracks, list)), _this);
5942    list.changing_ = false;
5943
5944    return _ret = list, possibleConstructorReturn(_this, _ret);
5945  }
5946
5947  /**
5948   * Add an {@link AudioTrack} to the `AudioTrackList`.
5949   *
5950   * @param {AudioTrack} track
5951   *        The AudioTrack to add to the list
5952   *
5953   * @fires TrackList#addtrack
5954   */
5955
5956
5957  AudioTrackList.prototype.addTrack = function addTrack(track) {
5958    var _this2 = this;
5959
5960    if (track.enabled) {
5961      disableOthers(this, track);
5962    }
5963
5964    _TrackList.prototype.addTrack.call(this, track);
5965    // native tracks don't have this
5966    if (!track.addEventListener) {
5967      return;
5968    }
5969
5970    /**
5971     * @listens AudioTrack#enabledchange
5972     * @fires TrackList#change
5973     */
5974    track.addEventListener('enabledchange', function () {
5975      // when we are disabling other tracks (since we don't support
5976      // more than one track at a time) we will set changing_
5977      // to true so that we don't trigger additional change events
5978      if (_this2.changing_) {
5979        return;
5980      }
5981      _this2.changing_ = true;
5982      disableOthers(_this2, track);
5983      _this2.changing_ = false;
5984      _this2.trigger('change');
5985    });
5986  };
5987
5988  return AudioTrackList;
5989}(TrackList);
5990
5991/**
5992 * @file video-track-list.js
5993 */
5994/**
5995 * Un-select all other {@link VideoTrack}s that are selected.
5996 *
5997 * @param {VideoTrackList} list
5998 *        list to work on
5999 *
6000 * @param {VideoTrack} track
6001 *        The track to skip
6002 *
6003 * @private
6004 */
6005var disableOthers$1 = function disableOthers(list, track) {
6006  for (var i = 0; i < list.length; i++) {
6007    if (!Object.keys(list[i]).length || track.id === list[i].id) {
6008      continue;
6009    }
6010    // another video track is enabled, disable it
6011    list[i].selected = false;
6012  }
6013};
6014
6015/**
6016 * The current list of {@link VideoTrack} for a video.
6017 *
6018 * @see [Spec]{@link https://html.spec.whatwg.org/multipage/embedded-content.html#videotracklist}
6019 * @extends TrackList
6020 */
6021
6022var VideoTrackList = function (_TrackList) {
6023  inherits(VideoTrackList, _TrackList);
6024
6025  /**
6026   * Create an instance of this class.
6027   *
6028   * @param {VideoTrack[]} [tracks=[]]
6029   *        A list of `VideoTrack` to instantiate the list with.
6030   */
6031  function VideoTrackList() {
6032    var _this, _ret;
6033
6034    var tracks = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : [];
6035    classCallCheck(this, VideoTrackList);
6036
6037    var list = void 0;
6038
6039    // make sure only 1 track is enabled
6040    // sorted from last index to first index
6041    for (var i = tracks.length - 1; i >= 0; i--) {
6042      if (tracks[i].selected) {
6043        disableOthers$1(tracks, tracks[i]);
6044        break;
6045      }
6046    }
6047
6048    // IE8 forces us to implement inheritance ourselves
6049    // as it does not support Object.defineProperty properly
6050    if (IS_IE8) {
6051      list = document_1.createElement('custom');
6052      for (var prop in TrackList.prototype) {
6053        if (prop !== 'constructor') {
6054          list[prop] = TrackList.prototype[prop];
6055        }
6056      }
6057      for (var _prop in VideoTrackList.prototype) {
6058        if (_prop !== 'constructor') {
6059          list[_prop] = VideoTrackList.prototype[_prop];
6060        }
6061      }
6062    }
6063
6064    list = (_this = possibleConstructorReturn(this, _TrackList.call(this, tracks, list)), _this);
6065    list.changing_ = false;
6066
6067    /**
6068     * @member {number} VideoTrackList#selectedIndex
6069     *         The current index of the selected {@link VideoTrack`}.
6070     */
6071    Object.defineProperty(list, 'selectedIndex', {
6072      get: function get$$1() {
6073        for (var _i = 0; _i < this.length; _i++) {
6074          if (this[_i].selected) {
6075            return _i;
6076          }
6077        }
6078        return -1;
6079      },
6080      set: function set$$1() {}
6081    });
6082
6083    return _ret = list, possibleConstructorReturn(_this, _ret);
6084  }
6085
6086  /**
6087   * Add a {@link VideoTrack} to the `VideoTrackList`.
6088   *
6089   * @param {VideoTrack} track
6090   *        The VideoTrack to add to the list
6091   *
6092   * @fires TrackList#addtrack
6093   */
6094
6095
6096  VideoTrackList.prototype.addTrack = function addTrack(track) {
6097    var _this2 = this;
6098
6099    if (track.selected) {
6100      disableOthers$1(this, track);
6101    }
6102
6103    _TrackList.prototype.addTrack.call(this, track);
6104    // native tracks don't have this
6105    if (!track.addEventListener) {
6106      return;
6107    }
6108
6109    /**
6110     * @listens VideoTrack#selectedchange
6111     * @fires TrackList#change
6112     */
6113    track.addEventListener('selectedchange', function () {
6114      if (_this2.changing_) {
6115        return;
6116      }
6117      _this2.changing_ = true;
6118      disableOthers$1(_this2, track);
6119      _this2.changing_ = false;
6120      _this2.trigger('change');
6121    });
6122  };
6123
6124  return VideoTrackList;
6125}(TrackList);
6126
6127/**
6128 * @file text-track-list.js
6129 */
6130/**
6131 * The current list of {@link TextTrack} for a media file.
6132 *
6133 * @see [Spec]{@link https://html.spec.whatwg.org/multipage/embedded-content.html#texttracklist}
6134 * @extends TrackList
6135 */
6136
6137var TextTrackList = function (_TrackList) {
6138  inherits(TextTrackList, _TrackList);
6139
6140  /**
6141   * Create an instance of this class.
6142   *
6143   * @param {TextTrack[]} [tracks=[]]
6144   *        A list of `TextTrack` to instantiate the list with.
6145   */
6146  function TextTrackList() {
6147    var _this, _ret;
6148
6149    var tracks = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : [];
6150    classCallCheck(this, TextTrackList);
6151
6152    var list = void 0;
6153
6154    // IE8 forces us to implement inheritance ourselves
6155    // as it does not support Object.defineProperty properly
6156    if (IS_IE8) {
6157      list = document_1.createElement('custom');
6158      for (var prop in TrackList.prototype) {
6159        if (prop !== 'constructor') {
6160          list[prop] = TrackList.prototype[prop];
6161        }
6162      }
6163      for (var _prop in TextTrackList.prototype) {
6164        if (_prop !== 'constructor') {
6165          list[_prop] = TextTrackList.prototype[_prop];
6166        }
6167      }
6168    }
6169
vendor: 7,767 bytes, lines 6170-6504
6170    list = (_this = possibleConstructorReturn(this, _TrackList.call(this, tracks, list)), _this);
6171    return _ret = list, possibleConstructorReturn(_this, _ret);
6172  }
6173
6174  /**
6175   * Add a {@link TextTrack} to the `TextTrackList`
6176   *
6177   * @param {TextTrack} track
6178   *        The text track to add to the list.
6179   *
6180   * @fires TrackList#addtrack
6181   */
6182
6183
6184  TextTrackList.prototype.addTrack = function addTrack(track) {
6185    _TrackList.prototype.addTrack.call(this, track);
6186
6187    /**
6188     * @listens TextTrack#modechange
6189     * @fires TrackList#change
6190     */
6191    track.addEventListener('modechange', bind(this, function () {
6192      this.trigger('change');
6193    }));
6194
6195    var nonLanguageTextTrackKind = ['metadata', 'chapters'];
6196
6197    if (nonLanguageTextTrackKind.indexOf(track.kind) === -1) {
6198      track.addEventListener('modechange', bind(this, function () {
6199        this.trigger('selectedlanguagechange');
6200      }));
6201    }
6202  };
6203
6204  return TextTrackList;
6205}(TrackList);
6206
6207/**
6208 * @file html-track-element-list.js
6209 */
6210
6211/**
6212 * The current list of {@link HtmlTrackElement}s.
6213 */
6214
6215var HtmlTrackElementList = function () {
6216
6217  /**
6218   * Create an instance of this class.
6219   *
6220   * @param {HtmlTrackElement[]} [tracks=[]]
6221   *        A list of `HtmlTrackElement` to instantiate the list with.
6222   */
6223  function HtmlTrackElementList() {
6224    var trackElements = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : [];
6225    classCallCheck(this, HtmlTrackElementList);
6226
6227    var list = this; // eslint-disable-line
6228
6229    if (IS_IE8) {
6230      list = document_1.createElement('custom');
6231
6232      for (var prop in HtmlTrackElementList.prototype) {
6233        if (prop !== 'constructor') {
6234          list[prop] = HtmlTrackElementList.prototype[prop];
6235        }
6236      }
6237    }
6238
6239    list.trackElements_ = [];
6240
6241    /**
6242     * @memberof HtmlTrackElementList
6243     * @member {number} length
6244     *         The current number of `Track`s in the this Trackist.
6245     * @instance
6246     */
6247    Object.defineProperty(list, 'length', {
6248      get: function get$$1() {
6249        return this.trackElements_.length;
6250      }
6251    });
6252
6253    for (var i = 0, length = trackElements.length; i < length; i++) {
6254      list.addTrackElement_(trackElements[i]);
6255    }
6256
6257    if (IS_IE8) {
6258      return list;
6259    }
6260  }
6261
6262  /**
6263   * Add an {@link HtmlTrackElement} to the `HtmlTrackElementList`
6264   *
6265   * @param {HtmlTrackElement} trackElement
6266   *        The track element to add to the list.
6267   *
6268   * @private
6269   */
6270
6271
6272  HtmlTrackElementList.prototype.addTrackElement_ = function addTrackElement_(trackElement) {
6273    var index = this.trackElements_.length;
6274
6275    if (!('' + index in this)) {
6276      Object.defineProperty(this, index, {
6277        get: function get$$1() {
6278          return this.trackElements_[index];
6279        }
6280      });
6281    }
6282
6283    // Do not add duplicate elements
6284    if (this.trackElements_.indexOf(trackElement) === -1) {
6285      this.trackElements_.push(trackElement);
6286    }
6287  };
6288
6289  /**
6290   * Get an {@link HtmlTrackElement} from the `HtmlTrackElementList` given an
6291   * {@link TextTrack}.
6292   *
6293   * @param {TextTrack} track
6294   *        The track associated with a track element.
6295   *
6296   * @return {HtmlTrackElement|undefined}
6297   *         The track element that was found or undefined.
6298   *
6299   * @private
6300   */
6301
6302
6303  HtmlTrackElementList.prototype.getTrackElementByTrack_ = function getTrackElementByTrack_(track) {
6304    var trackElement_ = void 0;
6305
6306    for (var i = 0, length = this.trackElements_.length; i < length; i++) {
6307      if (track === this.trackElements_[i].track) {
6308        trackElement_ = this.trackElements_[i];
6309
6310        break;
6311      }
6312    }
6313
6314    return trackElement_;
6315  };
6316
6317  /**
6318   * Remove a {@link HtmlTrackElement} from the `HtmlTrackElementList`
6319   *
6320   * @param {HtmlTrackElement} trackElement
6321   *        The track element to remove from the list.
6322   *
6323   * @private
6324   */
6325
6326
6327  HtmlTrackElementList.prototype.removeTrackElement_ = function removeTrackElement_(trackElement) {
6328    for (var i = 0, length = this.trackElements_.length; i < length; i++) {
6329      if (trackElement === this.trackElements_[i]) {
6330        this.trackElements_.splice(i, 1);
6331
6332        break;
6333      }
6334    }
6335  };
6336
6337  return HtmlTrackElementList;
6338}();
6339
6340/**
6341 * @file text-track-cue-list.js
6342 */
6343/**
6344 * @typedef {Object} TextTrackCueList~TextTrackCue
6345 *
6346 * @property {string} id
6347 *           The unique id for this text track cue
6348 *
6349 * @property {number} startTime
6350 *           The start time for this text track cue
6351 *
6352 * @property {number} endTime
6353 *           The end time for this text track cue
6354 *
6355 * @property {boolean} pauseOnExit
6356 *           Pause when the end time is reached if true.
6357 *
6358 * @see [Spec]{@link https://html.spec.whatwg.org/multipage/embedded-content.html#texttrackcue}
6359 */
6360
6361/**
6362 * A List of TextTrackCues.
6363 *
6364 * @see [Spec]{@link https://html.spec.whatwg.org/multipage/embedded-content.html#texttrackcuelist}
6365 */
6366
6367var TextTrackCueList = function () {
6368
6369  /**
6370   * Create an instance of this class..
6371   *
6372   * @param {Array} cues
6373   *        A list of cues to be initialized with
6374   */
6375  function TextTrackCueList(cues) {
6376    classCallCheck(this, TextTrackCueList);
6377
6378    var list = this; // eslint-disable-line
6379
6380    if (IS_IE8) {
6381      list = document_1.createElement('custom');
6382
6383      for (var prop in TextTrackCueList.prototype) {
6384        if (prop !== 'constructor') {
6385          list[prop] = TextTrackCueList.prototype[prop];
6386        }
6387      }
6388    }
6389
6390    TextTrackCueList.prototype.setCues_.call(list, cues);
6391
6392    /**
6393     * @memberof TextTrackCueList
6394     * @member {number} length
6395     *         The current number of `TextTrackCue`s in the TextTrackCueList.
6396     * @instance
6397     */
6398    Object.defineProperty(list, 'length', {
6399      get: function get$$1() {
6400        return this.length_;
6401      }
6402    });
6403
6404    if (IS_IE8) {
6405      return list;
6406    }
6407  }
6408
6409  /**
6410   * A setter for cues in this list. Creates getters
6411   * an an index for the cues.
6412   *
6413   * @param {Array} cues
6414   *        An array of cues to set
6415   *
6416   * @private
6417   */
6418
6419
6420  TextTrackCueList.prototype.setCues_ = function setCues_(cues) {
6421    var oldLength = this.length || 0;
6422    var i = 0;
6423    var l = cues.length;
6424
6425    this.cues_ = cues;
6426    this.length_ = cues.length;
6427
6428    var defineProp = function defineProp(index) {
6429      if (!('' + index in this)) {
6430        Object.defineProperty(this, '' + index, {
6431          get: function get$$1() {
6432            return this.cues_[index];
6433          }
6434        });
6435      }
6436    };
6437
6438    if (oldLength < l) {
6439      i = oldLength;
6440
6441      for (; i < l; i++) {
6442        defineProp.call(this, i);
6443      }
6444    }
6445  };
6446
6447  /**
6448   * Get a `TextTrackCue` that is currently in the `TextTrackCueList` by id.
6449   *
6450   * @param {string} id
6451   *        The id of the cue that should be searched for.
6452   *
6453   * @return {TextTrackCueList~TextTrackCue|null}
6454   *         A single cue or null if none was found.
6455   */
6456
6457
6458  TextTrackCueList.prototype.getCueById = function getCueById(id) {
6459    var result = null;
6460
6461    for (var i = 0, l = this.length; i < l; i++) {
6462      var cue = this[i];
6463
6464      if (cue.id === id) {
6465        result = cue;
6466        break;
6467      }
6468    }
6469
6470    return result;
6471  };
6472
6473  return TextTrackCueList;
6474}();
6475
6476/**
6477 * @file track-kinds.js
6478 */
6479
6480/**
6481 * All possible `VideoTrackKind`s
6482 *
6483 * @see https://html.spec.whatwg.org/multipage/embedded-content.html#dom-videotrack-kind
6484 * @typedef VideoTrack~Kind
6485 * @enum
6486 */
6487var VideoTrackKind = {
6488  alternative: 'alternative',
6489  captions: 'captions',
6490  main: 'main',
6491  sign: 'sign',
6492  subtitles: 'subtitles',
6493  commentary: 'commentary'
6494};
6495
6496/**
6497 * All possible `AudioTrackKind`s
6498 *
6499 * @see https://html.spec.whatwg.org/multipage/embedded-content.html#dom-audiotrack-kind
6500 * @typedef AudioTrack~Kind
6501 * @enum
6502 */
6503var AudioTrackKind = {
6504  'alternative': 'alternative',
6505  'descriptions': 'descriptions',
6506  'main': 'main',
6507  'main-desc': 'main-desc',
6508  'translation': 'translation',
6509  'commentary': 'commentary'
6510};
6511
6512/**
6513 * All possible `TextTrackKind`s
6514 *
6515 * @see https://html.spec.whatwg.org/multipage/embedded-content.html#dom-texttrack-kind
6516 * @typedef TextTrack~Kind
6517 * @enum
6518 */
6519var TextTrackKind = {
6520  subtitles: 'subtitles',
6521  captions: 'captions',
6522  descriptions: 'descriptions',
6523  chapters: 'chapters',
6524  metadata: 'metadata'
6525};
6526
6527/**
6528 * All possible `TextTrackMode`s
6529 *
6530 * @see https://html.spec.whatwg.org/multipage/embedded-content.html#texttrackmode
6531 * @typedef TextTrack~Mode
6532 * @enum
6533 */
6534var TextTrackMode = {
6535  disabled: 'disabled',
6536  hidden: 'hidden',
6537  showing: 'showing'
6538};
6539
6540/**
6541 * @file track.js
6542 */
6543/**
6544 * A Track class that contains all of the common functionality for {@link AudioTrack},
6545 * {@link VideoTrack}, and {@link TextTrack}.
6546 *
6547 * > Note: This class should not be used directly
6548 *
6549 * @see {@link https://html.spec.whatwg.org/multipage/embedded-content.html}
6550 * @extends EventTarget
6551 * @abstract
6552 */
6553
6554var Track = function (_EventTarget) {
6555  inherits(Track, _EventTarget);
6556
6557  /**
6558   * Create an instance of this class.
6559   *
6560   * @param {Object} [options={}]
6561   *        Object of option names and values
6562   *
6563   * @param {string} [options.kind='']
6564   *        A valid kind for the track type you are creating.
6565   *
6566   * @param {string} [options.id='vjs_track_' + Guid.newGUID()]
6567   *        A unique id for this AudioTrack.
6568   *
6569   * @param {string} [options.label='']
6570   *        The menu label for this track.
6571   *
6572   * @param {string} [options.language='']
6573   *        A valid two character language code.
6574   *
6575   * @abstract
6576   */
6577  function Track() {
6578    var _ret;
6579
6580    var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
6581    classCallCheck(this, Track);
6582
6583    var _this = possibleConstructorReturn(this, _EventTarget.call(this));
6584
6585    var track = _this; // eslint-disable-line
6586
6587    if (IS_IE8) {
6588      track = document_1.createElement('custom');
6589      for (var prop in Track.prototype) {
6590        if (prop !== 'constructor') {
6591          track[prop] = Track.prototype[prop];
6592        }
6593      }
6594    }
6595
6596    var trackProps = {
6597      id: options.id || 'vjs_track_' + newGUID(),
6598      kind: options.kind || '',
6599      label: options.label || '',
6600      language: options.language || ''
6601    };
6602
6603    /**
6604     * @memberof Track
6605     * @member {string} id
6606     *         The id of this track. Cannot be changed after creation.
6607     * @instance
6608     *
6609     * @readonly
6610     */
6611
6612    /**
6613     * @memberof Track
6614     * @member {string} kind
6615     *         The kind of track that this is. Cannot be changed after creation.
6616     * @instance
6617     *
6618     * @readonly
6619     */
6620
6621    /**
6622     * @memberof Track
6623     * @member {string} label
6624     *         The label of this track. Cannot be changed after creation.
6625     * @instance
6626     *
6627     * @readonly
6628     */
6629
6630    /**
6631     * @memberof Track
6632     * @member {string} language
6633     *         The two letter language code for this track. Cannot be changed after
6634     *         creation.
6635     * @instance
6636     *
6637     * @readonly
6638     */
6639
6640    var _loop = function _loop(key) {
6641      Object.defineProperty(track, key, {
6642        get: function get$$1() {
6643          return trackProps[key];
6644        },
6645        set: function set$$1() {}
6646      });
6647    };
6648
6649    for (var key in trackProps) {
6650      _loop(key);
6651    }
6652
6653    return _ret = track, possibleConstructorReturn(_this, _ret);
6654  }
6655
6656  return Track;
6657}(EventTarget);
6658
6659/**
6660 * @file url.js
6661 * @module url
6662 */
6663/**
6664 * @typedef {Object} url:URLObject
6665 *
6666 * @property {string} protocol
6667 *           The protocol of the url that was parsed.
6668 *
6669 * @property {string} hostname
6670 *           The hostname of the url that was parsed.
6671 *
6672 * @property {string} port
6673 *           The port of the url that was parsed.
6674 *
6675 * @property {string} pathname
6676 *           The pathname of the url that was parsed.
6677 *
6678 * @property {string} search
6679 *           The search query of the url that was parsed.
6680 *
6681 * @property {string} hash
6682 *           The hash of the url that was parsed.
6683 *
6684 * @property {string} host
6685 *           The host of the url that was parsed.
6686 */
6687
6688/**
6689 * Resolve and parse the elements of a URL.
6690 *
6691 * @param  {String} url
6692 *         The url to parse
6693 *
6694 * @return {url:URLObject}
6695 *         An object of url details
6696 */
6697var parseUrl = function parseUrl(url) {
6698  var props = ['protocol', 'hostname', 'port', 'pathname', 'search', 'hash', 'host'];
6699
6700  // add the url to an anchor and let the browser parse the URL
6701  var a = document_1.createElement('a');
6702
6703  a.href = url;
6704
6705  // IE8 (and 9?) Fix
6706  // ie8 doesn't parse the URL correctly until the anchor is actually
6707  // added to the body, and an innerHTML is needed to trigger the parsing
6708  var addToBody = a.host === '' && a.protocol !== 'file:';
6709  var div = void 0;
6710
6711  if (addToBody) {
6712    div = document_1.createElement('div');
6713    div.innerHTML = '<a href="' + url + '"></a>';
6714    a = div.firstChild;
6715    // prevent the div from affecting layout
6716    div.setAttribute('style', 'display:none; position:absolute;');
6717    document_1.body.appendChild(div);
6718  }
6719
6720  // Copy the specific URL properties to a new object
6721  // This is also needed for IE8 because the anchor loses its
6722  // properties when it's removed from the dom
6723  var details = {};
6724
6725  for (var i = 0; i < props.length; i++) {
6726    details[props[i]] = a[props[i]];
6727  }
6728
6729  // IE9 adds the port to the host property unlike everyone else. If
6730  // a port identifier is added for standard ports, strip it.
6731  if (details.protocol === 'http:') {
6732    details.host = details.host.replace(/:80$/, '');
6733  }
6734
6735  if (details.protocol === 'https:') {
6736    details.host = details.host.replace(/:443$/, '');
6737  }
6738
6739  if (addToBody) {
6740    document_1.body.removeChild(div);
6741  }
6742
6743  return details;
6744};
6745
6746/**
6747 * Get absolute version of relative URL. Used to tell flash correct URL.
6748 *
6749 *
6750 * @param  {string} url
6751 *         URL to make absolute
6752 *
6753 * @return {string}
6754 *         Absolute URL
6755 *
6756 * @see http://stackoverflow.com/questions/470832/getting-an-absolute-url-from-a-relative-one-ie6-issue
6757 */
6758var getAbsoluteURL = function getAbsoluteURL(url) {
6759  // Check if absolute URL
6760  if (!url.match(/^https?:\/\//)) {
6761    // Convert to absolute URL. Flash hosted off-site needs an absolute URL.
6762    var div = document_1.createElement('div');
6763
6764    div.innerHTML = '<a href="' + url + '">x</a>';
6765    url = div.firstChild.href;
6766  }
6767
6768  return url;
6769};
6770
6771/**
6772 * Returns the extension of the passed file name. It will return an empty string
6773 * if passed an invalid path.
6774 *
6775 * @param {string} path
6776 *        The fileName path like '/path/to/file.mp4'
6777 *
6778 * @returns {string}
6779 *          The extension in lower case or an empty string if no
6780 *          extension could be found.
6781 */
6782var getFileExtension = function getFileExtension(path) {
6783  if (typeof path === 'string') {
6784    var splitPathRe = /^(\/?)([\s\S]*?)((?:\.{1,2}|[^\/]+?)(\.([^\.\/\?]+)))(?:[\/]*|[\?].*)$/i;
6785    var pathParts = splitPathRe.exec(path);
6786
6787    if (pathParts) {
6788      return pathParts.pop().toLowerCase();
6789    }
6790  }
6791
6792  return '';
6793};
6794
6795/**
6796 * Returns whether the url passed is a cross domain request or not.
6797 *
6798 * @param {string} url
6799 *        The url to check.
6800 *
6801 * @return {boolean}
6802 *         Whether it is a cross domain request or not.
6803 */
6804var isCrossOrigin = function isCrossOrigin(url) {
6805  var winLoc = window_1.location;
6806  var urlInfo = parseUrl(url);
6807
6808  // IE8 protocol relative urls will return ':' for protocol
6809  var srcProtocol = urlInfo.protocol === ':' ? winLoc.protocol : urlInfo.protocol;
6810
6811  // Check if url is for another domain/origin
6812  // IE8 doesn't know location.origin, so we won't rely on it here
6813  var crossOrigin = srcProtocol + urlInfo.host !== winLoc.protocol + winLoc.host;
6814
6815  return crossOrigin;
6816};
6817
6818var Url = (Object.freeze || Object)({
6819	parseUrl: parseUrl,
6820	getAbsoluteURL: getAbsoluteURL,
6821	getFileExtension: getFileExtension,
6822	isCrossOrigin: isCrossOrigin
6823});
6824
6825var isFunction_1 = isFunction;
6826
6827var toString$1 = Object.prototype.toString;
6828
6829function isFunction (fn) {
6830  var string = toString$1.call(fn);
6831  return string === '[object Function]' ||
6832    (typeof fn === 'function' && string !== '[object RegExp]') ||
6833    (typeof window !== 'undefined' &&
6834     // IE8 and below
6835     (fn === window.setTimeout ||
6836      fn === window.alert ||
6837      fn === window.confirm ||
6838      fn === window.prompt))
6839}
6840
6841var trim_1 = createCommonjsModule(function (module, exports) {
6842exports = module.exports = trim;
6843
6844function trim(str){
6845  return str.replace(/^\s*|\s*$/g, '');
6846}
6847
6848exports.left = function(str){
6849  return str.replace(/^\s*/, '');
6850};
6851
6852exports.right = function(str){
6853  return str.replace(/\s*$/, '');
6854};
6855});
6856
6857var forEach_1 = forEach;
6858
6859var toString$2 = Object.prototype.toString;
6860var hasOwnProperty = Object.prototype.hasOwnProperty;
6861
6862function forEach(list, iterator, context) {
6863    if (!isFunction_1(iterator)) {
6864        throw new TypeError('iterator must be a function')
6865    }
6866
6867    if (arguments.length < 3) {
6868        context = this;
6869    }
6870    
6871    if (toString$2.call(list) === '[object Array]')
6872        forEachArray$1(list, iterator, context);
6873    else if (typeof list === 'string')
6874        forEachString(list, iterator, context);
6875    else
6876        forEachObject(list, iterator, context);
6877}
6878
6879function forEachArray$1(array, iterator, context) {
6880    for (var i = 0, len = array.length; i < len; i++) {
6881        if (hasOwnProperty.call(array, i)) {
6882            iterator.call(context, array[i], i, array);
6883        }
6884    }
6885}
6886
6887function forEachString(string, iterator, context) {
6888    for (var i = 0, len = string.length; i < len; i++) {
6889        // no such thing as a sparse string.
6890        iterator.call(context, string.charAt(i), i, string);
6891    }
6892}
6893
6894function forEachObject(object, iterator, context) {
6895    for (var k in object) {
6896        if (hasOwnProperty.call(object, k)) {
6897            iterator.call(context, object[k], k, object);
6898        }
6899    }
6900}
6901
6902var isArray = function(arg) {
6903      return Object.prototype.toString.call(arg) === '[object Array]';
6904    };
6905
6906var parseHeaders = function (headers) {
6907  if (!headers)
6908    return {}
6909
6910  var result = {};
6911
6912  forEach_1(
6913      trim_1(headers).split('\n')
6914    , function (row) {
6915        var index = row.indexOf(':')
6916          , key = trim_1(row.slice(0, index)).toLowerCase()
6917          , value = trim_1(row.slice(index + 1));
6918
6919        if (typeof(result[key]) === 'undefined') {
6920          result[key] = value;
6921        } else if (isArray(result[key])) {
6922          result[key].push(value);
6923        } else {
6924          result[key] = [ result[key], value ];
6925        }
6926      }
6927  );
6928
6929  return result
6930};
6931
6932var immutable = extend;
6933
6934var hasOwnProperty$1 = Object.prototype.hasOwnProperty;
6935
6936function extend() {
6937    var target = {};
6938
6939    for (var i = 0; i < arguments.length; i++) {
6940        var source = arguments[i];
6941
6942        for (var key in source) {
6943            if (hasOwnProperty$1.call(source, key)) {
6944                target[key] = source[key];
6945            }
6946        }
6947    }
6948
6949    return target
6950}
6951
6952var xhr = createXHR;
6953createXHR.XMLHttpRequest = window_1.XMLHttpRequest || noop;
6954createXHR.XDomainRequest = "withCredentials" in (new createXHR.XMLHttpRequest()) ? createXHR.XMLHttpRequest : window_1.XDomainRequest;
6955
6956forEachArray(["get", "put", "post", "patch", "head", "delete"], function(method) {
6957    createXHR[method === "delete" ? "del" : method] = function(uri, options, callback) {
6958        options = initParams(uri, options, callback);
6959        options.method = method.toUpperCase();
6960        return _createXHR(options)
6961    };
6962});
6963
6964function forEachArray(array, iterator) {
6965    for (var i = 0; i < array.length; i++) {
6966        iterator(array[i]);
6967    }
6968}
6969
6970function isEmpty(obj){
6971    for(var i in obj){
6972        if(obj.hasOwnProperty(i)) return false
6973    }
6974    return true
6975}
6976
6977function initParams(uri, options, callback) {
6978    var params = uri;
6979
6980    if (isFunction_1(options)) {
6981        callback = options;
6982        if (typeof uri === "string") {
6983            params = {uri:uri};
6984        }
6985    } else {
6986        params = immutable(options, {uri: uri});
6987    }
6988
6989    params.callback = callback;
6990    return params
6991}
6992
6993function createXHR(uri, options, callback) {
6994    options = initParams(uri, options, callback);
6995    return _createXHR(options)
6996}
6997
6998function _createXHR(options) {
6999    if(typeof options.callback === "undefined"){
7000        throw new Error("callback argument missing")
7001    }
7002
7003    var called = false;
7004    var callback = function cbOnce(err, response, body){
7005        if(!called){
7006            called = true;
7007            options.callback(err, response, body);
7008        }
7009    };
7010
7011    function readystatechange() {
7012        if (xhr.readyState === 4) {
7013            setTimeout(loadFunc, 0);
7014        }
7015    }
7016
7017    function getBody() {
7018        // Chrome with requestType=blob throws errors arround when even testing access to responseText
7019        var body = undefined;
7020
7021        if (xhr.response) {
7022            body = xhr.response;
7023        } else {
7024            body = xhr.responseText || getXml(xhr);
7025        }
7026
7027        if (isJson) {
7028            try {
7029                body = JSON.parse(body);
7030            } catch (e) {}
7031        }
7032
7033        return body
7034    }
7035
7036    function errorFunc(evt) {
7037        clearTimeout(timeoutTimer);
7038        if(!(evt instanceof Error)){
7039            evt = new Error("" + (evt || "Unknown XMLHttpRequest Error") );
7040        }
7041        evt.statusCode = 0;
7042        return callback(evt, failureResponse)
7043    }
7044
7045    // will load the data & process the response in a special response object
7046    function loadFunc() {
7047        if (aborted) return
7048        var status;
7049        clearTimeout(timeoutTimer);
7050        if(options.useXDR && xhr.status===undefined) {
7051            //IE8 CORS GET successful response doesn't have a status field, but body is fine
7052            status = 200;
7053        } else {
7054            status = (xhr.status === 1223 ? 204 : xhr.status);
7055        }
7056        var response = failureResponse;
7057        var err = null;
7058
7059        if (status !== 0){
7060            response = {
7061                body: getBody(),
7062                statusCode: status,
7063                method: method,
7064                headers: {},
vendor: 4,867 bytes, lines 7065-7225
7065                url: uri,
7066                rawRequest: xhr
7067            };
7068            if(xhr.getAllResponseHeaders){ //remember xhr can in fact be XDR for CORS in IE
7069                response.headers = parseHeaders(xhr.getAllResponseHeaders());
7070            }
7071        } else {
7072            err = new Error("Internal XMLHttpRequest Error");
7073        }
7074        return callback(err, response, response.body)
7075    }
7076
7077    var xhr = options.xhr || null;
7078
7079    if (!xhr) {
7080        if (options.cors || options.useXDR) {
7081            xhr = new createXHR.XDomainRequest();
7082        }else{
7083            xhr = new createXHR.XMLHttpRequest();
7084        }
7085    }
7086
7087    var key;
7088    var aborted;
7089    var uri = xhr.url = options.uri || options.url;
7090    var method = xhr.method = options.method || "GET";
7091    var body = options.body || options.data;
7092    var headers = xhr.headers = options.headers || {};
7093    var sync = !!options.sync;
7094    var isJson = false;
7095    var timeoutTimer;
7096    var failureResponse = {
7097        body: undefined,
7098        headers: {},
7099        statusCode: 0,
7100        method: method,
7101        url: uri,
7102        rawRequest: xhr
7103    };
7104
7105    if ("json" in options && options.json !== false) {
7106        isJson = true;
7107        headers["accept"] || headers["Accept"] || (headers["Accept"] = "application/json"); //Don't override existing accept header declared by user
7108        if (method !== "GET" && method !== "HEAD") {
7109            headers["content-type"] || headers["Content-Type"] || (headers["Content-Type"] = "application/json"); //Don't override existing accept header declared by user
7110            body = JSON.stringify(options.json === true ? body : options.json);
7111        }
7112    }
7113
7114    xhr.onreadystatechange = readystatechange;
7115    xhr.onload = loadFunc;
7116    xhr.onerror = errorFunc;
7117    // IE9 must have onprogress be set to a unique function.
7118    xhr.onprogress = function () {
7119        // IE must die
7120    };
7121    xhr.onabort = function(){
7122        aborted = true;
7123    };
7124    xhr.ontimeout = errorFunc;
7125    xhr.open(method, uri, !sync, options.username, options.password);
7126    //has to be after open
7127    if(!sync) {
7128        xhr.withCredentials = !!options.withCredentials;
7129    }
7130    // Cannot set timeout with sync request
7131    // not setting timeout on the xhr object, because of old webkits etc. not handling that correctly
7132    // both npm's request and jquery 1.x use this kind of timeout, so this is being consistent
7133    if (!sync && options.timeout > 0 ) {
7134        timeoutTimer = setTimeout(function(){
7135            if (aborted) return
7136            aborted = true;//IE9 may still call readystatechange
7137            xhr.abort("timeout");
7138            var e = new Error("XMLHttpRequest timeout");
7139            e.code = "ETIMEDOUT";
7140            errorFunc(e);
7141        }, options.timeout );
7142    }
7143
7144    if (xhr.setRequestHeader) {
7145        for(key in headers){
7146            if(headers.hasOwnProperty(key)){
7147                xhr.setRequestHeader(key, headers[key]);
7148            }
7149        }
7150    } else if (options.headers && !isEmpty(options.headers)) {
7151        throw new Error("Headers cannot be set on an XDomainRequest object")
7152    }
7153
7154    if ("responseType" in options) {
7155        xhr.responseType = options.responseType;
7156    }
7157
7158    if ("beforeSend" in options &&
7159        typeof options.beforeSend === "function"
7160    ) {
7161        options.beforeSend(xhr);
7162    }
7163
7164    // Microsoft Edge browser sends "undefined" when send is called with undefined value.
7165    // XMLHttpRequest spec says to pass null as body to indicate no body
7166    // See https://github.com/naugtur/xhr/issues/100.
7167    xhr.send(body || null);
7168
7169    return xhr
7170
7171
7172}
7173
7174function getXml(xhr) {
7175    if (xhr.responseType === "document") {
7176        return xhr.responseXML
7177    }
7178    var firefoxBugTakenEffect = xhr.responseXML && xhr.responseXML.documentElement.nodeName === "parsererror";
7179    if (xhr.responseType === "" && !firefoxBugTakenEffect) {
7180        return xhr.responseXML
7181    }
7182
7183    return null
7184}
7185
7186function noop() {}
7187
7188/**
7189 * @file text-track.js
7190 */
7191/**
7192 * Takes a webvtt file contents and parses it into cues
7193 *
7194 * @param {string} srcContent
7195 *        webVTT file contents
7196 *
7197 * @param {TextTrack} track
7198 *        TextTrack to add cues to. Cues come from the srcContent.
7199 *
7200 * @private
7201 */
7202var parseCues = function parseCues(srcContent, track) {
7203  var parser = new window_1.WebVTT.Parser(window_1, window_1.vttjs, window_1.WebVTT.StringDecoder());
7204  var errors = [];
7205
7206  parser.oncue = function (cue) {
7207    track.addCue(cue);
7208  };
7209
7210  parser.onparsingerror = function (error) {
7211    errors.push(error);
7212  };
7213
7214  parser.onflush = function () {
7215    track.trigger({
7216      type: 'loadeddata',
7217      target: track
7218    });
7219  };
7220
7221  parser.parse(srcContent);
7222  if (errors.length > 0) {
7223    if (window_1.console && window_1.console.groupCollapsed) {
7224      window_1.console.groupCollapsed('Text Track parsing errors for ' + track.src);
7225    }
7226    errors.forEach(function (error) {
7227      return log$1.error(error);
7228    });
7229    if (window_1.console && window_1.console.groupEnd) {
7230      window_1.console.groupEnd();
7231    }
7232  }
7233
7234  parser.flush();
7235};
7236
7237/**
7238 * Load a `TextTrack` from a specifed url.
7239 *
7240 * @param {string} src
7241 *        Url to load track from.
7242 *
7243 * @param {TextTrack} track
7244 *        Track to add cues to. Comes from the content at the end of `url`.
7245 *
7246 * @private
7247 */
7248var loadTrack = function loadTrack(src, track) {
7249  var opts = {
7250    uri: src
7251  };
7252  var crossOrigin = isCrossOrigin(src);
7253
7254  if (crossOrigin) {
7255    opts.cors = crossOrigin;
7256  }
7257
7258  xhr(opts, bind(this, function (err, response, responseBody) {
7259    if (err) {
7260      return log$1.error(err, response);
7261    }
7262
7263    track.loaded_ = true;
7264
7265    // Make sure that vttjs has loaded, otherwise, wait till it finished loading
7266    // NOTE: this is only used for the alt/video.novtt.js build
7267    if (typeof window_1.WebVTT !== 'function') {
7268      if (track.tech_) {
7269        var loadHandler = function loadHandler() {
7270          return parseCues(responseBody, track);
7271        };
7272
7273        track.tech_.on('vttjsloaded', loadHandler);
7274        track.tech_.on('vttjserror', function () {
7275          log$1.error('vttjs failed to load, stopping trying to process ' + track.src);
7276          track.tech_.off('vttjsloaded', loadHandler);
7277        });
7278      }
7279    } else {
7280      parseCues(responseBody, track);
7281    }
7282  }));
7283};
7284
7285/**
7286 * A representation of a single `TextTrack`.
7287 *
7288 * @see [Spec]{@link https://html.spec.whatwg.org/multipage/embedded-content.html#texttrack}
7289 * @extends Track
7290 */
7291
7292var TextTrack = function (_Track) {
7293  inherits(TextTrack, _Track);
7294
7295  /**
7296   * Create an instance of this class.
7297   *
7298   * @param {Object} options={}
7299   *        Object of option names and values
7300   *
7301   * @param {Tech} options.tech
7302   *        A reference to the tech that owns this TextTrack.
7303   *
7304   * @param {TextTrack~Kind} [options.kind='subtitles']
7305   *        A valid text track kind.
7306   *
7307   * @param {TextTrack~Mode} [options.mode='disabled']
7308   *        A valid text track mode.
7309   *
7310   * @param {string} [options.id='vjs_track_' + Guid.newGUID()]
7311   *        A unique id for this TextTrack.
7312   *
7313   * @param {string} [options.label='']
7314   *        The menu label for this track.
7315   *
7316   * @param {string} [options.language='']
7317   *        A valid two character language code.
7318   *
7319   * @param {string} [options.srclang='']
7320   *        A valid two character language code. An alternative, but deprioritized
7321   *        vesion of `options.language`
7322   *
7323   * @param {string} [options.src]
7324   *        A url to TextTrack cues.
7325   *
7326   * @param {boolean} [options.default]
7327   *        If this track should default to on or off.
7328   */
7329  function TextTrack() {
7330    var _this, _ret;
7331
7332    var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
7333    classCallCheck(this, TextTrack);
7334
7335    if (!options.tech) {
7336      throw new Error('A tech was not provided.');
7337    }
7338
7339    var settings = mergeOptions(options, {
7340      kind: TextTrackKind[options.kind] || 'subtitles',
7341      language: options.language || options.srclang || ''
7342    });
7343    var mode = TextTrackMode[settings.mode] || 'disabled';
7344    var default_ = settings['default'];
7345
7346    if (settings.kind === 'metadata' || settings.kind === 'chapters') {
7347      mode = 'hidden';
7348    }
7349    // on IE8 this will be a document element
7350    // for every other browser this will be a normal object
7351    var tt = (_this = possibleConstructorReturn(this, _Track.call(this, settings)), _this);
7352
7353    tt.tech_ = settings.tech;
7354
7355    if (IS_IE8) {
7356      for (var prop in TextTrack.prototype) {
7357        if (prop !== 'constructor') {
7358          tt[prop] = TextTrack.prototype[prop];
7359        }
7360      }
7361    }
7362
7363    tt.cues_ = [];
7364    tt.activeCues_ = [];
7365
7366    var cues = new TextTrackCueList(tt.cues_);
7367    var activeCues = new TextTrackCueList(tt.activeCues_);
7368    var changed = false;
7369    var timeupdateHandler = bind(tt, function () {
7370
7371      // Accessing this.activeCues for the side-effects of updating itself
7372      // due to it's nature as a getter function. Do not remove or cues will
7373      // stop updating!
7374      /* eslint-disable no-unused-expressions */
7375      this.activeCues;
7376      /* eslint-enable no-unused-expressions */
7377      if (changed) {
7378        this.trigger('cuechange');
7379        changed = false;
vendor: 6,553 bytes, lines 7380-7635
7380      }
7381    });
7382
7383    if (mode !== 'disabled') {
7384      tt.tech_.ready(function () {
7385        tt.tech_.on('timeupdate', timeupdateHandler);
7386      }, true);
7387    }
7388
7389    /**
7390     * @memberof TextTrack
7391     * @member {boolean} default
7392     *         If this track was set to be on or off by default. Cannot be changed after
7393     *         creation.
7394     * @instance
7395     *
7396     * @readonly
7397     */
7398    Object.defineProperty(tt, 'default', {
7399      get: function get$$1() {
7400        return default_;
7401      },
7402      set: function set$$1() {}
7403    });
7404
7405    /**
7406     * @memberof TextTrack
7407     * @member {string} mode
7408     *         Set the mode of this TextTrack to a valid {@link TextTrack~Mode}. Will
7409     *         not be set if setting to an invalid mode.
7410     * @instance
7411     *
7412     * @fires TextTrack#modechange
7413     */
7414    Object.defineProperty(tt, 'mode', {
7415      get: function get$$1() {
7416        return mode;
7417      },
7418      set: function set$$1(newMode) {
7419        var _this2 = this;
7420
7421        if (!TextTrackMode[newMode]) {
7422          return;
7423        }
7424        mode = newMode;
7425        if (mode === 'showing') {
7426
7427          this.tech_.ready(function () {
7428            _this2.tech_.on('timeupdate', timeupdateHandler);
7429          }, true);
7430        }
7431        /**
7432         * An event that fires when mode changes on this track. This allows
7433         * the TextTrackList that holds this track to act accordingly.
7434         *
7435         * > Note: This is not part of the spec!
7436         *
7437         * @event TextTrack#modechange
7438         * @type {EventTarget~Event}
7439         */
7440        this.trigger('modechange');
7441      }
7442    });
7443
7444    /**
7445     * @memberof TextTrack
7446     * @member {TextTrackCueList} cues
7447     *         The text track cue list for this TextTrack.
7448     * @instance
7449     */
7450    Object.defineProperty(tt, 'cues', {
7451      get: function get$$1() {
7452        if (!this.loaded_) {
7453          return null;
7454        }
7455
7456        return cues;
7457      },
7458      set: function set$$1() {}
7459    });
7460
7461    /**
7462     * @memberof TextTrack
7463     * @member {TextTrackCueList} activeCues
7464     *         The list text track cues that are currently active for this TextTrack.
7465     * @instance
7466     */
7467    Object.defineProperty(tt, 'activeCues', {
7468      get: function get$$1() {
7469        if (!this.loaded_) {
7470          return null;
7471        }
7472
7473        // nothing to do
7474        if (this.cues.length === 0) {
7475          return activeCues;
7476        }
7477
7478        var ct = this.tech_.currentTime();
7479        var active = [];
7480
7481        for (var i = 0, l = this.cues.length; i < l; i++) {
7482          var cue = this.cues[i];
7483
7484          if (cue.startTime <= ct && cue.endTime >= ct) {
7485            active.push(cue);
7486          } else if (cue.startTime === cue.endTime && cue.startTime <= ct && cue.startTime + 0.5 >= ct) {
7487            active.push(cue);
7488          }
7489        }
7490
7491        changed = false;
7492
7493        if (active.length !== this.activeCues_.length) {
7494          changed = true;
7495        } else {
7496          for (var _i = 0; _i < active.length; _i++) {
7497            if (this.activeCues_.indexOf(active[_i]) === -1) {
7498              changed = true;
7499            }
7500          }
7501        }
7502
7503        this.activeCues_ = active;
7504        activeCues.setCues_(this.activeCues_);
7505
7506        return activeCues;
7507      },
7508      set: function set$$1() {}
7509    });
7510
7511    if (settings.src) {
7512      tt.src = settings.src;
7513      loadTrack(settings.src, tt);
7514    } else {
7515      tt.loaded_ = true;
7516    }
7517
7518    return _ret = tt, possibleConstructorReturn(_this, _ret);
7519  }
7520
7521  /**
7522   * Add a cue to the internal list of cues.
7523   *
7524   * @param {TextTrack~Cue} cue
7525   *        The cue to add to our internal list
7526   */
7527
7528
7529  TextTrack.prototype.addCue = function addCue(originalCue) {
7530    var cue = originalCue;
7531
7532    if (window_1.vttjs && !(originalCue instanceof window_1.vttjs.VTTCue)) {
7533      cue = new window_1.vttjs.VTTCue(originalCue.startTime, originalCue.endTime, originalCue.text);
7534
7535      for (var prop in originalCue) {
7536        if (!(prop in cue)) {
7537          cue[prop] = originalCue[prop];
7538        }
7539      }
7540
7541      // make sure that `id` is copied over
7542      cue.id = originalCue.id;
7543      cue.originalCue_ = originalCue;
7544    }
7545
7546    var tracks = this.tech_.textTracks();
7547
7548    for (var i = 0; i < tracks.length; i++) {
7549      if (tracks[i] !== this) {
7550        tracks[i].removeCue(cue);
7551      }
7552    }
7553
7554    this.cues_.push(cue);
7555    this.cues.setCues_(this.cues_);
7556  };
7557
7558  /**
7559   * Remove a cue from our internal list
7560   *
7561   * @param {TextTrack~Cue} removeCue
7562   *        The cue to remove from our internal list
7563   */
7564
7565
7566  TextTrack.prototype.removeCue = function removeCue(_removeCue) {
7567    var i = this.cues_.length;
7568
7569    while (i--) {
7570      var cue = this.cues_[i];
7571
7572      if (cue === _removeCue || cue.originalCue_ && cue.originalCue_ === _removeCue) {
7573        this.cues_.splice(i, 1);
7574        this.cues.setCues_(this.cues_);
7575        break;
7576      }
7577    }
7578  };
7579
7580  return TextTrack;
7581}(Track);
7582
7583/**
7584 * cuechange - One or more cues in the track have become active or stopped being active.
7585 */
7586
7587
7588TextTrack.prototype.allowedEvents_ = {
7589  cuechange: 'cuechange'
7590};
7591
7592/**
7593 * A representation of a single `AudioTrack`. If it is part of an {@link AudioTrackList}
7594 * only one `AudioTrack` in the list will be enabled at a time.
7595 *
7596 * @see [Spec]{@link https://html.spec.whatwg.org/multipage/embedded-content.html#audiotrack}
7597 * @extends Track
7598 */
7599
7600var AudioTrack = function (_Track) {
7601  inherits(AudioTrack, _Track);
7602
7603  /**
7604   * Create an instance of this class.
7605   *
7606   * @param {Object} [options={}]
7607   *        Object of option names and values
7608   *
7609   * @param {AudioTrack~Kind} [options.kind='']
7610   *        A valid audio track kind
7611   *
7612   * @param {string} [options.id='vjs_track_' + Guid.newGUID()]
7613   *        A unique id for this AudioTrack.
7614   *
7615   * @param {string} [options.label='']
7616   *        The menu label for this track.
7617   *
7618   * @param {string} [options.language='']
7619   *        A valid two character language code.
7620   *
7621   * @param {boolean} [options.enabled]
7622   *        If this track is the one that is currently playing. If this track is part of
7623   *        an {@link AudioTrackList}, only one {@link AudioTrack} will be enabled.
7624   */
7625  function AudioTrack() {
7626    var _this, _ret;
7627
7628    var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
7629    classCallCheck(this, AudioTrack);
7630
7631    var settings = mergeOptions(options, {
7632      kind: AudioTrackKind[options.kind] || ''
7633    });
7634    // on IE8 this will be a document element
7635    // for every other browser this will be a normal object
7636    var track = (_this = possibleConstructorReturn(this, _Track.call(this, settings)), _this);
7637    var enabled = false;
7638
7639    if (IS_IE8) {
7640      for (var prop in AudioTrack.prototype) {
7641        if (prop !== 'constructor') {
7642          track[prop] = AudioTrack.prototype[prop];
7643        }
7644      }
7645    }
7646    /**
7647     * @memberof AudioTrack
7648     * @member {boolean} enabled
7649     *         If this `AudioTrack` is enabled or not. When setting this will
7650     *         fire {@link AudioTrack#enabledchange} if the state of enabled is changed.
7651     * @instance
7652     *
7653     * @fires VideoTrack#selectedchange
7654     */
7655    Object.defineProperty(track, 'enabled', {
7656      get: function get$$1() {
7657        return enabled;
7658      },
7659      set: function set$$1(newEnabled) {
7660        // an invalid or unchanged value
7661        if (typeof newEnabled !== 'boolean' || newEnabled === enabled) {
7662          return;
7663        }
7664        enabled = newEnabled;
7665
7666        /**
7667         * An event that fires when enabled changes on this track. This allows
7668         * the AudioTrackList that holds this track to act accordingly.
7669         *
7670         * > Note: This is not part of the spec! Native tracks will do
7671         *         this internally without an event.
7672         *
7673         * @event AudioTrack#enabledchange
7674         * @type {EventTarget~Event}
7675         */
7676        this.trigger('enabledchange');
7677      }
7678    });
7679
7680    // if the user sets this track to selected then
7681    // set selected to that true value otherwise
7682    // we keep it false
7683    if (settings.enabled) {
7684      track.enabled = settings.enabled;
7685    }
7686    track.loaded_ = true;
7687
7688    return _ret = track, possibleConstructorReturn(_this, _ret);
7689  }
7690
7691  return AudioTrack;
7692}(Track);
7693
7694/**
7695 * A representation of a single `VideoTrack`.
7696 *
7697 * @see [Spec]{@link https://html.spec.whatwg.org/multipage/embedded-content.html#videotrack}
7698 * @extends Track
7699 */
7700
7701var VideoTrack = function (_Track) {
7702  inherits(VideoTrack, _Track);
7703
7704  /**
7705   * Create an instance of this class.
7706   *
7707   * @param {Object} [options={}]
7708   *        Object of option names and values
7709   *
7710   * @param {string} [options.kind='']
7711   *        A valid {@link VideoTrack~Kind}
7712   *
7713   * @param {string} [options.id='vjs_track_' + Guid.newGUID()]
7714   *        A unique id for this AudioTrack.
7715   *
7716   * @param {string} [options.label='']
7717   *        The menu label for this track.
7718   *
7719   * @param {string} [options.language='']
7720   *        A valid two character language code.
7721   *
7722   * @param {boolean} [options.selected]
7723   *        If this track is the one that is currently playing.
7724   */
7725  function VideoTrack() {
7726    var _this, _ret;
7727
7728    var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
7729    classCallCheck(this, VideoTrack);
7730
7731    var settings = mergeOptions(options, {
7732      kind: VideoTrackKind[options.kind] || ''
7733    });
7734
7735    // on IE8 this will be a document element
7736    // for every other browser this will be a normal object
7737    var track = (_this = possibleConstructorReturn(this, _Track.call(this, settings)), _this);
7738    var selected = false;
7739
7740    if (IS_IE8) {
7741      for (var prop in VideoTrack.prototype) {
7742        if (prop !== 'constructor') {
7743          track[prop] = VideoTrack.prototype[prop];
7744        }
7745      }
7746    }
7747
7748    /**
7749     * @memberof VideoTrack
7750     * @member {boolean} selected
7751     *         If this `VideoTrack` is selected or not. When setting this will
7752     *         fire {@link VideoTrack#selectedchange} if the state of selected changed.
7753     * @instance
7754     *
7755     * @fires VideoTrack#selectedchange
7756     */
7757    Object.defineProperty(track, 'selected', {
7758      get: function get$$1() {
7759        return selected;
7760      },
7761      set: function set$$1(newSelected) {
7762        // an invalid or unchanged value
7763        if (typeof newSelected !== 'boolean' || newSelected === selected) {
7764          return;
7765        }
7766        selected = newSelected;
7767
7768        /**
7769         * An event that fires when selected changes on this track. This allows
7770         * the VideoTrackList that holds this track to act accordingly.
7771         *
7772         * > Note: This is not part of the spec! Native tracks will do
7773         *         this internally without an event.
7774         *
7775         * @event VideoTrack#selectedchange
7776         * @type {EventTarget~Event}
7777         */
7778        this.trigger('selectedchange');
7779      }
7780    });
7781
7782    // if the user sets this track to selected then
7783    // set selected to that true value otherwise
7784    // we keep it false
7785    if (settings.selected) {
7786      track.selected = settings.selected;
7787    }
7788
7789    return _ret = track, possibleConstructorReturn(_this, _ret);
7790  }
7791
7792  return VideoTrack;
7793}(Track);
7794
7795/**
7796 * @file html-track-element.js
7797 */
7798
7799/**
7800 * @memberof HTMLTrackElement
7801 * @typedef {HTMLTrackElement~ReadyState}
7802 * @enum {number}
7803 */
7804var NONE = 0;
7805var LOADING = 1;
7806var LOADED = 2;
7807var ERROR = 3;
7808
7809/**
7810 * A single track represented in the DOM.
7811 *
7812 * @see [Spec]{@link https://html.spec.whatwg.org/multipage/embedded-content.html#htmltrackelement}
7813 * @extends EventTarget
7814 */
7815
7816var HTMLTrackElement = function (_EventTarget) {
7817  inherits(HTMLTrackElement, _EventTarget);
7818
7819  /**
7820   * Create an instance of this class.
7821   *
7822   * @param {Object} options={}
7823   *        Object of option names and values
7824   *
7825   * @param {Tech} options.tech
7826   *        A reference to the tech that owns this HTMLTrackElement.
7827   *
7828   * @param {TextTrack~Kind} [options.kind='subtitles']
7829   *        A valid text track kind.
7830   *
7831   * @param {TextTrack~Mode} [options.mode='disabled']
7832   *        A valid text track mode.
7833   *
7834   * @param {string} [options.id='vjs_track_' + Guid.newGUID()]
7835   *        A unique id for this TextTrack.
7836   *
7837   * @param {string} [options.label='']
7838   *        The menu label for this track.
7839   *
7840   * @param {string} [options.language='']
7841   *        A valid two character language code.
7842   *
7843   * @param {string} [options.srclang='']
7844   *        A valid two character language code. An alternative, but deprioritized
7845   *        vesion of `options.language`
7846   *
7847   * @param {string} [options.src]
7848   *        A url to TextTrack cues.
7849   *
7850   * @param {boolean} [options.default]
7851   *        If this track should default to on or off.
7852   */
7853  function HTMLTrackElement() {
7854    var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
7855    classCallCheck(this, HTMLTrackElement);
7856
7857    var _this = possibleConstructorReturn(this, _EventTarget.call(this));
7858
7859    var readyState = void 0;
7860    var trackElement = _this; // eslint-disable-line
7861
7862    if (IS_IE8) {
7863      trackElement = document_1.createElement('custom');
7864
7865      for (var prop in HTMLTrackElement.prototype) {
7866        if (prop !== 'constructor') {
7867          trackElement[prop] = HTMLTrackElement.prototype[prop];
7868        }
7869      }
7870    }
7871
7872    var track = new TextTrack(options);
7873
7874    trackElement.kind = track.kind;
7875    trackElement.src = track.src;
7876    trackElement.srclang = track.language;
7877    trackElement.label = track.label;
7878    trackElement['default'] = track['default'];
7879
7880    /**
7881     * @memberof HTMLTrackElement
7882     * @member {HTMLTrackElement~ReadyState} readyState
7883     *         The current ready state of the track element.
7884     * @instance
7885     */
7886    Object.defineProperty(trackElement, 'readyState', {
7887      get: function get$$1() {
7888        return readyState;
7889      }
7890    });
7891
7892    /**
7893     * @memberof HTMLTrackElement
7894     * @member {TextTrack} track
7895     *         The underlying TextTrack object.
7896     * @instance
7897     *
7898     */
7899    Object.defineProperty(trackElement, 'track', {
7900      get: function get$$1() {
7901        return track;
7902      }
7903    });
7904
7905    readyState = NONE;
7906
7907    /**
7908     * @listens TextTrack#loadeddata
7909     * @fires HTMLTrackElement#load
7910     */
7911    track.addEventListener('loadeddata', function () {
7912      readyState = LOADED;
7913
7914      trackElement.trigger({
7915        type: 'load',
7916        target: trackElement
7917      });
7918    });
7919
7920    if (IS_IE8) {
7921      var _ret;
7922
7923      return _ret = trackElement, possibleConstructorReturn(_this, _ret);
7924    }
7925    return _this;
7926  }
7927
7928  return HTMLTrackElement;
7929}(EventTarget);
7930
7931HTMLTrackElement.prototype.allowedEvents_ = {
7932  load: 'load'
7933};
7934
7935HTMLTrackElement.NONE = NONE;
7936HTMLTrackElement.LOADING = LOADING;
7937HTMLTrackElement.LOADED = LOADED;
7938HTMLTrackElement.ERROR = ERROR;
7939
7940/*
7941 * This file contains all track properties that are used in
7942 * player.js, tech.js, html5.js and possibly other techs in the future.
7943 */
7944
7945var NORMAL = {
7946  audio: {
7947    ListClass: AudioTrackList,
7948    TrackClass: AudioTrack,
7949    capitalName: 'Audio'
7950  },
7951  video: {
7952    ListClass: VideoTrackList,
7953    TrackClass: VideoTrack,
7954    capitalName: 'Video'
7955  },
7956  text: {
7957    ListClass: TextTrackList,
7958    TrackClass: TextTrack,
7959    capitalName: 'Text'
7960  }
7961};
7962
7963Object.keys(NORMAL).forEach(function (type) {
7964  NORMAL[type].getterName = type + 'Tracks';
7965  NORMAL[type].privateName = type + 'Tracks_';
7966});
7967
7968var REMOTE = {
7969  remoteText: {
7970    ListClass: TextTrackList,
7971    TrackClass: TextTrack,
7972    capitalName: 'RemoteText',
7973    getterName: 'remoteTextTracks',
7974    privateName: 'remoteTextTracks_'
7975  },
7976  remoteTextEl: {
7977    ListClass: HtmlTrackElementList,
7978    TrackClass: HTMLTrackElement,
7979    capitalName: 'RemoteTextTrackEls',
7980    getterName: 'remoteTextTrackEls',
7981    privateName: 'remoteTextTrackEls_'
7982  }
7983};
7984
7985var ALL = mergeOptions(NORMAL, REMOTE);
7986
7987REMOTE.names = Object.keys(REMOTE);
7988NORMAL.names = Object.keys(NORMAL);
7989ALL.names = [].concat(REMOTE.names).concat(NORMAL.names);
7990
7991/**
7992 * Copyright 2013 vtt.js Contributors
7993 *
7994 * Licensed under the Apache License, Version 2.0 (the "License");
vendor: 16,438 bytes, lines 7995-8532
7995 * you may not use this file except in compliance with the License.
7996 * You may obtain a copy of the License at
7997 *
7998 *   http://www.apache.org/licenses/LICENSE-2.0
7999 *
8000 * Unless required by applicable law or agreed to in writing, software
8001 * distributed under the License is distributed on an "AS IS" BASIS,
8002 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
8003 * See the License for the specific language governing permissions and
8004 * limitations under the License.
8005 */
8006
8007/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
8008/* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */
8009var _objCreate = Object.create || (function() {
8010  function F() {}
8011  return function(o) {
8012    if (arguments.length !== 1) {
8013      throw new Error('Object.create shim only accepts one parameter.');
8014    }
8015    F.prototype = o;
8016    return new F();
8017  };
8018})();
8019
8020// Creates a new ParserError object from an errorData object. The errorData
8021// object should have default code and message properties. The default message
8022// property can be overriden by passing in a message parameter.
8023// See ParsingError.Errors below for acceptable errors.
8024function ParsingError(errorData, message) {
8025  this.name = "ParsingError";
8026  this.code = errorData.code;
8027  this.message = message || errorData.message;
8028}
8029ParsingError.prototype = _objCreate(Error.prototype);
8030ParsingError.prototype.constructor = ParsingError;
8031
8032// ParsingError metadata for acceptable ParsingErrors.
8033ParsingError.Errors = {
8034  BadSignature: {
8035    code: 0,
8036    message: "Malformed WebVTT signature."
8037  },
8038  BadTimeStamp: {
8039    code: 1,
8040    message: "Malformed time stamp."
8041  }
8042};
8043
8044// Try to parse input as a time stamp.
8045function parseTimeStamp(input) {
8046
8047  function computeSeconds(h, m, s, f) {
8048    return (h | 0) * 3600 + (m | 0) * 60 + (s | 0) + (f | 0) / 1000;
8049  }
8050
8051  var m = input.match(/^(\d+):(\d{2})(:\d{2})?\.(\d{3})/);
8052  if (!m) {
8053    return null;
8054  }
8055
8056  if (m[3]) {
8057    // Timestamp takes the form of [hours]:[minutes]:[seconds].[milliseconds]
8058    return computeSeconds(m[1], m[2], m[3].replace(":", ""), m[4]);
8059  } else if (m[1] > 59) {
8060    // Timestamp takes the form of [hours]:[minutes].[milliseconds]
8061    // First position is hours as it's over 59.
8062    return computeSeconds(m[1], m[2], 0,  m[4]);
8063  } else {
8064    // Timestamp takes the form of [minutes]:[seconds].[milliseconds]
8065    return computeSeconds(0, m[1], m[2], m[4]);
8066  }
8067}
8068
8069// A settings object holds key/value pairs and will ignore anything but the first
8070// assignment to a specific key.
8071function Settings() {
8072  this.values = _objCreate(null);
8073}
8074
8075Settings.prototype = {
8076  // Only accept the first assignment to any key.
8077  set: function(k, v) {
8078    if (!this.get(k) && v !== "") {
8079      this.values[k] = v;
8080    }
8081  },
8082  // Return the value for a key, or a default value.
8083  // If 'defaultKey' is passed then 'dflt' is assumed to be an object with
8084  // a number of possible default values as properties where 'defaultKey' is
8085  // the key of the property that will be chosen; otherwise it's assumed to be
8086  // a single value.
8087  get: function(k, dflt, defaultKey) {
8088    if (defaultKey) {
8089      return this.has(k) ? this.values[k] : dflt[defaultKey];
8090    }
8091    return this.has(k) ? this.values[k] : dflt;
8092  },
8093  // Check whether we have a value for a key.
8094  has: function(k) {
8095    return k in this.values;
8096  },
8097  // Accept a setting if its one of the given alternatives.
8098  alt: function(k, v, a) {
8099    for (var n = 0; n < a.length; ++n) {
8100      if (v === a[n]) {
8101        this.set(k, v);
8102        break;
8103      }
8104    }
8105  },
8106  // Accept a setting if its a valid (signed) integer.
8107  integer: function(k, v) {
8108    if (/^-?\d+$/.test(v)) { // integer
8109      this.set(k, parseInt(v, 10));
8110    }
8111  },
8112  // Accept a setting if its a valid percentage.
8113  percent: function(k, v) {
8114    var m;
8115    if ((m = v.match(/^([\d]{1,3})(\.[\d]*)?%$/))) {
8116      v = parseFloat(v);
8117      if (v >= 0 && v <= 100) {
8118        this.set(k, v);
8119        return true;
8120      }
8121    }
8122    return false;
8123  }
8124};
8125
8126// Helper function to parse input into groups separated by 'groupDelim', and
8127// interprete each group as a key/value pair separated by 'keyValueDelim'.
8128function parseOptions(input, callback, keyValueDelim, groupDelim) {
8129  var groups = groupDelim ? input.split(groupDelim) : [input];
8130  for (var i in groups) {
8131    if (typeof groups[i] !== "string") {
8132      continue;
8133    }
8134    var kv = groups[i].split(keyValueDelim);
8135    if (kv.length !== 2) {
8136      continue;
8137    }
8138    var k = kv[0];
8139    var v = kv[1];
8140    callback(k, v);
8141  }
8142}
8143
8144function parseCue(input, cue, regionList) {
8145  // Remember the original input if we need to throw an error.
8146  var oInput = input;
8147  // 4.1 WebVTT timestamp
8148  function consumeTimeStamp() {
8149    var ts = parseTimeStamp(input);
8150    if (ts === null) {
8151      throw new ParsingError(ParsingError.Errors.BadTimeStamp,
8152                            "Malformed timestamp: " + oInput);
8153    }
8154    // Remove time stamp from input.
8155    input = input.replace(/^[^\sa-zA-Z-]+/, "");
8156    return ts;
8157  }
8158
8159  // 4.4.2 WebVTT cue settings
8160  function consumeCueSettings(input, cue) {
8161    var settings = new Settings();
8162
8163    parseOptions(input, function (k, v) {
8164      switch (k) {
8165      case "region":
8166        // Find the last region we parsed with the same region id.
8167        for (var i = regionList.length - 1; i >= 0; i--) {
8168          if (regionList[i].id === v) {
8169            settings.set(k, regionList[i].region);
8170            break;
8171          }
8172        }
8173        break;
8174      case "vertical":
8175        settings.alt(k, v, ["rl", "lr"]);
8176        break;
8177      case "line":
8178        var vals = v.split(","),
8179            vals0 = vals[0];
8180        settings.integer(k, vals0);
8181        settings.percent(k, vals0) ? settings.set("snapToLines", false) : null;
8182        settings.alt(k, vals0, ["auto"]);
8183        if (vals.length === 2) {
8184          settings.alt("lineAlign", vals[1], ["start", "middle", "end"]);
8185        }
8186        break;
8187      case "position":
8188        vals = v.split(",");
8189        settings.percent(k, vals[0]);
8190        if (vals.length === 2) {
8191          settings.alt("positionAlign", vals[1], ["start", "middle", "end"]);
8192        }
8193        break;
8194      case "size":
8195        settings.percent(k, v);
8196        break;
8197      case "align":
8198        settings.alt(k, v, ["start", "middle", "end", "left", "right"]);
8199        break;
8200      }
8201    }, /:/, /\s/);
8202
8203    // Apply default values for any missing fields.
8204    cue.region = settings.get("region", null);
8205    cue.vertical = settings.get("vertical", "");
8206    cue.line = settings.get("line", "auto");
8207    cue.lineAlign = settings.get("lineAlign", "start");
8208    cue.snapToLines = settings.get("snapToLines", true);
8209    cue.size = settings.get("size", 100);
8210    cue.align = settings.get("align", "middle");
8211    cue.position = settings.get("position", {
8212      start: 0,
8213      left: 0,
8214      middle: 50,
8215      end: 100,
8216      right: 100
8217    }, cue.align);
8218    cue.positionAlign = settings.get("positionAlign", {
8219      start: "start",
8220      left: "start",
8221      middle: "middle",
8222      end: "end",
8223      right: "end"
8224    }, cue.align);
8225  }
8226
8227  function skipWhitespace() {
8228    input = input.replace(/^\s+/, "");
8229  }
8230
8231  // 4.1 WebVTT cue timings.
8232  skipWhitespace();
8233  cue.startTime = consumeTimeStamp();   // (1) collect cue start time
8234  skipWhitespace();
8235  if (input.substr(0, 3) !== "-->") {     // (3) next characters must match "-->"
8236    throw new ParsingError(ParsingError.Errors.BadTimeStamp,
8237                           "Malformed time stamp (time stamps must be separated by '-->'): " +
8238                           oInput);
8239  }
8240  input = input.substr(3);
8241  skipWhitespace();
8242  cue.endTime = consumeTimeStamp();     // (5) collect cue end time
8243
8244  // 4.1 WebVTT cue settings list.
8245  skipWhitespace();
8246  consumeCueSettings(input, cue);
8247}
8248
8249var ESCAPE = {
8250  "&amp;": "&",
8251  "&lt;": "<",
8252  "&gt;": ">",
8253  "&lrm;": "\u200e",
8254  "&rlm;": "\u200f",
8255  "&nbsp;": "\u00a0"
8256};
8257
8258var TAG_NAME = {
8259  c: "span",
8260  i: "i",
8261  b: "b",
8262  u: "u",
8263  ruby: "ruby",
8264  rt: "rt",
8265  v: "span",
8266  lang: "span"
8267};
8268
8269var TAG_ANNOTATION = {
8270  v: "title",
8271  lang: "lang"
8272};
8273
8274var NEEDS_PARENT = {
8275  rt: "ruby"
8276};
8277
8278// Parse content into a document fragment.
8279function parseContent(window, input) {
8280  function nextToken() {
8281    // Check for end-of-string.
8282    if (!input) {
8283      return null;
8284    }
8285
8286    // Consume 'n' characters from the input.
8287    function consume(result) {
8288      input = input.substr(result.length);
8289      return result;
8290    }
8291
8292    var m = input.match(/^([^<]*)(<[^>]+>?)?/);
8293    // If there is some text before the next tag, return it, otherwise return
8294    // the tag.
8295    return consume(m[1] ? m[1] : m[2]);
8296  }
8297
8298  // Unescape a string 's'.
8299  function unescape1(e) {
8300    return ESCAPE[e];
8301  }
8302  function unescape(s) {
8303    while ((m = s.match(/&(amp|lt|gt|lrm|rlm|nbsp);/))) {
8304      s = s.replace(m[0], unescape1);
8305    }
8306    return s;
8307  }
8308
8309  function shouldAdd(current, element) {
8310    return !NEEDS_PARENT[element.localName] ||
8311           NEEDS_PARENT[element.localName] === current.localName;
8312  }
8313
8314  // Create an element for this tag.
8315  function createElement(type, annotation) {
8316    var tagName = TAG_NAME[type];
8317    if (!tagName) {
8318      return null;
8319    }
8320    var element = window.document.createElement(tagName);
8321    element.localName = tagName;
8322    var name = TAG_ANNOTATION[type];
8323    if (name && annotation) {
8324      element[name] = annotation.trim();
8325    }
8326    return element;
8327  }
8328
8329  var rootDiv = window.document.createElement("div"),
8330      current = rootDiv,
8331      t,
8332      tagStack = [];
8333
8334  while ((t = nextToken()) !== null) {
8335    if (t[0] === '<') {
8336      if (t[1] === "/") {
8337        // If the closing tag matches, move back up to the parent node.
8338        if (tagStack.length &&
8339            tagStack[tagStack.length - 1] === t.substr(2).replace(">", "")) {
8340          tagStack.pop();
8341          current = current.parentNode;
8342        }
8343        // Otherwise just ignore the end tag.
8344        continue;
8345      }
8346      var ts = parseTimeStamp(t.substr(1, t.length - 2));
8347      var node;
8348      if (ts) {
8349        // Timestamps are lead nodes as well.
8350        node = window.document.createProcessingInstruction("timestamp", ts);
8351        current.appendChild(node);
8352        continue;
8353      }
8354      var m = t.match(/^<([^.\s/0-9>]+)(\.[^\s\\>]+)?([^>\\]+)?(\\?)>?$/);
8355      // If we can't parse the tag, skip to the next tag.
8356      if (!m) {
8357        continue;
8358      }
8359      // Try to construct an element, and ignore the tag if we couldn't.
8360      node = createElement(m[1], m[3]);
8361      if (!node) {
8362        continue;
8363      }
8364      // Determine if the tag should be added based on the context of where it
8365      // is placed in the cuetext.
8366      if (!shouldAdd(current, node)) {
8367        continue;
8368      }
8369      // Set the class list (as a list of classes, separated by space).
8370      if (m[2]) {
8371        node.className = m[2].substr(1).replace('.', ' ');
8372      }
8373      // Append the node to the current node, and enter the scope of the new
8374      // node.
8375      tagStack.push(m[1]);
8376      current.appendChild(node);
8377      current = node;
8378      continue;
8379    }
8380
8381    // Text nodes are leaf nodes.
8382    current.appendChild(window.document.createTextNode(unescape(t)));
8383  }
8384
8385  return rootDiv;
8386}
8387
8388// This is a list of all the Unicode characters that have a strong
8389// right-to-left category. What this means is that these characters are
8390// written right-to-left for sure. It was generated by pulling all the strong
8391// right-to-left characters out of the Unicode data table. That table can
8392// found at: http://www.unicode.org/Public/UNIDATA/UnicodeData.txt
8393var strongRTLRanges = [[0x5be, 0x5be], [0x5c0, 0x5c0], [0x5c3, 0x5c3], [0x5c6, 0x5c6],
8394 [0x5d0, 0x5ea], [0x5f0, 0x5f4], [0x608, 0x608], [0x60b, 0x60b], [0x60d, 0x60d],
8395 [0x61b, 0x61b], [0x61e, 0x64a], [0x66d, 0x66f], [0x671, 0x6d5], [0x6e5, 0x6e6],
8396 [0x6ee, 0x6ef], [0x6fa, 0x70d], [0x70f, 0x710], [0x712, 0x72f], [0x74d, 0x7a5],
8397 [0x7b1, 0x7b1], [0x7c0, 0x7ea], [0x7f4, 0x7f5], [0x7fa, 0x7fa], [0x800, 0x815],
8398 [0x81a, 0x81a], [0x824, 0x824], [0x828, 0x828], [0x830, 0x83e], [0x840, 0x858],
8399 [0x85e, 0x85e], [0x8a0, 0x8a0], [0x8a2, 0x8ac], [0x200f, 0x200f],
8400 [0xfb1d, 0xfb1d], [0xfb1f, 0xfb28], [0xfb2a, 0xfb36], [0xfb38, 0xfb3c],
8401 [0xfb3e, 0xfb3e], [0xfb40, 0xfb41], [0xfb43, 0xfb44], [0xfb46, 0xfbc1],
8402 [0xfbd3, 0xfd3d], [0xfd50, 0xfd8f], [0xfd92, 0xfdc7], [0xfdf0, 0xfdfc],
8403 [0xfe70, 0xfe74], [0xfe76, 0xfefc], [0x10800, 0x10805], [0x10808, 0x10808],
8404 [0x1080a, 0x10835], [0x10837, 0x10838], [0x1083c, 0x1083c], [0x1083f, 0x10855],
8405 [0x10857, 0x1085f], [0x10900, 0x1091b], [0x10920, 0x10939], [0x1093f, 0x1093f],
8406 [0x10980, 0x109b7], [0x109be, 0x109bf], [0x10a00, 0x10a00], [0x10a10, 0x10a13],
8407 [0x10a15, 0x10a17], [0x10a19, 0x10a33], [0x10a40, 0x10a47], [0x10a50, 0x10a58],
8408 [0x10a60, 0x10a7f], [0x10b00, 0x10b35], [0x10b40, 0x10b55], [0x10b58, 0x10b72],
8409 [0x10b78, 0x10b7f], [0x10c00, 0x10c48], [0x1ee00, 0x1ee03], [0x1ee05, 0x1ee1f],
8410 [0x1ee21, 0x1ee22], [0x1ee24, 0x1ee24], [0x1ee27, 0x1ee27], [0x1ee29, 0x1ee32],
8411 [0x1ee34, 0x1ee37], [0x1ee39, 0x1ee39], [0x1ee3b, 0x1ee3b], [0x1ee42, 0x1ee42],
8412 [0x1ee47, 0x1ee47], [0x1ee49, 0x1ee49], [0x1ee4b, 0x1ee4b], [0x1ee4d, 0x1ee4f],
8413 [0x1ee51, 0x1ee52], [0x1ee54, 0x1ee54], [0x1ee57, 0x1ee57], [0x1ee59, 0x1ee59],
8414 [0x1ee5b, 0x1ee5b], [0x1ee5d, 0x1ee5d], [0x1ee5f, 0x1ee5f], [0x1ee61, 0x1ee62],
8415 [0x1ee64, 0x1ee64], [0x1ee67, 0x1ee6a], [0x1ee6c, 0x1ee72], [0x1ee74, 0x1ee77],
8416 [0x1ee79, 0x1ee7c], [0x1ee7e, 0x1ee7e], [0x1ee80, 0x1ee89], [0x1ee8b, 0x1ee9b],
8417 [0x1eea1, 0x1eea3], [0x1eea5, 0x1eea9], [0x1eeab, 0x1eebb], [0x10fffd, 0x10fffd]];
8418
8419function isStrongRTLChar(charCode) {
8420  for (var i = 0; i < strongRTLRanges.length; i++) {
8421    var currentRange = strongRTLRanges[i];
8422    if (charCode >= currentRange[0] && charCode <= currentRange[1]) {
8423      return true;
8424    }
8425  }
8426
8427  return false;
8428}
8429
8430function determineBidi(cueDiv) {
8431  var nodeStack = [],
8432      text = "",
8433      charCode;
8434
8435  if (!cueDiv || !cueDiv.childNodes) {
8436    return "ltr";
8437  }
8438
8439  function pushNodes(nodeStack, node) {
8440    for (var i = node.childNodes.length - 1; i >= 0; i--) {
8441      nodeStack.push(node.childNodes[i]);
8442    }
8443  }
8444
8445  function nextTextNode(nodeStack) {
8446    if (!nodeStack || !nodeStack.length) {
8447      return null;
8448    }
8449
8450    var node = nodeStack.pop(),
8451        text = node.textContent || node.innerText;
8452    if (text) {
8453      // TODO: This should match all unicode type B characters (paragraph
8454      // separator characters). See issue #115.
8455      var m = text.match(/^.*(\n|\r)/);
8456      if (m) {
8457        nodeStack.length = 0;
8458        return m[0];
8459      }
8460      return text;
8461    }
8462    if (node.tagName === "ruby") {
8463      return nextTextNode(nodeStack);
8464    }
8465    if (node.childNodes) {
8466      pushNodes(nodeStack, node);
8467      return nextTextNode(nodeStack);
8468    }
8469  }
8470
8471  pushNodes(nodeStack, cueDiv);
8472  while ((text = nextTextNode(nodeStack))) {
8473    for (var i = 0; i < text.length; i++) {
8474      charCode = text.charCodeAt(i);
8475      if (isStrongRTLChar(charCode)) {
8476        return "rtl";
8477      }
8478    }
8479  }
8480  return "ltr";
8481}
8482
8483function computeLinePos(cue) {
8484  if (typeof cue.line === "number" &&
8485      (cue.snapToLines || (cue.line >= 0 && cue.line <= 100))) {
8486    return cue.line;
8487  }
8488  if (!cue.track || !cue.track.textTrackList ||
8489      !cue.track.textTrackList.mediaElement) {
8490    return -1;
8491  }
8492  var track = cue.track,
8493      trackList = track.textTrackList,
8494      count = 0;
8495  for (var i = 0; i < trackList.length && trackList[i] !== track; i++) {
8496    if (trackList[i].mode === "showing") {
8497      count++;
8498    }
8499  }
8500  return ++count * -1;
8501}
8502
8503function StyleBox() {
8504}
8505
8506// Apply styles to a div. If there is no div passed then it defaults to the
8507// div on 'this'.
8508StyleBox.prototype.applyStyles = function(styles, div) {
8509  div = div || this.div;
8510  for (var prop in styles) {
8511    if (styles.hasOwnProperty(prop)) {
8512      div.style[prop] = styles[prop];
8513    }
8514  }
8515};
8516
8517StyleBox.prototype.formatStyle = function(val, unit) {
8518  return val === 0 ? 0 : val + unit;
8519};
8520
8521// Constructs the computed display state of the cue (a div). Places the div
8522// into the overlay which should be a block level element (usually a div).
8523function CueStyleBox(window, cue, styleOptions) {
8524  var isIE8 = (/MSIE\s8\.0/).test(navigator.userAgent);
8525  var color = "rgba(255, 255, 255, 1)";
8526  var backgroundColor = "rgba(0, 0, 0, 0.8)";
8527
8528  if (isIE8) {
8529    color = "rgb(255, 255, 255)";
8530    backgroundColor = "rgb(0, 0, 0)";
8531  }
8532
vendor: 5,880 bytes, lines 8533-8709
8533  StyleBox.call(this);
8534  this.cue = cue;
8535
8536  // Parse our cue's text into a DOM tree rooted at 'cueDiv'. This div will
8537  // have inline positioning and will function as the cue background box.
8538  this.cueDiv = parseContent(window, cue.text);
8539  var styles = {
8540    color: color,
8541    backgroundColor: backgroundColor,
8542    position: "relative",
8543    left: 0,
8544    right: 0,
8545    top: 0,
8546    bottom: 0,
8547    display: "inline"
8548  };
8549
8550  if (!isIE8) {
8551    styles.writingMode = cue.vertical === "" ? "horizontal-tb"
8552                                             : cue.vertical === "lr" ? "vertical-lr"
8553                                                                     : "vertical-rl";
8554    styles.unicodeBidi = "plaintext";
8555  }
8556  this.applyStyles(styles, this.cueDiv);
8557
8558  // Create an absolutely positioned div that will be used to position the cue
8559  // div. Note, all WebVTT cue-setting alignments are equivalent to the CSS
8560  // mirrors of them except "middle" which is "center" in CSS.
8561  this.div = window.document.createElement("div");
8562  styles = {
8563    textAlign: cue.align === "middle" ? "center" : cue.align,
8564    font: styleOptions.font,
8565    whiteSpace: "pre-line",
8566    position: "absolute"
8567  };
8568
8569  if (!isIE8) {
8570    styles.direction = determineBidi(this.cueDiv);
8571    styles.writingMode = cue.vertical === "" ? "horizontal-tb"
8572                                             : cue.vertical === "lr" ? "vertical-lr"
8573                                                                     : "vertical-rl".
8574    stylesunicodeBidi =  "plaintext";
8575  }
8576
8577  this.applyStyles(styles);
8578
8579  this.div.appendChild(this.cueDiv);
8580
8581  // Calculate the distance from the reference edge of the viewport to the text
8582  // position of the cue box. The reference edge will be resolved later when
8583  // the box orientation styles are applied.
8584  var textPos = 0;
8585  switch (cue.positionAlign) {
8586  case "start":
8587    textPos = cue.position;
8588    break;
8589  case "middle":
8590    textPos = cue.position - (cue.size / 2);
8591    break;
8592  case "end":
8593    textPos = cue.position - cue.size;
8594    break;
8595  }
8596
8597  // Horizontal box orientation; textPos is the distance from the left edge of the
8598  // area to the left edge of the box and cue.size is the distance extending to
8599  // the right from there.
8600  if (cue.vertical === "") {
8601    this.applyStyles({
8602      left:  this.formatStyle(textPos, "%"),
8603      width: this.formatStyle(cue.size, "%")
8604    });
8605  // Vertical box orientation; textPos is the distance from the top edge of the
8606  // area to the top edge of the box and cue.size is the height extending
8607  // downwards from there.
8608  } else {
8609    this.applyStyles({
8610      top: this.formatStyle(textPos, "%"),
8611      height: this.formatStyle(cue.size, "%")
8612    });
8613  }
8614
8615  this.move = function(box) {
8616    this.applyStyles({
8617      top: this.formatStyle(box.top, "px"),
8618      bottom: this.formatStyle(box.bottom, "px"),
8619      left: this.formatStyle(box.left, "px"),
8620      right: this.formatStyle(box.right, "px"),
8621      height: this.formatStyle(box.height, "px"),
8622      width: this.formatStyle(box.width, "px")
8623    });
8624  };
8625}
8626CueStyleBox.prototype = _objCreate(StyleBox.prototype);
8627CueStyleBox.prototype.constructor = CueStyleBox;
8628
8629// Represents the co-ordinates of an Element in a way that we can easily
8630// compute things with such as if it overlaps or intersects with another Element.
8631// Can initialize it with either a StyleBox or another BoxPosition.
8632function BoxPosition(obj) {
8633  var isIE8 = (/MSIE\s8\.0/).test(navigator.userAgent);
8634
8635  // Either a BoxPosition was passed in and we need to copy it, or a StyleBox
8636  // was passed in and we need to copy the results of 'getBoundingClientRect'
8637  // as the object returned is readonly. All co-ordinate values are in reference
8638  // to the viewport origin (top left).
8639  var lh, height, width, top;
8640  if (obj.div) {
8641    height = obj.div.offsetHeight;
8642    width = obj.div.offsetWidth;
8643    top = obj.div.offsetTop;
8644
8645    var rects = (rects = obj.div.childNodes) && (rects = rects[0]) &&
8646                rects.getClientRects && rects.getClientRects();
8647    obj = obj.div.getBoundingClientRect();
8648    // In certain cases the outter div will be slightly larger then the sum of
8649    // the inner div's lines. This could be due to bold text, etc, on some platforms.
8650    // In this case we should get the average line height and use that. This will
8651    // result in the desired behaviour.
8652    lh = rects ? Math.max((rects[0] && rects[0].height) || 0, obj.height / rects.length)
8653               : 0;
8654
8655  }
8656  this.left = obj.left;
8657  this.right = obj.right;
8658  this.top = obj.top || top;
8659  this.height = obj.height || height;
8660  this.bottom = obj.bottom || (top + (obj.height || height));
8661  this.width = obj.width || width;
8662  this.lineHeight = lh !== undefined ? lh : obj.lineHeight;
8663
8664  if (isIE8 && !this.lineHeight) {
8665    this.lineHeight = 13;
8666  }
8667}
8668
8669// Move the box along a particular axis. Optionally pass in an amount to move
8670// the box. If no amount is passed then the default is the line height of the
8671// box.
8672BoxPosition.prototype.move = function(axis, toMove) {
8673  toMove = toMove !== undefined ? toMove : this.lineHeight;
8674  switch (axis) {
8675  case "+x":
8676    this.left += toMove;
8677    this.right += toMove;
8678    break;
8679  case "-x":
8680    this.left -= toMove;
8681    this.right -= toMove;
8682    break;
8683  case "+y":
8684    this.top += toMove;
8685    this.bottom += toMove;
8686    break;
8687  case "-y":
8688    this.top -= toMove;
8689    this.bottom -= toMove;
8690    break;
8691  }
8692};
8693
8694// Check if this box overlaps another box, b2.
8695BoxPosition.prototype.overlaps = function(b2) {
8696  return this.left < b2.right &&
8697         this.right > b2.left &&
8698         this.top < b2.bottom &&
8699         this.bottom > b2.top;
8700};
8701
8702// Check if this box overlaps any other boxes in boxes.
8703BoxPosition.prototype.overlapsAny = function(boxes) {
8704  for (var i = 0; i < boxes.length; i++) {
8705    if (this.overlaps(boxes[i])) {
8706      return true;
8707    }
8708  }
8709  return false;
vendor: 6,016 bytes, lines 8710-8873
8710};
8711
8712// Check if this box is within another box.
8713BoxPosition.prototype.within = function(container) {
8714  return this.top >= container.top &&
8715         this.bottom <= container.bottom &&
8716         this.left >= container.left &&
8717         this.right <= container.right;
8718};
8719
8720// Check if this box is entirely within the container or it is overlapping
8721// on the edge opposite of the axis direction passed. For example, if "+x" is
8722// passed and the box is overlapping on the left edge of the container, then
8723// return true.
8724BoxPosition.prototype.overlapsOppositeAxis = function(container, axis) {
8725  switch (axis) {
8726  case "+x":
8727    return this.left < container.left;
8728  case "-x":
8729    return this.right > container.right;
8730  case "+y":
8731    return this.top < container.top;
8732  case "-y":
8733    return this.bottom > container.bottom;
8734  }
8735};
8736
8737// Find the percentage of the area that this box is overlapping with another
8738// box.
8739BoxPosition.prototype.intersectPercentage = function(b2) {
8740  var x = Math.max(0, Math.min(this.right, b2.right) - Math.max(this.left, b2.left)),
8741      y = Math.max(0, Math.min(this.bottom, b2.bottom) - Math.max(this.top, b2.top)),
8742      intersectArea = x * y;
8743  return intersectArea / (this.height * this.width);
8744};
8745
8746// Convert the positions from this box to CSS compatible positions using
8747// the reference container's positions. This has to be done because this
8748// box's positions are in reference to the viewport origin, whereas, CSS
8749// values are in referecne to their respective edges.
8750BoxPosition.prototype.toCSSCompatValues = function(reference) {
8751  return {
8752    top: this.top - reference.top,
8753    bottom: reference.bottom - this.bottom,
8754    left: this.left - reference.left,
8755    right: reference.right - this.right,
8756    height: this.height,
8757    width: this.width
8758  };
8759};
8760
8761// Get an object that represents the box's position without anything extra.
8762// Can pass a StyleBox, HTMLElement, or another BoxPositon.
8763BoxPosition.getSimpleBoxPosition = function(obj) {
8764  var height = obj.div ? obj.div.offsetHeight : obj.tagName ? obj.offsetHeight : 0;
8765  var width = obj.div ? obj.div.offsetWidth : obj.tagName ? obj.offsetWidth : 0;
8766  var top = obj.div ? obj.div.offsetTop : obj.tagName ? obj.offsetTop : 0;
8767
8768  obj = obj.div ? obj.div.getBoundingClientRect() :
8769                obj.tagName ? obj.getBoundingClientRect() : obj;
8770  var ret = {
8771    left: obj.left,
8772    right: obj.right,
8773    top: obj.top || top,
8774    height: obj.height || height,
8775    bottom: obj.bottom || (top + (obj.height || height)),
8776    width: obj.width || width
8777  };
8778  return ret;
8779};
8780
8781// Move a StyleBox to its specified, or next best, position. The containerBox
8782// is the box that contains the StyleBox, such as a div. boxPositions are
8783// a list of other boxes that the styleBox can't overlap with.
8784function moveBoxToLinePosition(window, styleBox, containerBox, boxPositions) {
8785
8786  // Find the best position for a cue box, b, on the video. The axis parameter
8787  // is a list of axis, the order of which, it will move the box along. For example:
8788  // Passing ["+x", "-x"] will move the box first along the x axis in the positive
8789  // direction. If it doesn't find a good position for it there it will then move
8790  // it along the x axis in the negative direction.
8791  function findBestPosition(b, axis) {
8792    var bestPosition,
8793        specifiedPosition = new BoxPosition(b),
8794        percentage = 1; // Highest possible so the first thing we get is better.
8795
8796    for (var i = 0; i < axis.length; i++) {
8797      while (b.overlapsOppositeAxis(containerBox, axis[i]) ||
8798             (b.within(containerBox) && b.overlapsAny(boxPositions))) {
8799        b.move(axis[i]);
8800      }
8801      // We found a spot where we aren't overlapping anything. This is our
8802      // best position.
8803      if (b.within(containerBox)) {
8804        return b;
8805      }
8806      var p = b.intersectPercentage(containerBox);
8807      // If we're outside the container box less then we were on our last try
8808      // then remember this position as the best position.
8809      if (percentage > p) {
8810        bestPosition = new BoxPosition(b);
8811        percentage = p;
8812      }
8813      // Reset the box position to the specified position.
8814      b = new BoxPosition(specifiedPosition);
8815    }
8816    return bestPosition || specifiedPosition;
8817  }
8818
8819  var boxPosition = new BoxPosition(styleBox),
8820      cue = styleBox.cue,
8821      linePos = computeLinePos(cue),
8822      axis = [];
8823
8824  // If we have a line number to align the cue to.
8825  if (cue.snapToLines) {
8826    var size;
8827    switch (cue.vertical) {
8828    case "":
8829      axis = [ "+y", "-y" ];
8830      size = "height";
8831      break;
8832    case "rl":
8833      axis = [ "+x", "-x" ];
8834      size = "width";
8835      break;
8836    case "lr":
8837      axis = [ "-x", "+x" ];
8838      size = "width";
8839      break;
8840    }
8841
8842    var step = boxPosition.lineHeight,
8843        position = step * Math.round(linePos),
8844        maxPosition = containerBox[size] + step,
8845        initialAxis = axis[0];
8846
8847    // If the specified intial position is greater then the max position then
8848    // clamp the box to the amount of steps it would take for the box to
8849    // reach the max position.
8850    if (Math.abs(position) > maxPosition) {
8851      position = position < 0 ? -1 : 1;
8852      position *= Math.ceil(maxPosition / step) * step;
8853    }
8854
8855    // If computed line position returns negative then line numbers are
8856    // relative to the bottom of the video instead of the top. Therefore, we
8857    // need to increase our initial position by the length or width of the
8858    // video, depending on the writing direction, and reverse our axis directions.
8859    if (linePos < 0) {
8860      position += cue.vertical === "" ? containerBox.height : containerBox.width;
8861      axis = axis.reverse();
8862    }
8863
8864    // Move the box to the specified position. This may not be its best
8865    // position.
8866    boxPosition.move(initialAxis, position);
8867
8868  } else {
8869    // If we have a percentage line value for the cue.
8870    var calculatedPercentage = (boxPosition.lineHeight / containerBox.height) * 100;
8871
8872    switch (cue.lineAlign) {
8873    case "middle
vendor: 10,353 bytes, lines 8873-9234
8873":
8874      linePos -= (calculatedPercentage / 2);
8875      break;
8876    case "end":
8877      linePos -= calculatedPercentage;
8878      break;
8879    }
8880
8881    // Apply initial line position to the cue box.
8882    switch (cue.vertical) {
8883    case "":
8884      styleBox.applyStyles({
8885        top: styleBox.formatStyle(linePos, "%")
8886      });
8887      break;
8888    case "rl":
8889      styleBox.applyStyles({
8890        left: styleBox.formatStyle(linePos, "%")
8891      });
8892      break;
8893    case "lr":
8894      styleBox.applyStyles({
8895        right: styleBox.formatStyle(linePos, "%")
8896      });
8897      break;
8898    }
8899
8900    axis = [ "+y", "-x", "+x", "-y" ];
8901
8902    // Get the box position again after we've applied the specified positioning
8903    // to it.
8904    boxPosition = new BoxPosition(styleBox);
8905  }
8906
8907  var bestPosition = findBestPosition(boxPosition, axis);
8908  styleBox.move(bestPosition.toCSSCompatValues(containerBox));
8909}
8910
8911function WebVTT$1() {
8912  // Nothing
8913}
8914
8915// Helper to allow strings to be decoded instead of the default binary utf8 data.
8916WebVTT$1.StringDecoder = function() {
8917  return {
8918    decode: function(data) {
8919      if (!data) {
8920        return "";
8921      }
8922      if (typeof data !== "string") {
8923        throw new Error("Error - expected string data.");
8924      }
8925      return decodeURIComponent(encodeURIComponent(data));
8926    }
8927  };
8928};
8929
8930WebVTT$1.convertCueToDOMTree = function(window, cuetext) {
8931  if (!window || !cuetext) {
8932    return null;
8933  }
8934  return parseContent(window, cuetext);
8935};
8936
8937var FONT_SIZE_PERCENT = 0.05;
8938var FONT_STYLE = "sans-serif";
8939var CUE_BACKGROUND_PADDING = "1.5%";
8940
8941// Runs the processing model over the cues and regions passed to it.
8942// @param overlay A block level element (usually a div) that the computed cues
8943//                and regions will be placed into.
8944WebVTT$1.processCues = function(window, cues, overlay) {
8945  if (!window || !cues || !overlay) {
8946    return null;
8947  }
8948
8949  // Remove all previous children.
8950  while (overlay.firstChild) {
8951    overlay.removeChild(overlay.firstChild);
8952  }
8953
8954  var paddedOverlay = window.document.createElement("div");
8955  paddedOverlay.style.position = "absolute";
8956  paddedOverlay.style.left = "0";
8957  paddedOverlay.style.right = "0";
8958  paddedOverlay.style.top = "0";
8959  paddedOverlay.style.bottom = "0";
8960  paddedOverlay.style.margin = CUE_BACKGROUND_PADDING;
8961  overlay.appendChild(paddedOverlay);
8962
8963  // Determine if we need to compute the display states of the cues. This could
8964  // be the case if a cue's state has been changed since the last computation or
8965  // if it has not been computed yet.
8966  function shouldCompute(cues) {
8967    for (var i = 0; i < cues.length; i++) {
8968      if (cues[i].hasBeenReset || !cues[i].displayState) {
8969        return true;
8970      }
8971    }
8972    return false;
8973  }
8974
8975  // We don't need to recompute the cues' display states. Just reuse them.
8976  if (!shouldCompute(cues)) {
8977    for (var i = 0; i < cues.length; i++) {
8978      paddedOverlay.appendChild(cues[i].displayState);
8979    }
8980    return;
8981  }
8982
8983  var boxPositions = [],
8984      containerBox = BoxPosition.getSimpleBoxPosition(paddedOverlay),
8985      fontSize = Math.round(containerBox.height * FONT_SIZE_PERCENT * 100) / 100;
8986  var styleOptions = {
8987    font: fontSize + "px " + FONT_STYLE
8988  };
8989
8990  (function() {
8991    var styleBox, cue;
8992
8993    for (var i = 0; i < cues.length; i++) {
8994      cue = cues[i];
8995
8996      // Compute the intial position and styles of the cue div.
8997      styleBox = new CueStyleBox(window, cue, styleOptions);
8998      paddedOverlay.appendChild(styleBox.div);
8999
9000      // Move the cue div to it's correct line position.
9001      moveBoxToLinePosition(window, styleBox, containerBox, boxPositions);
9002
9003      // Remember the computed div so that we don't have to recompute it later
9004      // if we don't have too.
9005      cue.displayState = styleBox.div;
9006
9007      boxPositions.push(BoxPosition.getSimpleBoxPosition(styleBox));
9008    }
9009  })();
9010};
9011
9012WebVTT$1.Parser = function(window, vttjs, decoder) {
9013  if (!decoder) {
9014    decoder = vttjs;
9015    vttjs = {};
9016  }
9017  if (!vttjs) {
9018    vttjs = {};
9019  }
9020
9021  this.window = window;
9022  this.vttjs = vttjs;
9023  this.state = "INITIAL";
9024  this.buffer = "";
9025  this.decoder = decoder || new TextDecoder("utf8");
9026  this.regionList = [];
9027};
9028
9029WebVTT$1.Parser.prototype = {
9030  // If the error is a ParsingError then report it to the consumer if
9031  // possible. If it's not a ParsingError then throw it like normal.
9032  reportOrThrowError: function(e) {
9033    if (e instanceof ParsingError) {
9034      this.onparsingerror && this.onparsingerror(e);
9035    } else {
9036      throw e;
9037    }
9038  },
9039  parse: function (data) {
9040    var self = this;
9041
9042    // If there is no data then we won't decode it, but will just try to parse
9043    // whatever is in buffer already. This may occur in circumstances, for
9044    // example when flush() is called.
9045    if (data) {
9046      // Try to decode the data that we received.
9047      self.buffer += self.decoder.decode(data, {stream: true});
9048    }
9049
9050    function collectNextLine() {
9051      var buffer = self.buffer;
9052      var pos = 0;
9053      while (pos < buffer.length && buffer[pos] !== '\r' && buffer[pos] !== '\n') {
9054        ++pos;
9055      }
9056      var line = buffer.substr(0, pos);
9057      // Advance the buffer early in case we fail below.
9058      if (buffer[pos] === '\r') {
9059        ++pos;
9060      }
9061      if (buffer[pos] === '\n') {
9062        ++pos;
9063      }
9064      self.buffer = buffer.substr(pos);
9065      return line;
9066    }
9067
9068    // 3.4 WebVTT region and WebVTT region settings syntax
9069    function parseRegion(input) {
9070      var settings = new Settings();
9071
9072      parseOptions(input, function (k, v) {
9073        switch (k) {
9074        case "id":
9075          settings.set(k, v);
9076          break;
9077        case "width":
9078          settings.percent(k, v);
9079          break;
9080        case "lines":
9081          settings.integer(k, v);
9082          break;
9083        case "regionanchor":
9084        case "viewportanchor":
9085          var xy = v.split(',');
9086          if (xy.length !== 2) {
9087            break;
9088          }
9089          // We have to make sure both x and y parse, so use a temporary
9090          // settings object here.
9091          var anchor = new Settings();
9092          anchor.percent("x", xy[0]);
9093          anchor.percent("y", xy[1]);
9094          if (!anchor.has("x") || !anchor.has("y")) {
9095            break;
9096          }
9097          settings.set(k + "X", anchor.get("x"));
9098          settings.set(k + "Y", anchor.get("y"));
9099          break;
9100        case "scroll":
9101          settings.alt(k, v, ["up"]);
9102          break;
9103        }
9104      }, /=/, /\s/);
9105
9106      // Create the region, using default values for any values that were not
9107      // specified.
9108      if (settings.has("id")) {
9109        var region = new (self.vttjs.VTTRegion || self.window.VTTRegion)();
9110        region.width = settings.get("width", 100);
9111        region.lines = settings.get("lines", 3);
9112        region.regionAnchorX = settings.get("regionanchorX", 0);
9113        region.regionAnchorY = settings.get("regionanchorY", 100);
9114        region.viewportAnchorX = settings.get("viewportanchorX", 0);
9115        region.viewportAnchorY = settings.get("viewportanchorY", 100);
9116        region.scroll = settings.get("scroll", "");
9117        // Register the region.
9118        self.onregion && self.onregion(region);
9119        // Remember the VTTRegion for later in case we parse any VTTCues that
9120        // reference it.
9121        self.regionList.push({
9122          id: settings.get("id"),
9123          region: region
9124        });
9125      }
9126    }
9127
9128    // draft-pantos-http-live-streaming-20
9129    // https://tools.ietf.org/html/draft-pantos-http-live-streaming-20#section-3.5
9130    // 3.5 WebVTT
9131    function parseTimestampMap(input) {
9132      var settings = new Settings();
9133
9134      parseOptions(input, function(k, v) {
9135        switch(k) {
9136        case "MPEGT":
9137          settings.integer(k + 'S', v);
9138          break;
9139        case "LOCA":
9140          settings.set(k + 'L', parseTimeStamp(v));
9141          break;
9142        }
9143      }, /[^\d]:/, /,/);
9144
9145      self.ontimestampmap && self.ontimestampmap({
9146        "MPEGTS": settings.get("MPEGTS"),
9147        "LOCAL": settings.get("LOCAL")
9148      });
9149    }
9150
9151    // 3.2 WebVTT metadata header syntax
9152    function parseHeader(input) {
9153      if (input.match(/X-TIMESTAMP-MAP/)) {
9154        // This line contains HLS X-TIMESTAMP-MAP metadata
9155        parseOptions(input, function(k, v) {
9156          switch(k) {
9157          case "X-TIMESTAMP-MAP":
9158            parseTimestampMap(v);
9159            break;
9160          }
9161        }, /=/);
9162      } else {
9163        parseOptions(input, function (k, v) {
9164          switch (k) {
9165          case "Region":
9166            // 3.3 WebVTT region metadata header syntax
9167            parseRegion(v);
9168            break;
9169          }
9170        }, /:/);
9171      }
9172
9173    }
9174
9175    // 5.1 WebVTT file parsing.
9176    try {
9177      var line;
9178      if (self.state === "INITIAL") {
9179        // We can't start parsing until we have the first line.
9180        if (!/\r\n|\n/.test(self.buffer)) {
9181          return this;
9182        }
9183
9184        line = collectNextLine();
9185
9186        var m = line.match(/^WEBVTT([ \t].*)?$/);
9187        if (!m || !m[0]) {
9188          throw new ParsingError(ParsingError.Errors.BadSignature);
9189        }
9190
9191        self.state = "HEADER";
9192      }
9193
9194      var alreadyCollectedLine = false;
9195      while (self.buffer) {
9196        // We can't parse a line until we have the full line.
9197        if (!/\r\n|\n/.test(self.buffer)) {
9198          return this;
9199        }
9200
9201        if (!alreadyCollectedLine) {
9202          line = collectNextLine();
9203        } else {
9204          alreadyCollectedLine = false;
9205        }
9206
9207        switch (self.state) {
9208        case "HEADER":
9209          // 13-18 - Allow a header (metadata) under the WEBVTT line.
9210          if (/:/.test(line)) {
9211            parseHeader(line);
9212          } else if (!line) {
9213            // An empty line terminates the header and starts the body (cues).
9214            self.state = "ID";
9215          }
9216          continue;
9217        case "NOTE":
9218          // Ignore NOTE blocks.
9219          if (!line) {
9220            self.state = "ID";
9221          }
9222          continue;
9223        case "ID":
9224          // Check for the start of NOTE blocks.
9225          if (/^NOTE($|[ \t])/.test(line)) {
9226            self.state = "NOTE";
9227            break;
9228          }
9229          // 19-29 - Allow any number of line terminators, then initialize new cue values.
9230          if (!line) {
9231            continue;
9232          }
9233          self.cue = new (self.vttjs.VTTCue || self.window.VTTCue)(0, 0, "");
9234          
vendor: 2,935 bytes, lines 9234-9324
9234self.state = "CUE";
9235          // 30-39 - Check if self line contains an optional identifier or timing data.
9236          if (line.indexOf("-->") === -1) {
9237            self.cue.id = line;
9238            continue;
9239          }
9240          // Process line as start of a cue.
9241          /*falls through*/
9242        case "CUE":
9243          // 40 - Collect cue timings and settings.
9244          try {
9245            parseCue(line, self.cue, self.regionList);
9246          } catch (e) {
9247            self.reportOrThrowError(e);
9248            // In case of an error ignore rest of the cue.
9249            self.cue = null;
9250            self.state = "BADCUE";
9251            continue;
9252          }
9253          self.state = "CUETEXT";
9254          continue;
9255        case "CUETEXT":
9256          var hasSubstring = line.indexOf("-->") !== -1;
9257          // 34 - If we have an empty line then report the cue.
9258          // 35 - If we have the special substring '-->' then report the cue,
9259          // but do not collect the line as we need to process the current
9260          // one as a new cue.
9261          if (!line || hasSubstring && (alreadyCollectedLine = true)) {
9262            // We are done parsing self cue.
9263            self.oncue && self.oncue(self.cue);
9264            self.cue = null;
9265            self.state = "ID";
9266            continue;
9267          }
9268          if (self.cue.text) {
9269            self.cue.text += "\n";
9270          }
9271          self.cue.text += line;
9272          continue;
9273        case "BADCUE": // BADCUE
9274          // 54-62 - Collect and discard the remaining cue.
9275          if (!line) {
9276            self.state = "ID";
9277          }
9278          continue;
9279        }
9280      }
9281    } catch (e) {
9282      self.reportOrThrowError(e);
9283
9284      // If we are currently parsing a cue, report what we have.
9285      if (self.state === "CUETEXT" && self.cue && self.oncue) {
9286        self.oncue(self.cue);
9287      }
9288      self.cue = null;
9289      // Enter BADWEBVTT state if header was not parsed correctly otherwise
9290      // another exception occurred so enter BADCUE state.
9291      self.state = self.state === "INITIAL" ? "BADWEBVTT" : "BADCUE";
9292    }
9293    return this;
9294  },
9295  flush: function () {
9296    var self = this;
9297    try {
9298      // Finish decoding the stream.
9299      self.buffer += self.decoder.decode();
9300      // Synthesize the end of the current cue or region.
9301      if (self.cue || self.state === "HEADER") {
9302        self.buffer += "\n\n";
9303        self.parse();
9304      }
9305      // If we've flushed, parsed, and we're still on the INITIAL state then
9306      // that means we don't have enough of the stream to parse the first
9307      // line.
9308      if (self.state === "INITIAL") {
9309        throw new ParsingError(ParsingError.Errors.BadSignature);
9310      }
9311    } catch(e) {
9312      self.reportOrThrowError(e);
9313    }
9314    self.onflush && self.onflush();
9315    return this;
9316  }
9317};
9318
9319var vtt$1 = WebVTT$1;
9320
9321/**
9322 * Copyright 2013 vtt.js Contributors
9323 *
9324 * Licensed under the Apache License, Version 2.0 (the "License");
9325 * you may not use this file except in compliance with the License.
9326 * You may obtain a copy of the License at
9327 *
9328 *   http://www.apache.org/licenses/LICENSE-2.0
9329 *
9330 * Unless required by applicable law or agreed to in writing, software
9331 * distributed under the License is distributed on an "AS IS" BASIS,
9332 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
9333 * See the License for the specific language governing permissions and
9334 * limitations under the License.
9335 */
9336
9337var autoKeyword = "auto";
9338var directionSetting = {
9339  "": true,
9340  "lr": true,
9341  "rl": true
9342};
9343var alignSetting = {
9344  "start": true,
9345  "middle": true,
9346  "end": true,
9347  "left": true,
9348  "right": true
9349};
9350
9351function findDirectionSetting(value) {
9352  if (typeof value !== "string") {
9353    return false;
9354  }
9355  var dir = directionSetting[value.toLowerCase()];
9356  return dir ? value.toLowerCase() : false;
9357}
9358
9359function findAlignSetting(value) {
9360  if (typeof value !== "string") {
9361    return false;
9362  }
9363  var align = alignSetting[value.toLowerCase()];
9364  return align ? value.toLowerCase() : false;
9365}
9366
9367function extend$1(obj) {
9368  var i = 1;
9369  for (; i < arguments.length; i++) {
9370    var cobj = arguments[i];
9371    for (var p in cobj) {
9372      obj[p] = cobj[p];
9373    }
9374  }
9375
9376  return obj;
9377}
9378
9379function VTTCue(startTime, endTime, text) {
9380  var cue = this;
9381  var isIE8 = (/MSIE\s8\.0/).test(navigator.userAgent);
9382  var baseObj = {};
9383
9384  if (isIE8) {
9385    cue = document.createElement('custom');
9386  } else {
9387    baseObj.enumerable = true;
9388  }
9389
9390  /**
9391   * Shim implementation specific properties. These properties are not in
9392   * the spec.
9393   */
9394
9395  // Lets us know when the VTTCue's data has changed in such a way that we need
9396  // to recompute its display state. This lets us compute its display state
9397  // lazily.
9398  cue.hasBeenReset = false;
9399
9400  /**
9401   * VTTCue and TextTrackCue properties
9402   * http://dev.w3.org/html5/webvtt/#vttcue-interface
9403   */
9404
9405  var _id = "";
9406  var _pauseOnExit = false;
9407  var _startTime = startTime;
9408  var _endTime = endTime;
9409  var _text = text;
9410  var _region = null;
9411  var _vertical = "";
9412  var _snapToLines = true;
9413  var _line = "auto";
9414  var _lineAlign = "start";
9415  var _position = 50;
9416  var _positionAlign = "middle";
9417  var _size = 50;
9418  var _align = "middle";
9419
9420  Object.defineProperty(cue,
9421    "id", extend$1({}, baseObj, {
9422      get: function() {
9423        return _id;
9424      },
9425      set: function(value) {
9426        _id = "" + value;
9427      }
9428    }));
9429
9430  Object.defineProperty(cue,
9431    "pauseOnExit", extend$1({}, baseObj, {
9432      get: function() {
9433        return _pauseOnExit;
9434      },
9435      set: function(value) {
9436        _pauseOnExit = !!value;
9437      }
9438    }));
9439
9440  Object.defineProperty(cue,
9441    "startTime", extend$1({}, baseObj, {
9442      get: function() {
9443        return _startTime;
9444      },
9445      set: function(value) {
9446        if (typeof value !== "number") {
9447          throw new TypeError("Start time must be set to a number.");
9448        }
9449        _startTime = value;
9450        this.hasBeenReset = true;
9451      }
9452    }));
9453
9454  Object.defineProperty(cue,
9455    "endTime", extend$1({}, baseObj, {
9456      get: function() {
9457        return _endTime;
9458      },
9459      set: function(value) {
9460        if (typeof value !== "number") {
9461          throw new TypeError("End time must be set to a number.");
9462        }
9463        _endTime = value;
9464        this.hasBeenReset = true;
9465      }
9466    }));
9467
9468  Object.defineProperty(cue,
9469    "text", extend$1({}, baseObj, {
9470      get: function() {
9471        return _text;
9472      },
9473      set: function(value) {
9474        _text = "" + value;
9475        this.hasBeenReset = true;
9476      }
9477    }));
9478
9479  Object.defineProperty(cue,
9480    "region", extend$1({}, baseObj, {
9481      get: function() {
9482        return _region;
9483      },
9484      set: function(value) {
9485        _region = value;
9486        this.hasBeenReset = true;
9487      }
9488    }));
9489
9490  Object.defineProperty(cue,
9491    "vertical", extend$1({}, baseObj, {
9492      get: function() {
9493        return _vertical;
9494      },
9495      set: function(value) {
9496        var setting = findDirectionSetting(value);
9497        // Have to check for false because the setting an be an empty string.
9498        if (setting === false) {
9499          throw new SyntaxError("An invalid or illegal string was specified.");
9500        }
9501        _vertical = setting;
9502        this.hasBeenReset = true;
9503      }
9504    }));
9505
9506  Object.defineProperty(cue,
9507    "snapToLines", extend$1({}, baseObj, {
9508      get: function() {
9509        return _snapToLines;
9510      },
9511      set: function(value) {
9512        _snapToLines = !!value;
9513        this.hasBeenReset = true;
9514      }
9515    }));
9516
9517  Object.defineProperty(cue,
9518    "line", extend$1({}, baseObj, {
9519      get: function() {
9520        return _line;
9521      },
9522      set: function(value) {
vendor: 4,245 bytes, lines 9523-9688
9523        if (typeof value !== "number" && value !== autoKeyword) {
9524          throw new SyntaxError("An invalid number or illegal string was specified.");
9525        }
9526        _line = value;
9527        this.hasBeenReset = true;
9528      }
9529    }));
9530
9531  Object.defineProperty(cue,
9532    "lineAlign", extend$1({}, baseObj, {
9533      get: function() {
9534        return _lineAlign;
9535      },
9536      set: function(value) {
9537        var setting = findAlignSetting(value);
9538        if (!setting) {
9539          throw new SyntaxError("An invalid or illegal string was specified.");
9540        }
9541        _lineAlign = setting;
9542        this.hasBeenReset = true;
9543      }
9544    }));
9545
9546  Object.defineProperty(cue,
9547    "position", extend$1({}, baseObj, {
9548      get: function() {
9549        return _position;
9550      },
9551      set: function(value) {
9552        if (value < 0 || value > 100) {
9553          throw new Error("Position must be between 0 and 100.");
9554        }
9555        _position = value;
9556        this.hasBeenReset = true;
9557      }
9558    }));
9559
9560  Object.defineProperty(cue,
9561    "positionAlign", extend$1({}, baseObj, {
9562      get: function() {
9563        return _positionAlign;
9564      },
9565      set: function(value) {
9566        var setting = findAlignSetting(value);
9567        if (!setting) {
9568          throw new SyntaxError("An invalid or illegal string was specified.");
9569        }
9570        _positionAlign = setting;
9571        this.hasBeenReset = true;
9572      }
9573    }));
9574
9575  Object.defineProperty(cue,
9576    "size", extend$1({}, baseObj, {
9577      get: function() {
9578        return _size;
9579      },
9580      set: function(value) {
9581        if (value < 0 || value > 100) {
9582          throw new Error("Size must be between 0 and 100.");
9583        }
9584        _size = value;
9585        this.hasBeenReset = true;
9586      }
9587    }));
9588
9589  Object.defineProperty(cue,
9590    "align", extend$1({}, baseObj, {
9591      get: function() {
9592        return _align;
9593      },
9594      set: function(value) {
9595        var setting = findAlignSetting(value);
9596        if (!setting) {
9597          throw new SyntaxError("An invalid or illegal string was specified.");
9598        }
9599        _align = setting;
9600        this.hasBeenReset = true;
9601      }
9602    }));
9603
9604  /**
9605   * Other <track> spec defined properties
9606   */
9607
9608  // http://www.whatwg.org/specs/web-apps/current-work/multipage/the-video-element.html#text-track-cue-display-state
9609  cue.displayState = undefined;
9610
9611  if (isIE8) {
9612    return cue;
9613  }
9614}
9615
9616/**
9617 * VTTCue methods
9618 */
9619
9620VTTCue.prototype.getCueAsHTML = function() {
9621  // Assume WebVTT.convertCueToDOMTree is on the global.
9622  return WebVTT.convertCueToDOMTree(window, this.text);
9623};
9624
9625var vttcue = VTTCue;
9626
9627/**
9628 * Copyright 2013 vtt.js Contributors
9629 *
9630 * Licensed under the Apache License, Version 2.0 (the "License");
9631 * you may not use this file except in compliance with the License.
9632 * You may obtain a copy of the License at
9633 *
9634 *   http://www.apache.org/licenses/LICENSE-2.0
9635 *
9636 * Unless required by applicable law or agreed to in writing, software
9637 * distributed under the License is distributed on an "AS IS" BASIS,
9638 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
9639 * See the License for the specific language governing permissions and
9640 * limitations under the License.
9641 */
9642
9643var scrollSetting = {
9644  "": true,
9645  "up": true
9646};
9647
9648function findScrollSetting(value) {
9649  if (typeof value !== "string") {
9650    return false;
9651  }
9652  var scroll = scrollSetting[value.toLowerCase()];
9653  return scroll ? value.toLowerCase() : false;
9654}
9655
9656function isValidPercentValue(value) {
9657  return typeof value === "number" && (value >= 0 && value <= 100);
9658}
9659
9660// VTTRegion shim http://dev.w3.org/html5/webvtt/#vttregion-interface
9661function VTTRegion() {
9662  var _width = 100;
9663  var _lines = 3;
9664  var _regionAnchorX = 0;
9665  var _regionAnchorY = 100;
9666  var _viewportAnchorX = 0;
9667  var _viewportAnchorY = 100;
9668  var _scroll = "";
9669
9670  Object.defineProperties(this, {
9671    "width": {
9672      enumerable: true,
9673      get: function() {
9674        return _width;
9675      },
9676      set: function(value) {
9677        if (!isValidPercentValue(value)) {
9678          throw new Error("Width must be between 0 and 100.");
9679        }
9680        _width = value;
9681      }
9682    },
9683    "lines": {
9684      enumerable: true,
9685      get: function() {
9686        return _lines;
9687      },
9688      set: function(value) {
vendor: 81,856 bytes, lines 9689-12693
9689        if (typeof value !== "number") {
9690          throw new TypeError("Lines must be set to a number.");
9691        }
9692        _lines = value;
9693      }
9694    },
9695    "regionAnchorY": {
9696      enumerable: true,
9697      get: function() {
9698        return _regionAnchorY;
9699      },
9700      set: function(value) {
9701        if (!isValidPercentValue(value)) {
9702          throw new Error("RegionAnchorX must be between 0 and 100.");
9703        }
9704        _regionAnchorY = value;
9705      }
9706    },
9707    "regionAnchorX": {
9708      enumerable: true,
9709      get: function() {
9710        return _regionAnchorX;
9711      },
9712      set: function(value) {
9713        if(!isValidPercentValue(value)) {
9714          throw new Error("RegionAnchorY must be between 0 and 100.");
9715        }
9716        _regionAnchorX = value;
9717      }
9718    },
9719    "viewportAnchorY": {
9720      enumerable: true,
9721      get: function() {
9722        return _viewportAnchorY;
9723      },
9724      set: function(value) {
9725        if (!isValidPercentValue(value)) {
9726          throw new Error("ViewportAnchorY must be between 0 and 100.");
9727        }
9728        _viewportAnchorY = value;
9729      }
9730    },
9731    "viewportAnchorX": {
9732      enumerable: true,
9733      get: function() {
9734        return _viewportAnchorX;
9735      },
9736      set: function(value) {
9737        if (!isValidPercentValue(value)) {
9738          throw new Error("ViewportAnchorX must be between 0 and 100.");
9739        }
9740        _viewportAnchorX = value;
9741      }
9742    },
9743    "scroll": {
9744      enumerable: true,
9745      get: function() {
9746        return _scroll;
9747      },
9748      set: function(value) {
9749        var setting = findScrollSetting(value);
9750        // Have to check for false as an empty string is a legal value.
9751        if (setting === false) {
9752          throw new SyntaxError("An invalid or illegal string was specified.");
9753        }
9754        _scroll = setting;
9755      }
9756    }
9757  });
9758}
9759
9760var vttregion = VTTRegion;
9761
9762var browserIndex = createCommonjsModule(function (module) {
9763/**
9764 * Copyright 2013 vtt.js Contributors
9765 *
9766 * Licensed under the Apache License, Version 2.0 (the "License");
9767 * you may not use this file except in compliance with the License.
9768 * You may obtain a copy of the License at
9769 *
9770 *   http://www.apache.org/licenses/LICENSE-2.0
9771 *
9772 * Unless required by applicable law or agreed to in writing, software
9773 * distributed under the License is distributed on an "AS IS" BASIS,
9774 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
9775 * See the License for the specific language governing permissions and
9776 * limitations under the License.
9777 */
9778
9779// Default exports for Node. Export the extended versions of VTTCue and
9780// VTTRegion in Node since we likely want the capability to convert back and
9781// forth between JSON. If we don't then it's not that big of a deal since we're
9782// off browser.
9783
9784
9785
9786var vttjs = module.exports = {
9787  WebVTT: vtt$1,
9788  VTTCue: vttcue,
9789  VTTRegion: vttregion
9790};
9791
9792window_1.vttjs = vttjs;
9793window_1.WebVTT = vttjs.WebVTT;
9794
9795var cueShim = vttjs.VTTCue;
9796var regionShim = vttjs.VTTRegion;
9797var nativeVTTCue = window_1.VTTCue;
9798var nativeVTTRegion = window_1.VTTRegion;
9799
9800vttjs.shim = function() {
9801  window_1.VTTCue = cueShim;
9802  window_1.VTTRegion = regionShim;
9803};
9804
9805vttjs.restore = function() {
9806  window_1.VTTCue = nativeVTTCue;
9807  window_1.VTTRegion = nativeVTTRegion;
9808};
9809
9810if (!window_1.VTTCue) {
9811  vttjs.shim();
9812}
9813});
9814
9815/**
9816 * @file tech.js
9817 */
9818
9819/**
9820 * An Object containing a structure like: `{src: 'url', type: 'mimetype'}` or string
9821 * that just contains the src url alone.
9822 * * `var SourceObject = {src: 'http://ex.com/video.mp4', type: 'video/mp4'};`
9823   * `var SourceString = 'http://example.com/some-video.mp4';`
9824 *
9825 * @typedef {Object|string} Tech~SourceObject
9826 *
9827 * @property {string} src
9828 *           The url to the source
9829 *
9830 * @property {string} type
9831 *           The mime type of the source
9832 */
9833
9834/**
9835 * A function used by {@link Tech} to create a new {@link TextTrack}.
9836 *
9837 * @private
9838 *
9839 * @param {Tech} self
9840 *        An instance of the Tech class.
9841 *
9842 * @param {string} kind
9843 *        `TextTrack` kind (subtitles, captions, descriptions, chapters, or metadata)
9844 *
9845 * @param {string} [label]
9846 *        Label to identify the text track
9847 *
9848 * @param {string} [language]
9849 *        Two letter language abbreviation
9850 *
9851 * @param {Object} [options={}]
9852 *        An object with additional text track options
9853 *
9854 * @return {TextTrack}
9855 *          The text track that was created.
9856 */
9857function createTrackHelper(self, kind, label, language) {
9858  var options = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : {};
9859
9860  var tracks = self.textTracks();
9861
9862  options.kind = kind;
9863
9864  if (label) {
9865    options.label = label;
9866  }
9867  if (language) {
9868    options.language = language;
9869  }
9870  options.tech = self;
9871
9872  var track = new ALL.text.TrackClass(options);
9873
9874  tracks.addTrack(track);
9875
9876  return track;
9877}
9878
9879/**
9880 * This is the base class for media playback technology controllers, such as
9881 * {@link Flash} and {@link HTML5}
9882 *
9883 * @extends Component
9884 */
9885
9886var Tech = function (_Component) {
9887  inherits(Tech, _Component);
9888
9889  /**
9890   * Create an instance of this Tech.
9891   *
9892   * @param {Object} [options]
9893   *        The key/value store of player options.
9894   *
9895   * @param {Component~ReadyCallback} ready
9896   *        Callback function to call when the `HTML5` Tech is ready.
9897   */
9898  function Tech() {
9899    var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
9900    var ready = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : function () {};
9901    classCallCheck(this, Tech);
9902
9903    // we don't want the tech to report user activity automatically.
9904    // This is done manually in addControlsListeners
9905    options.reportTouchActivity = false;
9906
9907    // keep track of whether the current source has played at all to
9908    // implement a very limited played()
9909    var _this = possibleConstructorReturn(this, _Component.call(this, null, options, ready));
9910
9911    _this.hasStarted_ = false;
9912    _this.on('playing', function () {
9913      this.hasStarted_ = true;
9914    });
9915    _this.on('loadstart', function () {
9916      this.hasStarted_ = false;
9917    });
9918
9919    ALL.names.forEach(function (name) {
9920      var props = ALL[name];
9921
9922      if (options && options[props.getterName]) {
9923        _this[props.privateName] = options[props.getterName];
9924      }
9925    });
9926
9927    // Manually track progress in cases where the browser/flash player doesn't report it.
9928    if (!_this.featuresProgressEvents) {
9929      _this.manualProgressOn();
9930    }
9931
9932    // Manually track timeupdates in cases where the browser/flash player doesn't report it.
9933    if (!_this.featuresTimeupdateEvents) {
9934      _this.manualTimeUpdatesOn();
9935    }
9936
9937    ['Text', 'Audio', 'Video'].forEach(function (track) {
9938      if (options['native' + track + 'Tracks'] === false) {
9939        _this['featuresNative' + track + 'Tracks'] = false;
9940      }
9941    });
9942
9943    if (options.nativeCaptions === false || options.nativeTextTracks === false) {
9944      _this.featuresNativeTextTracks = false;
9945    } else if (options.nativeCaptions === true || options.nativeTextTracks === true) {
9946      _this.featuresNativeTextTracks = true;
9947    }
9948
9949    if (!_this.featuresNativeTextTracks) {
9950      _this.emulateTextTracks();
9951    }
9952
9953    _this.autoRemoteTextTracks_ = new ALL.text.ListClass();
9954
9955    _this.initTrackListeners();
9956
9957    // Turn on component tap events only if not using native controls
9958    if (!options.nativeControlsForTouch) {
9959      _this.emitTapEvents();
9960    }
9961
9962    if (_this.constructor) {
9963      _this.name_ = _this.constructor.name || 'Unknown Tech';
9964    }
9965    return _this;
9966  }
9967
9968  /* Fallbacks for unsupported event types
9969  ================================================================================ */
9970
9971  /**
9972   * Polyfill the `progress` event for browsers that don't support it natively.
9973   *
9974   * @see {@link Tech#trackProgress}
9975   */
9976
9977
9978  Tech.prototype.manualProgressOn = function manualProgressOn() {
9979    this.on('durationchange', this.onDurationChange);
9980
9981    this.manualProgress = true;
9982
9983    // Trigger progress watching when a source begins loading
9984    this.one('ready', this.trackProgress);
9985  };
9986
9987  /**
9988   * Turn off the polyfill for `progress` events that was created in
9989   * {@link Tech#manualProgressOn}
9990   */
9991
9992
9993  Tech.prototype.manualProgressOff = function manualProgressOff() {
9994    this.manualProgress = false;
9995    this.stopTrackingProgress();
9996
9997    this.off('durationchange', this.onDurationChange);
9998  };
9999
10000  /**
10001   * This is used to trigger a `progress` event when the buffered percent changes. It
10002   * sets an interval function that will be called every 500 milliseconds to check if the
10003   * buffer end percent has changed.
10004   *
10005   * > This function is called by {@link Tech#manualProgressOn}
10006   *
10007   * @param {EventTarget~Event} event
10008   *        The `ready` event that caused this to run.
10009   *
10010   * @listens Tech#ready
10011   * @fires Tech#progress
10012   */
10013
10014
10015  Tech.prototype.trackProgress = function trackProgress(event) {
10016    this.stopTrackingProgress();
10017    this.progressInterval = this.setInterval(bind(this, function () {
10018      // Don't trigger unless buffered amount is greater than last time
10019
10020      var numBufferedPercent = this.bufferedPercent();
10021
10022      if (this.bufferedPercent_ !== numBufferedPercent) {
10023        /**
10024         * See {@link Player#progress}
10025         *
10026         * @event Tech#progress
10027         * @type {EventTarget~Event}
10028         */
10029        this.trigger('progress');
10030      }
10031
10032      this.bufferedPercent_ = numBufferedPercent;
10033
10034      if (numBufferedPercent === 1) {
10035        this.stopTrackingProgress();
10036      }
10037    }), 500);
10038  };
10039
10040  /**
10041   * Update our internal duration on a `durationchange` event by calling
10042   * {@link Tech#duration}.
10043   *
10044   * @param {EventTarget~Event} event
10045   *        The `durationchange` event that caused this to run.
10046   *
10047   * @listens Tech#durationchange
10048   */
10049
10050
10051  Tech.prototype.onDurationChange = function onDurationChange(event) {
10052    this.duration_ = this.duration();
10053  };
10054
10055  /**
10056   * Get and create a `TimeRange` object for buffering.
10057   *
10058   * @return {TimeRange}
10059   *         The time range object that was created.
10060   */
10061
10062
10063  Tech.prototype.buffered = function buffered() {
10064    return createTimeRanges(0, 0);
10065  };
10066
10067  /**
10068   * Get the percentage of the current video that is currently buffered.
10069   *
10070   * @return {number}
10071   *         A number from 0 to 1 that represents the decimal percentage of the
10072   *         video that is buffered.
10073   *
10074   */
10075
10076
10077  Tech.prototype.bufferedPercent = function bufferedPercent$$1() {
10078    return bufferedPercent(this.buffered(), this.duration_);
10079  };
10080
10081  /**
10082   * Turn off the polyfill for `progress` events that was created in
10083   * {@link Tech#manualProgressOn}
10084   * Stop manually tracking progress events by clearing the interval that was set in
10085   * {@link Tech#trackProgress}.
10086   */
10087
10088
10089  Tech.prototype.stopTrackingProgress = function stopTrackingProgress() {
10090    this.clearInterval(this.progressInterval);
10091  };
10092
10093  /**
10094   * Polyfill the `timeupdate` event for browsers that don't support it.
10095   *
10096   * @see {@link Tech#trackCurrentTime}
10097   */
10098
10099
10100  Tech.prototype.manualTimeUpdatesOn = function manualTimeUpdatesOn() {
10101    this.manualTimeUpdates = true;
10102
10103    this.on('play', this.trackCurrentTime);
10104    this.on('pause', this.stopTrackingCurrentTime);
10105  };
10106
10107  /**
10108   * Turn off the polyfill for `timeupdate` events that was created in
10109   * {@link Tech#manualTimeUpdatesOn}
10110   */
10111
10112
10113  Tech.prototype.manualTimeUpdatesOff = function manualTimeUpdatesOff() {
10114    this.manualTimeUpdates = false;
10115    this.stopTrackingCurrentTime();
10116    this.off('play', this.trackCurrentTime);
10117    this.off('pause', this.stopTrackingCurrentTime);
10118  };
10119
10120  /**
10121   * Sets up an interval function to track current time and trigger `timeupdate` every
10122   * 250 milliseconds.
10123   *
10124   * @listens Tech#play
10125   * @triggers Tech#timeupdate
10126   */
10127
10128
10129  Tech.prototype.trackCurrentTime = function trackCurrentTime() {
10130    if (this.currentTimeInterval) {
10131      this.stopTrackingCurrentTime();
10132    }
10133    this.currentTimeInterval = this.setInterval(function () {
10134      /**
10135       * Triggered at an interval of 250ms to indicated that time is passing in the video.
10136       *
10137       * @event Tech#timeupdate
10138       * @type {EventTarget~Event}
10139       */
10140      this.trigger({ type: 'timeupdate', target: this, manuallyTriggered: true });
10141
10142      // 42 = 24 fps // 250 is what Webkit uses // FF uses 15
10143    }, 250);
10144  };
10145
10146  /**
10147   * Stop the interval function created in {@link Tech#trackCurrentTime} so that the
10148   * `timeupdate` event is no longer triggered.
10149   *
10150   * @listens {Tech#pause}
10151   */
10152
10153
10154  Tech.prototype.stopTrackingCurrentTime = function stopTrackingCurrentTime() {
10155    this.clearInterval(this.currentTimeInterval);
10156
10157    // #1002 - if the video ends right before the next timeupdate would happen,
10158    // the progress bar won't make it all the way to the end
10159    this.trigger({ type: 'timeupdate', target: this, manuallyTriggered: true });
10160  };
10161
10162  /**
10163   * Turn off all event polyfills, clear the `Tech`s {@link AudioTrackList},
10164   * {@link VideoTrackList}, and {@link TextTrackList}, and dispose of this Tech.
10165   *
10166   * @fires Component#dispose
10167   */
10168
10169
10170  Tech.prototype.dispose = function dispose() {
10171
10172    // clear out all tracks because we can't reuse them between techs
10173    this.clearTracks(NORMAL.names);
10174
10175    // Turn off any manual progress or timeupdate tracking
10176    if (this.manualProgress) {
10177      this.manualProgressOff();
10178    }
10179
10180    if (this.manualTimeUpdates) {
10181      this.manualTimeUpdatesOff();
10182    }
10183
10184    _Component.prototype.dispose.call(this);
10185  };
10186
10187  /**
10188   * Clear out a single `TrackList` or an array of `TrackLists` given their names.
10189   *
10190   * > Note: Techs without source handlers should call this between sources for `video`
10191   *         & `audio` tracks. You don't want to use them between tracks!
10192   *
10193   * @param {string[]|string} types
10194   *        TrackList names to clear, valid names are `video`, `audio`, and
10195   *        `text`.
10196   */
10197
10198
10199  Tech.prototype.clearTracks = function clearTracks(types) {
10200    var _this2 = this;
10201
10202    types = [].concat(types);
10203    // clear out all tracks because we can't reuse them between techs
10204    types.forEach(function (type) {
10205      var list = _this2[type + 'Tracks']() || [];
10206      var i = list.length;
10207
10208      while (i--) {
10209        var track = list[i];
10210
10211        if (type === 'text') {
10212          _this2.removeRemoteTextTrack(track);
10213        }
10214        list.removeTrack(track);
10215      }
10216    });
10217  };
10218
10219  /**
10220   * Remove any TextTracks added via addRemoteTextTrack that are
10221   * flagged for automatic garbage collection
10222   */
10223
10224
10225  Tech.prototype.cleanupAutoTextTracks = function cleanupAutoTextTracks() {
10226    var list = this.autoRemoteTextTracks_ || [];
10227    var i = list.length;
10228
10229    while (i--) {
10230      var track = list[i];
10231
10232      this.removeRemoteTextTrack(track);
10233    }
10234  };
10235
10236  /**
10237   * Reset the tech, which will removes all sources and reset the internal readyState.
10238   *
10239   * @abstract
10240   */
10241
10242
10243  Tech.prototype.reset = function reset() {};
10244
10245  /**
10246   * Get or set an error on the Tech.
10247   *
10248   * @param {MediaError} [err]
10249   *        Error to set on the Tech
10250   *
10251   * @return {MediaError|null}
10252   *         The current error object on the tech, or null if there isn't one.
10253   */
10254
10255
10256  Tech.prototype.error = function error(err) {
10257    if (err !== undefined) {
10258      this.error_ = new MediaError(err);
10259      this.trigger('error');
10260    }
10261    return this.error_;
10262  };
10263
10264  /**
10265   * Returns the `TimeRange`s that have been played through for the current source.
10266   *
10267   * > NOTE: This implementation is incomplete. It does not track the played `TimeRange`.
10268   *         It only checks wether the source has played at all or not.
10269   *
10270   * @return {TimeRange}
10271   *         - A single time range if this video has played
10272   *         - An empty set of ranges if not.
10273   */
10274
10275
10276  Tech.prototype.played = function played() {
10277    if (this.hasStarted_) {
10278      return createTimeRanges(0, 0);
10279    }
10280    return createTimeRanges();
10281  };
10282
10283  /**
10284   * Causes a manual time update to occur if {@link Tech#manualTimeUpdatesOn} was
10285   * previously called.
10286   *
10287   * @fires Tech#timeupdate
10288   */
10289
10290
10291  Tech.prototype.setCurrentTime = function setCurrentTime() {
10292    // improve the accuracy of manual timeupdates
10293    if (this.manualTimeUpdates) {
10294      /**
10295       * A manual `timeupdate` event.
10296       *
10297       * @event Tech#timeupdate
10298       * @type {EventTarget~Event}
10299       */
10300      this.trigger({ type: 'timeupdate', target: this, manuallyTriggered: true });
10301    }
10302  };
10303
10304  /**
10305   * Turn on listeners for {@link VideoTrackList}, {@link {AudioTrackList}, and
10306   * {@link TextTrackList} events.
10307   *
10308   * This adds {@link EventTarget~EventListeners} for `addtrack`, and  `removetrack`.
10309   *
10310   * @fires Tech#audiotrackchange
10311   * @fires Tech#videotrackchange
10312   * @fires Tech#texttrackchange
10313   */
10314
10315
10316  Tech.prototype.initTrackListeners = function initTrackListeners() {
10317    var _this3 = this;
10318
10319    /**
10320     * Triggered when tracks are added or removed on the Tech {@link AudioTrackList}
10321     *
10322     * @event Tech#audiotrackchange
10323     * @type {EventTarget~Event}
10324     */
10325
10326    /**
10327     * Triggered when tracks are added or removed on the Tech {@link VideoTrackList}
10328     *
10329     * @event Tech#videotrackchange
10330     * @type {EventTarget~Event}
10331     */
10332
10333    /**
10334     * Triggered when tracks are added or removed on the Tech {@link TextTrackList}
10335     *
10336     * @event Tech#texttrackchange
10337     * @type {EventTarget~Event}
10338     */
10339    NORMAL.names.forEach(function (name) {
10340      var props = NORMAL[name];
10341      var trackListChanges = function trackListChanges() {
10342        _this3.trigger(name + 'trackchange');
10343      };
10344
10345      var tracks = _this3[props.getterName]();
10346
10347      tracks.addEventListener('removetrack', trackListChanges);
10348      tracks.addEventListener('addtrack', trackListChanges);
10349
10350      _this3.on('dispose', function () {
10351        tracks.removeEventListener('removetrack', trackListChanges);
10352        tracks.removeEventListener('addtrack', trackListChanges);
10353      });
10354    });
10355  };
10356
10357  /**
10358   * Emulate TextTracks using vtt.js if necessary
10359   *
10360   * @fires Tech#vttjsloaded
10361   * @fires Tech#vttjserror
10362   */
10363
10364
10365  Tech.prototype.addWebVttScript_ = function addWebVttScript_() {
10366    var _this4 = this;
10367
10368    if (window_1.WebVTT) {
10369      return;
10370    }
10371
10372    // Initially, Tech.el_ is a child of a dummy-div wait until the Component system
10373    // signals that the Tech is ready at which point Tech.el_ is part of the DOM
10374    // before inserting the WebVTT script
10375    if (document_1.body.contains(this.el())) {
10376
10377      // load via require if available and vtt.js script location was not passed in
10378      // as an option. novtt builds will turn the above require call into an empty object
10379      // which will cause this if check to always fail.
10380      if (!this.options_['vtt.js'] && isPlain(browserIndex) && Object.keys(browserIndex).length > 0) {
10381        this.trigger('vttjsloaded');
10382        return;
10383      }
10384
10385      // load vtt.js via the script location option or the cdn of no location was
10386      // passed in
10387      var script = document_1.createElement('script');
10388
10389      script.src = this.options_['vtt.js'] || 'https://vjs.zencdn.net/vttjs/0.12.4/vtt.min.js';
10390      script.onload = function () {
10391        /**
10392         * Fired when vtt.js is loaded.
10393         *
10394         * @event Tech#vttjsloaded
10395         * @type {EventTarget~Event}
10396         */
10397        _this4.trigger('vttjsloaded');
10398      };
10399      script.onerror = function () {
10400        /**
10401         * Fired when vtt.js was not loaded due to an error
10402         *
10403         * @event Tech#vttjsloaded
10404         * @type {EventTarget~Event}
10405         */
10406        _this4.trigger('vttjserror');
10407      };
10408      this.on('dispose', function () {
10409        script.onload = null;
10410        script.onerror = null;
10411      });
10412      // but have not loaded yet and we set it to true before the inject so that
10413      // we don't overwrite the injected window.WebVTT if it loads right away
10414      window_1.WebVTT = true;
10415      this.el().parentNode.appendChild(script);
10416    } else {
10417      this.ready(this.addWebVttScript_);
10418    }
10419  };
10420
10421  /**
10422   * Emulate texttracks
10423   *
10424   */
10425
10426
10427  Tech.prototype.emulateTextTracks = function emulateTextTracks() {
10428    var _this5 = this;
10429
10430    var tracks = this.textTracks();
10431    var remoteTracks = this.remoteTextTracks();
10432    var handleAddTrack = function handleAddTrack(e) {
10433      return tracks.addTrack(e.track);
10434    };
10435    var handleRemoveTrack = function handleRemoveTrack(e) {
10436      return tracks.removeTrack(e.track);
10437    };
10438
10439    remoteTracks.on('addtrack', handleAddTrack);
10440    remoteTracks.on('removetrack', handleRemoveTrack);
10441
10442    this.addWebVttScript_();
10443
10444    var updateDisplay = function updateDisplay() {
10445      return _this5.trigger('texttrackchange');
10446    };
10447
10448    var textTracksChanges = function textTracksChanges() {
10449      updateDisplay();
10450
10451      for (var i = 0; i < tracks.length; i++) {
10452        var track = tracks[i];
10453
10454        track.removeEventListener('cuechange', updateDisplay);
10455        if (track.mode === 'showing') {
10456          track.addEventListener('cuechange', updateDisplay);
10457        }
10458      }
10459    };
10460
10461    textTracksChanges();
10462    tracks.addEventListener('change', textTracksChanges);
10463    tracks.addEventListener('addtrack', textTracksChanges);
10464    tracks.addEventListener('removetrack', textTracksChanges);
10465
10466    this.on('dispose', function () {
10467      remoteTracks.off('addtrack', handleAddTrack);
10468      remoteTracks.off('removetrack', handleRemoveTrack);
10469      tracks.removeEventListener('change', textTracksChanges);
10470      tracks.removeEventListener('addtrack', textTracksChanges);
10471      tracks.removeEventListener('removetrack', textTracksChanges);
10472
10473      for (var i = 0; i < tracks.length; i++) {
10474        var track = tracks[i];
10475
10476        track.removeEventListener('cuechange', updateDisplay);
10477      }
10478    });
10479  };
10480
10481  /**
10482   * Create and returns a remote {@link TextTrack} object.
10483   *
10484   * @param {string} kind
10485   *        `TextTrack` kind (subtitles, captions, descriptions, chapters, or metadata)
10486   *
10487   * @param {string} [label]
10488   *        Label to identify the text track
10489   *
10490   * @param {string} [language]
10491   *        Two letter language abbreviation
10492   *
10493   * @return {TextTrack}
10494   *         The TextTrack that gets created.
10495   */
10496
10497
10498  Tech.prototype.addTextTrack = function addTextTrack(kind, label, language) {
10499    if (!kind) {
10500      throw new Error('TextTrack kind is required but was not provided');
10501    }
10502
10503    return createTrackHelper(this, kind, label, language);
10504  };
10505
10506  /**
10507   * Create an emulated TextTrack for use by addRemoteTextTrack
10508   *
10509   * This is intended to be overridden by classes that inherit from
10510   * Tech in order to create native or custom TextTracks.
10511   *
10512   * @param {Object} options
10513   *        The object should contain the options to initialize the TextTrack with.
10514   *
10515   * @param {string} [options.kind]
10516   *        `TextTrack` kind (subtitles, captions, descriptions, chapters, or metadata).
10517   *
10518   * @param {string} [options.label].
10519   *        Label to identify the text track
10520   *
10521   * @param {string} [options.language]
10522   *        Two letter language abbreviation.
10523   *
10524   * @return {HTMLTrackElement}
10525   *         The track element that gets created.
10526   */
10527
10528
10529  Tech.prototype.createRemoteTextTrack = function createRemoteTextTrack(options) {
10530    var track = mergeOptions(options, {
10531      tech: this
10532    });
10533
10534    return new REMOTE.remoteTextEl.TrackClass(track);
10535  };
10536
10537  /**
10538   * Creates a remote text track object and returns an html track element.
10539   *
10540   * > Note: This can be an emulated {@link HTMLTrackElement} or a native one.
10541   *
10542   * @param {Object} options
10543   *        See {@link Tech#createRemoteTextTrack} for more detailed properties.
10544   *
10545   * @param {boolean} [manualCleanup=true]
10546   *        - When false: the TextTrack will be automatically removed from the video
10547   *          element whenever the source changes
10548   *        - When True: The TextTrack will have to be cleaned up manually
10549   *
10550   * @return {HTMLTrackElement}
10551   *         An Html Track Element.
10552   *
10553   * @deprecated The default functionality for this function will be equivalent
10554   *             to "manualCleanup=false" in the future. The manualCleanup parameter will
10555   *             also be removed.
10556   */
10557
10558
10559  Tech.prototype.addRemoteTextTrack = function addRemoteTextTrack() {
10560    var _this6 = this;
10561
10562    var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
10563    var manualCleanup = arguments[1];
10564
10565    var htmlTrackElement = this.createRemoteTextTrack(options);
10566
10567    if (manualCleanup !== true && manualCleanup !== false) {
10568      // deprecation warning
10569      log$1.warn('Calling addRemoteTextTrack without explicitly setting the "manualCleanup" parameter to `true` is deprecated and default to `false` in future version of video.js');
10570      manualCleanup = true;
10571    }
10572
10573    // store HTMLTrackElement and TextTrack to remote list
10574    this.remoteTextTrackEls().addTrackElement_(htmlTrackElement);
10575    this.remoteTextTracks().addTrack(htmlTrackElement.track);
10576
10577    if (manualCleanup !== true) {
10578      // create the TextTrackList if it doesn't exist
10579      this.ready(function () {
10580        return _this6.autoRemoteTextTracks_.addTrack(htmlTrackElement.track);
10581      });
10582    }
10583
10584    return htmlTrackElement;
10585  };
10586
10587  /**
10588   * Remove a remote text track from the remote `TextTrackList`.
10589   *
10590   * @param {TextTrack} track
10591   *        `TextTrack` to remove from the `TextTrackList`
10592   */
10593
10594
10595  Tech.prototype.removeRemoteTextTrack = function removeRemoteTextTrack(track) {
10596    var trackElement = this.remoteTextTrackEls().getTrackElementByTrack_(track);
10597
10598    // remove HTMLTrackElement and TextTrack from remote list
10599    this.remoteTextTrackEls().removeTrackElement_(trackElement);
10600    this.remoteTextTracks().removeTrack(track);
10601    this.autoRemoteTextTracks_.removeTrack(track);
10602  };
10603
10604  /**
10605   * Gets available media playback quality metrics as specified by the W3C's Media
10606   * Playback Quality API.
10607   *
10608   * @see [Spec]{@link https://wicg.github.io/media-playback-quality}
10609   *
10610   * @return {Object}
10611   *         An object with supported media playback quality metrics
10612   *
10613   * @abstract
10614   */
10615
10616
10617  Tech.prototype.getVideoPlaybackQuality = function getVideoPlaybackQuality() {
10618    return {};
10619  };
10620
10621  /**
10622   * A method to set a poster from a `Tech`.
10623   *
10624   * @abstract
10625   */
10626
10627
10628  Tech.prototype.setPoster = function setPoster() {};
10629
10630  /**
10631   * A method to check for the presence of the 'playsinine' <video> attribute.
10632   *
10633   * @abstract
10634   */
10635
10636
10637  Tech.prototype.playsinline = function playsinline() {};
10638
10639  /**
10640   * A method to set or unset the 'playsinine' <video> attribute.
10641   *
10642   * @abstract
10643   */
10644
10645
10646  Tech.prototype.setPlaysinline = function setPlaysinline() {};
10647
10648  /*
10649   * Check if the tech can support the given mime-type.
10650   *
10651   * The base tech does not support any type, but source handlers might
10652   * overwrite this.
10653   *
10654   * @param  {string} type
10655   *         The mimetype to check for support
10656   *
10657   * @return {string}
10658   *         'probably', 'maybe', or empty string
10659   *
10660   * @see [Spec]{@link https://developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement/canPlayType}
10661   *
10662   * @abstract
10663   */
10664
10665
10666  Tech.prototype.canPlayType = function canPlayType() {
10667    return '';
10668  };
10669
10670  /**
10671   * Check if the type is supported by this tech.
10672   *
10673   * The base tech does not support any type, but source handlers might
10674   * overwrite this.
10675   *
10676   * @param {string} type
10677   *        The media type to check
10678   * @return {string} Returns the native video element's response
10679   */
10680
10681
10682  Tech.canPlayType = function canPlayType() {
10683    return '';
10684  };
10685
10686  /**
10687   * Check if the tech can support the given source
10688   * @param {Object} srcObj
10689   *        The source object
10690   * @param {Object} options
10691   *        The options passed to the tech
10692   * @return {string} 'probably', 'maybe', or '' (empty string)
10693   */
10694
10695
10696  Tech.canPlaySource = function canPlaySource(srcObj, options) {
10697    return Tech.canPlayType(srcObj.type);
10698  };
10699
10700  /*
10701   * Return whether the argument is a Tech or not.
10702   * Can be passed either a Class like `Html5` or a instance like `player.tech_`
10703   *
10704   * @param {Object} component
10705   *        The item to check
10706   *
10707   * @return {boolean}
10708   *         Whether it is a tech or not
10709   *         - True if it is a tech
10710   *         - False if it is not
10711   */
10712
10713
10714  Tech.isTech = function isTech(component) {
10715    return component.prototype instanceof Tech || component instanceof Tech || component === Tech;
10716  };
10717
10718  /**
10719   * Registers a `Tech` into a shared list for videojs.
10720   *
10721   * @param {string} name
10722   *        Name of the `Tech` to register.
10723   *
10724   * @param {Object} tech
10725   *        The `Tech` class to register.
10726   */
10727
10728
10729  Tech.registerTech = function registerTech(name, tech) {
10730    if (!Tech.techs_) {
10731      Tech.techs_ = {};
10732    }
10733
10734    if (!Tech.isTech(tech)) {
10735      throw new Error('Tech ' + name + ' must be a Tech');
10736    }
10737
10738    if (!Tech.canPlayType) {
10739      throw new Error('Techs must have a static canPlayType method on them');
10740    }
10741    if (!Tech.canPlaySource) {
10742      throw new Error('Techs must have a static canPlaySource method on them');
10743    }
10744
10745    name = toTitleCase(name);
10746
10747    Tech.techs_[name] = tech;
10748    if (name !== 'Tech') {
10749      // camel case the techName for use in techOrder
10750      Tech.defaultTechOrder_.push(name);
10751    }
10752    return tech;
10753  };
10754
10755  /**
10756   * Get a `Tech` from the shared list by name.
10757   *
10758   * @param {string} name
10759   *        `camelCase` or `TitleCase` name of the Tech to get
10760   *
10761   * @return {Tech|undefined}
10762   *         The `Tech` or undefined if there was no tech with the name requsted.
10763   */
10764
10765
10766  Tech.getTech = function getTech(name) {
10767    if (!name) {
10768      return;
10769    }
10770
10771    name = toTitleCase(name);
10772
10773    if (Tech.techs_ && Tech.techs_[name]) {
10774      return Tech.techs_[name];
10775    }
10776
10777    if (window_1 && window_1.videojs && window_1.videojs[name]) {
10778      log$1.warn('The ' + name + ' tech was added to the videojs object when it should be registered using videojs.registerTech(name, tech)');
10779      return window_1.videojs[name];
10780    }
10781  };
10782
10783  return Tech;
10784}(Component);
10785
10786/**
10787 * Get the {@link VideoTrackList}
10788 *
10789 * @returns {VideoTrackList}
10790 * @method Tech.prototype.videoTracks
10791 */
10792
10793/**
10794 * Get the {@link AudioTrackList}
10795 *
10796 * @returns {AudioTrackList}
10797 * @method Tech.prototype.audioTracks
10798 */
10799
10800/**
10801 * Get the {@link TextTrackList}
10802 *
10803 * @returns {TextTrackList}
10804 * @method Tech.prototype.textTracks
10805 */
10806
10807/**
10808 * Get the remote element {@link TextTrackList}
10809 *
10810 * @returns {TextTrackList}
10811 * @method Tech.prototype.remoteTextTracks
10812 */
10813
10814/**
10815 * Get the remote element {@link HtmlTrackElementList}
10816 *
10817 * @returns {HtmlTrackElementList}
10818 * @method Tech.prototype.remoteTextTrackEls
10819 */
10820
10821ALL.names.forEach(function (name) {
10822  var props = ALL[name];
10823
10824  Tech.prototype[props.getterName] = function () {
10825    this[props.privateName] = this[props.privateName] || new props.ListClass();
10826    return this[props.privateName];
10827  };
10828});
10829
10830/**
10831 * List of associated text tracks
10832 *
10833 * @type {TextTrackList}
10834 * @private
10835 * @property Tech#textTracks_
10836 */
10837
10838/**
10839 * List of associated audio tracks.
10840 *
10841 * @type {AudioTrackList}
10842 * @private
10843 * @property Tech#audioTracks_
10844 */
10845
10846/**
10847 * List of associated video tracks.
10848 *
10849 * @type {VideoTrackList}
10850 * @private
10851 * @property Tech#videoTracks_
10852 */
10853
10854/**
10855 * Boolean indicating wether the `Tech` supports volume control.
10856 *
10857 * @type {boolean}
10858 * @default
10859 */
10860Tech.prototype.featuresVolumeControl = true;
10861
10862/**
10863 * Boolean indicating wether the `Tech` support fullscreen resize control.
10864 * Resizing plugins using request fullscreen reloads the plugin
10865 *
10866 * @type {boolean}
10867 * @default
10868 */
10869Tech.prototype.featuresFullscreenResize = false;
10870
10871/**
10872 * Boolean indicating wether the `Tech` supports changing the speed at which the video
10873 * plays. Examples:
10874 *   - Set player to play 2x (twice) as fast
10875 *   - Set player to play 0.5x (half) as fast
10876 *
10877 * @type {boolean}
10878 * @default
10879 */
10880Tech.prototype.featuresPlaybackRate = false;
10881
10882/**
10883 * Boolean indicating wether the `Tech` supports the `progress` event. This is currently
10884 * not triggered by video-js-swf. This will be used to determine if
10885 * {@link Tech#manualProgressOn} should be called.
10886 *
10887 * @type {boolean}
10888 * @default
10889 */
10890Tech.prototype.featuresProgressEvents = false;
10891
10892/**
10893 * Boolean indicating wether the `Tech` supports the `timeupdate` event. This is currently
10894 * not triggered by video-js-swf. This will be used to determine if
10895 * {@link Tech#manualTimeUpdates} should be called.
10896 *
10897 * @type {boolean}
10898 * @default
10899 */
10900Tech.prototype.featuresTimeupdateEvents = false;
10901
10902/**
10903 * Boolean indicating wether the `Tech` supports the native `TextTrack`s.
10904 * This will help us integrate with native `TextTrack`s if the browser supports them.
10905 *
10906 * @type {boolean}
10907 * @default
10908 */
10909Tech.prototype.featuresNativeTextTracks = false;
10910
10911/**
10912 * A functional mixin for techs that want to use the Source Handler pattern.
10913 * Source handlers are scripts for handling specific formats.
10914 * The source handler pattern is used for adaptive formats (HLS, DASH) that
10915 * manually load video data and feed it into a Source Buffer (Media Source Extensions)
10916 * Example: `Tech.withSourceHandlers.call(MyTech);`
10917 *
10918 * @param {Tech} _Tech
10919 *        The tech to add source handler functions to.
10920 *
10921 * @mixes Tech~SourceHandlerAdditions
10922 */
10923Tech.withSourceHandlers = function (_Tech) {
10924
10925  /**
10926   * Register a source handler
10927   *
10928   * @param {Function} handler
10929   *        The source handler class
10930   *
10931   * @param {number} [index]
10932   *        Register it at the following index
10933   */
10934  _Tech.registerSourceHandler = function (handler, index) {
10935    var handlers = _Tech.sourceHandlers;
10936
10937    if (!handlers) {
10938      handlers = _Tech.sourceHandlers = [];
10939    }
10940
10941    if (index === undefined) {
10942      // add to the end of the list
10943      index = handlers.length;
10944    }
10945
10946    handlers.splice(index, 0, handler);
10947  };
10948
10949  /**
10950   * Check if the tech can support the given type. Also checks the
10951   * Techs sourceHandlers.
10952   *
10953   * @param {string} type
10954   *         The mimetype to check.
10955   *
10956   * @return {string}
10957   *         'probably', 'maybe', or '' (empty string)
10958   */
10959  _Tech.canPlayType = function (type) {
10960    var handlers = _Tech.sourceHandlers || [];
10961    var can = void 0;
10962
10963    for (var i = 0; i < handlers.length; i++) {
10964      can = handlers[i].canPlayType(type);
10965
10966      if (can) {
10967        return can;
10968      }
10969    }
10970
10971    return '';
10972  };
10973
10974  /**
10975   * Returns the first source handler that supports the source.
10976   *
10977   * TODO: Answer question: should 'probably' be prioritized over 'maybe'
10978   *
10979   * @param {Tech~SourceObject} source
10980   *        The source object
10981   *
10982   * @param {Object} options
10983   *        The options passed to the tech
10984   *
10985   * @return {SourceHandler|null}
10986   *          The first source handler that supports the source or null if
10987   *          no SourceHandler supports the source
10988   */
10989  _Tech.selectSourceHandler = function (source, options) {
10990    var handlers = _Tech.sourceHandlers || [];
10991    var can = void 0;
10992
10993    for (var i = 0; i < handlers.length; i++) {
10994      can = handlers[i].canHandleSource(source, options);
10995
10996      if (can) {
10997        return handlers[i];
10998      }
10999    }
11000
11001    return null;
11002  };
11003
11004  /**
11005   * Check if the tech can support the given source.
11006   *
11007   * @param {Tech~SourceObject} srcObj
11008   *        The source object
11009   *
11010   * @param {Object} options
11011   *        The options passed to the tech
11012   *
11013   * @return {string}
11014   *         'probably', 'maybe', or '' (empty string)
11015   */
11016  _Tech.canPlaySource = function (srcObj, options) {
11017    var sh = _Tech.selectSourceHandler(srcObj, options);
11018
11019    if (sh) {
11020      return sh.canHandleSource(srcObj, options);
11021    }
11022
11023    return '';
11024  };
11025
11026  /**
11027   * When using a source handler, prefer its implementation of
11028   * any function normally provided by the tech.
11029   */
11030  var deferrable = ['seekable', 'duration'];
11031
11032  /**
11033   * A wrapper around {@link Tech#seekable} that will call a `SourceHandler`s seekable
11034   * function if it exists, with a fallback to the Techs seekable function.
11035   *
11036   * @method _Tech.seekable
11037   */
11038
11039  /**
11040   * A wrapper around {@link Tech#duration} that will call a `SourceHandler`s duration
11041   * function if it exists, otherwise it will fallback to the techs duration function.
11042   *
11043   * @method _Tech.duration
11044   */
11045
11046  deferrable.forEach(function (fnName) {
11047    var originalFn = this[fnName];
11048
11049    if (typeof originalFn !== 'function') {
11050      return;
11051    }
11052
11053    this[fnName] = function () {
11054      if (this.sourceHandler_ && this.sourceHandler_[fnName]) {
11055        return this.sourceHandler_[fnName].apply(this.sourceHandler_, arguments);
11056      }
11057      return originalFn.apply(this, arguments);
11058    };
11059  }, _Tech.prototype);
11060
11061  /**
11062   * Create a function for setting the source using a source object
11063   * and source handlers.
11064   * Should never be called unless a source handler was found.
11065   *
11066   * @param {Tech~SourceObject} source
11067   *        A source object with src and type keys
11068   */
11069  _Tech.prototype.setSource = function (source) {
11070    var sh = _Tech.selectSourceHandler(source, this.options_);
11071
11072    if (!sh) {
11073      // Fall back to a native source hander when unsupported sources are
11074      // deliberately set
11075      if (_Tech.nativeSourceHandler) {
11076        sh = _Tech.nativeSourceHandler;
11077      } else {
11078        log$1.error('No source hander found for the current source.');
11079      }
11080    }
11081
11082    // Dispose any existing source handler
11083    this.disposeSourceHandler();
11084    this.off('dispose', this.disposeSourceHandler);
11085
11086    if (sh !== _Tech.nativeSourceHandler) {
11087      this.currentSource_ = source;
11088    }
11089
11090    this.sourceHandler_ = sh.handleSource(source, this, this.options_);
11091    this.on('dispose', this.disposeSourceHandler);
11092  };
11093
11094  /**
11095   * Clean up any existing SourceHandlers and listeners when the Tech is disposed.
11096   *
11097   * @listens Tech#dispose
11098   */
11099  _Tech.prototype.disposeSourceHandler = function () {
11100    // if we have a source and get another one
11101    // then we are loading something new
11102    // than clear all of our current tracks
11103    if (this.currentSource_) {
11104      this.clearTracks(['audio', 'video']);
11105      this.currentSource_ = null;
11106    }
11107
11108    // always clean up auto-text tracks
11109    this.cleanupAutoTextTracks();
11110
11111    if (this.sourceHandler_) {
11112
11113      if (this.sourceHandler_.dispose) {
11114        this.sourceHandler_.dispose();
11115      }
11116
11117      this.sourceHandler_ = null;
11118    }
11119  };
11120};
11121
11122// The base Tech class needs to be registered as a Component. It is the only
11123// Tech that can be registered as a Component.
11124Component.registerComponent('Tech', Tech);
11125Tech.registerTech('Tech', Tech);
11126
11127/**
11128 * A list of techs that should be added to techOrder on Players
11129 *
11130 * @private
11131 */
11132Tech.defaultTechOrder_ = [];
11133
11134var middlewares = {};
11135
11136function use(type, middleware) {
11137  middlewares[type] = middlewares[type] || [];
11138  middlewares[type].push(middleware);
11139}
11140
11141
11142
11143function setSource(player, src, next) {
11144  player.setTimeout(function () {
11145    return setSourceHelper(src, middlewares[src.type], next, player);
11146  }, 1);
11147}
11148
11149function setTech(middleware, tech) {
11150  middleware.forEach(function (mw) {
11151    return mw.setTech && mw.setTech(tech);
11152  });
11153}
11154
11155function get$1(middleware, tech, method) {
11156  return middleware.reduceRight(middlewareIterator(method), tech[method]());
11157}
11158
11159function set$1(middleware, tech, method, arg) {
11160  return tech[method](middleware.reduce(middlewareIterator(method), arg));
11161}
11162
11163var allowedGetters = {
11164  buffered: 1,
11165  currentTime: 1,
11166  duration: 1,
11167  seekable: 1,
11168  played: 1
11169};
11170
11171var allowedSetters = {
11172  setCurrentTime: 1
11173};
11174
11175function middlewareIterator(method) {
11176  return function (value, mw) {
11177    if (mw[method]) {
11178      return mw[method](value);
11179    }
11180
11181    return value;
11182  };
11183}
11184
11185function setSourceHelper() {
11186  var src = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
11187  var middleware = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : [];
11188  var next = arguments[2];
11189  var player = arguments[3];
11190  var acc = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : [];
11191  var lastRun = arguments.length > 5 && arguments[5] !== undefined ? arguments[5] : false;
11192  var mwFactory = middleware[0],
11193      mwrest = middleware.slice(1);
11194
11195  // if mwFactory is a string, then we're at a fork in the road
11196
11197  if (typeof mwFactory === 'string') {
11198    setSourceHelper(src, middlewares[mwFactory], next, player, acc, lastRun);
11199
11200    // if we have an mwFactory, call it with the player to get the mw,
11201    // then call the mw's setSource method
11202  } else if (mwFactory) {
11203    var mw = mwFactory(player);
11204
11205    mw.setSource(assign({}, src), function (err, _src) {
11206
11207      // something happened, try the next middleware on the current level
11208      // make sure to use the old src
11209      if (err) {
11210        return setSourceHelper(src, mwrest, next, player, acc, lastRun);
11211      }
11212
11213      // we've succeeded, now we need to go deeper
11214      acc.push(mw);
11215
11216      // if it's the same time, continue does the current chain
11217      // otherwise, we want to go down the new chain
11218      setSourceHelper(_src, src.type === _src.type ? mwrest : middlewares[_src.type], next, player, acc, lastRun);
11219    });
11220  } else if (mwrest.length) {
11221    setSourceHelper(src, mwrest, next, player, acc, lastRun);
11222  } else if (lastRun) {
11223    next(src, acc);
11224  } else {
11225    setSourceHelper(src, middlewares['*'], next, player, acc, true);
11226  }
11227}
11228
11229/**
11230 * @module filter-source
11231 */
11232/**
11233 * Filter out single bad source objects or multiple source objects in an
11234 * array. Also flattens nested source object arrays into a 1 dimensional
11235 * array of source objects.
11236 *
11237 * @param {Tech~SourceObject|Tech~SourceObject[]} src
11238 *        The src object to filter
11239 *
11240 * @return {Tech~SourceObject[]}
11241 *         An array of sourceobjects containing only valid sources
11242 *
11243 * @private
11244 */
11245var filterSource = function filterSource(src) {
11246  // traverse array
11247  if (Array.isArray(src)) {
11248    var newsrc = [];
11249
11250    src.forEach(function (srcobj) {
11251      srcobj = filterSource(srcobj);
11252
11253      if (Array.isArray(srcobj)) {
11254        newsrc = newsrc.concat(srcobj);
11255      } else if (isObject(srcobj)) {
11256        newsrc.push(srcobj);
11257      }
11258    });
11259
11260    src = newsrc;
11261  } else if (typeof src === 'string' && src.trim()) {
11262    // convert string into object
11263    src = [{ src: src }];
11264  } else if (isObject(src) && typeof src.src === 'string' && src.src && src.src.trim()) {
11265    // src is already valid
11266    src = [src];
11267  } else {
11268    // invalid source, turn it into an empty array
11269    src = [];
11270  }
11271
11272  return src;
11273};
11274
11275/**
11276 * @file loader.js
11277 */
11278/**
11279 * The `MediaLoader` is the `Component` that decides which playback technology to load
11280 * when a player is initialized.
11281 *
11282 * @extends Component
11283 */
11284
11285var MediaLoader = function (_Component) {
11286  inherits(MediaLoader, _Component);
11287
11288  /**
11289   * Create an instance of this class.
11290   *
11291   * @param {Player} player
11292   *        The `Player` that this class should attach to.
11293   *
11294   * @param {Object} [options]
11295   *        The key/value stroe of player options.
11296   *
11297   * @param {Component~ReadyCallback} [ready]
11298   *        The function that is run when this component is ready.
11299   */
11300  function MediaLoader(player, options, ready) {
11301    classCallCheck(this, MediaLoader);
11302
11303    // MediaLoader has no element
11304    var options_ = mergeOptions({ createEl: false }, options);
11305
11306    // If there are no sources when the player is initialized,
11307    // load the first supported playback technology.
11308
11309    var _this = possibleConstructorReturn(this, _Component.call(this, player, options_, ready));
11310
11311    if (!options.playerOptions.sources || options.playerOptions.sources.length === 0) {
11312      for (var i = 0, j = options.playerOptions.techOrder; i < j.length; i++) {
11313        var techName = toTitleCase(j[i]);
11314        var tech = Tech.getTech(techName);
11315
11316        // Support old behavior of techs being registered as components.
11317        // Remove once that deprecated behavior is removed.
11318        if (!techName) {
11319          tech = Component.getComponent(techName);
11320        }
11321
11322        // Check if the browser supports this technology
11323        if (tech && tech.isSupported()) {
11324          player.loadTech_(techName);
11325          break;
11326        }
11327      }
11328    } else {
11329      // Loop through playback technologies (HTML5, Flash) and check for support.
11330      // Then load the best source.
11331      // A few assumptions here:
11332      //   All playback technologies respect preload false.
11333      player.src(options.playerOptions.sources);
11334    }
11335    return _this;
11336  }
11337
11338  return MediaLoader;
11339}(Component);
11340
11341Component.registerComponent('MediaLoader', MediaLoader);
11342
11343/**
11344 * @file button.js
11345 */
11346/**
11347 * Clickable Component which is clickable or keyboard actionable,
11348 * but is not a native HTML button.
11349 *
11350 * @extends Component
11351 */
11352
11353var ClickableComponent = function (_Component) {
11354  inherits(ClickableComponent, _Component);
11355
11356  /**
11357   * Creates an instance of this class.
11358   *
11359   * @param  {Player} player
11360   *         The `Player` that this class should be attached to.
11361   *
11362   * @param  {Object} [options]
11363   *         The key/value store of player options.
11364   */
11365  function ClickableComponent(player, options) {
11366    classCallCheck(this, ClickableComponent);
11367
11368    var _this = possibleConstructorReturn(this, _Component.call(this, player, options));
11369
11370    _this.emitTapEvents();
11371
11372    _this.enable();
11373    return _this;
11374  }
11375
11376  /**
11377   * Create the `Component`s DOM element.
11378   *
11379   * @param {string} [tag=div]
11380   *        The element's node type.
11381   *
11382   * @param {Object} [props={}]
11383   *        An object of properties that should be set on the element.
11384   *
11385   * @param {Object} [attributes={}]
11386   *        An object of attributes that should be set on the element.
11387   *
11388   * @return {Element}
11389   *         The element that gets created.
11390   */
11391
11392
11393  ClickableComponent.prototype.createEl = function createEl$$1() {
11394    var tag = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 'div';
11395    var props = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
11396    var attributes = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
11397
11398    props = assign({
11399      innerHTML: '<span aria-hidden="true" class="vjs-icon-placeholder"></span>',
11400      className: this.buildCSSClass(),
11401      tabIndex: 0
11402    }, props);
11403
11404    if (tag === 'button') {
11405      log$1.error('Creating a ClickableComponent with an HTML element of ' + tag + ' is not supported; use a Button instead.');
11406    }
11407
11408    // Add ARIA attributes for clickable element which is not a native HTML button
11409    attributes = assign({
11410      'role': 'button',
11411
11412      // let the screen reader user know that the text of the element may change
11413      'aria-live': 'polite'
11414    }, attributes);
11415
11416    this.tabIndex_ = props.tabIndex;
11417
11418    var el = _Component.prototype.createEl.call(this, tag, props, attributes);
11419
11420    this.createControlTextEl(el);
11421
11422    return el;
11423  };
11424
11425  /**
11426   * Create a control text element on this `Component`
11427   *
11428   * @param {Element} [el]
11429   *        Parent element for the control text.
11430   *
11431   * @return {Element}
11432   *         The control text element that gets created.
11433   */
11434
11435
11436  ClickableComponent.prototype.createControlTextEl = function createControlTextEl(el) {
11437    this.controlTextEl_ = createEl('span', {
11438      className: 'vjs-control-text'
11439    });
11440
11441    if (el) {
11442      el.appendChild(this.controlTextEl_);
11443    }
11444
11445    this.controlText(this.controlText_, el);
11446
11447    return this.controlTextEl_;
11448  };
11449
11450  /**
11451   * Get or set the localize text to use for the controls on the `Component`.
11452   *
11453   * @param {string} [text]
11454   *        Control text for element.
11455   *
11456   * @param {Element} [el=this.el()]
11457   *        Element to set the title on.
11458   *
11459   * @return {string}
11460   *         - The control text when getting
11461   */
11462
11463
11464  ClickableComponent.prototype.controlText = function controlText(text) {
11465    var el = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : this.el();
11466
11467    if (!text) {
11468      return this.controlText_ || 'Need Text';
11469    }
11470
11471    var localizedText = this.localize(text);
11472
11473    this.controlText_ = text;
11474    textContent(this.controlTextEl_, localizedText);
11475    if (!this.nonIconControl) {
11476      // Set title attribute if only an icon is shown
11477      el.setAttribute('title', localizedText);
11478    }
11479  };
11480
11481  /**
11482   * Builds the default DOM `className`.
11483   *
11484   * @return {string}
11485   *         The DOM `className` for this object.
11486   */
11487
11488
11489  ClickableComponent.prototype.buildCSSClass = function buildCSSClass() {
11490    return 'vjs-control vjs-button ' + _Component.prototype.buildCSSClass.call(this);
11491  };
11492
11493  /**
11494   * Enable this `Component`s element.
11495   */
11496
11497
11498  ClickableComponent.prototype.enable = function enable() {
11499    if (!this.enabled_) {
11500      this.enabled_ = true;
11501      this.removeClass('vjs-disabled');
11502      this.el_.setAttribute('aria-disabled', 'false');
11503      if (typeof this.tabIndex_ !== 'undefined') {
11504        this.el_.setAttribute('tabIndex', this.tabIndex_);
11505      }
11506      this.on(['tap', 'click'], this.handleClick);
11507      this.on('focus', this.handleFocus);
11508      this.on('blur', this.handleBlur);
11509    }
11510  };
11511
11512  /**
11513   * Disable this `Component`s element.
11514   */
11515
11516
11517  ClickableComponent.prototype.disable = function disable() {
11518    this.enabled_ = false;
11519    this.addClass('vjs-disabled');
11520    this.el_.setAttribute('aria-disabled', 'true');
11521    if (typeof this.tabIndex_ !== 'undefined') {
11522      this.el_.removeAttribute('tabIndex');
11523    }
11524    this.off(['tap', 'click'], this.handleClick);
11525    this.off('focus', this.handleFocus);
11526    this.off('blur', this.handleBlur);
11527  };
11528
11529  /**
11530   * This gets called when a `ClickableComponent` gets:
11531   * - Clicked (via the `click` event, listening starts in the constructor)
11532   * - Tapped (via the `tap` event, listening starts in the constructor)
11533   * - The following things happen in order:
11534   *   1. {@link ClickableComponent#handleFocus} is called via a `focus` event on the
11535   *      `ClickableComponent`.
11536   *   2. {@link ClickableComponent#handleFocus} adds a listener for `keydown` on using
11537   *      {@link ClickableComponent#handleKeyPress}.
11538   *   3. `ClickableComponent` has not had a `blur` event (`blur` means that focus was lost). The user presses
11539   *      the space or enter key.
11540   *   4. {@link ClickableComponent#handleKeyPress} calls this function with the `keydown`
11541   *      event as a parameter.
11542   *
11543   * @param {EventTarget~Event} event
11544   *        The `keydown`, `tap`, or `click` event that caused this function to be
11545   *        called.
11546   *
11547   * @listens tap
11548   * @listens click
11549   * @abstract
11550   */
11551
11552
11553  ClickableComponent.prototype.handleClick = function handleClick(event) {};
11554
11555  /**
11556   * This gets called when a `ClickableComponent` gains focus via a `focus` event.
11557   * Turns on listening for `keydown` events. When they happen it
11558   * calls `this.handleKeyPress`.
11559   *
11560   * @param {EventTarget~Event} event
11561   *        The `focus` event that caused this function to be called.
11562   *
11563   * @listens focus
11564   */
11565
11566
11567  ClickableComponent.prototype.handleFocus = function handleFocus(event) {
11568    on(document_1, 'keydown', bind(this, this.handleKeyPress));
11569  };
11570
11571  /**
11572   * Called when this ClickableComponent has focus and a key gets pressed down. By
11573   * default it will call `this.handleClick` when the key is space or enter.
11574   *
11575   * @param {EventTarget~Event} event
11576   *        The `keydown` event that caused this function to be called.
11577   *
11578   * @listens keydown
11579   */
11580
11581
11582  ClickableComponent.prototype.handleKeyPress = function handleKeyPress(event) {
11583
11584    // Support Space (32) or Enter (13) key operation to fire a click event
11585    if (event.which === 32 || event.which === 13) {
11586      event.preventDefault();
11587      this.trigger('click');
11588    } else if (_Component.prototype.handleKeyPress) {
11589
11590      // Pass keypress handling up for unsupported keys
11591      _Component.prototype.handleKeyPress.call(this, event);
11592    }
11593  };
11594
11595  /**
11596   * Called when a `ClickableComponent` loses focus. Turns off the listener for
11597   * `keydown` events. Which Stops `this.handleKeyPress` from getting called.
11598   *
11599   * @param {EventTarget~Event} event
11600   *        The `blur` event that caused this function to be called.
11601   *
11602   * @listens blur
11603   */
11604
11605
11606  ClickableComponent.prototype.handleBlur = function handleBlur(event) {
11607    off(document_1, 'keydown', bind(this, this.handleKeyPress));
11608  };
11609
11610  return ClickableComponent;
11611}(Component);
11612
11613Component.registerComponent('ClickableComponent', ClickableComponent);
11614
11615/**
11616 * @file poster-image.js
11617 */
11618/**
11619 * A `ClickableComponent` that handles showing the poster image for the player.
11620 *
11621 * @extends ClickableComponent
11622 */
11623
11624var PosterImage = function (_ClickableComponent) {
11625  inherits(PosterImage, _ClickableComponent);
11626
11627  /**
11628   * Create an instance of this class.
11629   *
11630   * @param {Player} player
11631   *        The `Player` that this class should attach to.
11632   *
11633   * @param {Object} [options]
11634   *        The key/value store of player options.
11635   */
11636  function PosterImage(player, options) {
11637    classCallCheck(this, PosterImage);
11638
11639    var _this = possibleConstructorReturn(this, _ClickableComponent.call(this, player, options));
11640
11641    _this.update();
11642    player.on('posterchange', bind(_this, _this.update));
11643    return _this;
11644  }
11645
11646  /**
11647   * Clean up and dispose of the `PosterImage`.
11648   */
11649
11650
11651  PosterImage.prototype.dispose = function dispose() {
11652    this.player().off('posterchange', this.update);
11653    _ClickableComponent.prototype.dispose.call(this);
11654  };
11655
11656  /**
11657   * Create the `PosterImage`s DOM element.
11658   *
11659   * @return {Element}
11660   *         The element that gets created.
11661   */
11662
11663
11664  PosterImage.prototype.createEl = function createEl$$1() {
11665    var el = createEl('div', {
11666      className: 'vjs-poster',
11667
11668      // Don't want poster to be tabbable.
11669      tabIndex: -1
11670    });
11671
11672    // To ensure the poster image resizes while maintaining its original aspect
11673    // ratio, use a div with `background-size` when available. For browsers that
11674    // do not support `background-size` (e.g. IE8), fall back on using a regular
11675    // img element.
11676    if (!BACKGROUND_SIZE_SUPPORTED) {
11677      this.fallbackImg_ = createEl('img');
11678      el.appendChild(this.fallbackImg_);
11679    }
11680
11681    return el;
11682  };
11683
11684  /**
11685   * An {@link EventTarget~EventListener} for {@link Player#posterchange} events.
11686   *
11687   * @listens Player#posterchange
11688   *
11689   * @param {EventTarget~Event} [event]
11690   *        The `Player#posterchange` event that triggered this function.
11691   */
11692
11693
11694  PosterImage.prototype.update = function update(event) {
11695    var url = this.player().poster();
11696
11697    this.setSrc(url);
11698
11699    // If there's no poster source we should display:none on this component
11700    // so it's not still clickable or right-clickable
11701    if (url) {
11702      this.show();
11703    } else {
11704      this.hide();
11705    }
11706  };
11707
11708  /**
11709   * Set the source of the `PosterImage` depending on the display method.
11710   *
11711   * @param {string} url
11712   *        The URL to the source for the `PosterImage`.
11713   */
11714
11715
11716  PosterImage.prototype.setSrc = function setSrc(url) {
11717    if (this.fallbackImg_) {
11718      this.fallbackImg_.src = url;
11719    } else {
11720      var backgroundImage = '';
11721
11722      // Any falsey values should stay as an empty string, otherwise
11723      // this will throw an extra error
11724      if (url) {
11725        backgroundImage = 'url("' + url + '")';
11726      }
11727
11728      this.el_.style.backgroundImage = backgroundImage;
11729    }
11730  };
11731
11732  /**
11733   * An {@link EventTarget~EventListener} for clicks on the `PosterImage`. See
11734   * {@link ClickableComponent#handleClick} for instances where this will be triggered.
11735   *
11736   * @listens tap
11737   * @listens click
11738   * @listens keydown
11739   *
11740   * @param {EventTarget~Event} event
11741   +        The `click`, `tap` or `keydown` event that caused this function to be called.
11742   */
11743
11744
11745  PosterImage.prototype.handleClick = function handleClick(event) {
11746    // We don't want a click to trigger playback when controls are disabled
11747    if (!this.player_.controls()) {
11748      return;
11749    }
11750
11751    if (this.player_.paused()) {
11752      this.player_.play();
11753    } else {
11754      this.player_.pause();
11755    }
11756  };
11757
11758  return PosterImage;
11759}(ClickableComponent);
11760
11761Component.registerComponent('PosterImage', PosterImage);
11762
11763/**
11764 * @file text-track-display.js
11765 */
11766var darkGray = '#222';
11767var lightGray = '#ccc';
11768var fontMap = {
11769  monospace: 'monospace',
11770  sansSerif: 'sans-serif',
11771  serif: 'serif',
11772  monospaceSansSerif: '"Andale Mono", "Lucida Console", monospace',
11773  monospaceSerif: '"Courier New", monospace',
11774  proportionalSansSerif: 'sans-serif',
11775  proportionalSerif: 'serif',
11776  casual: '"Comic Sans MS", Impact, fantasy',
11777  script: '"Monotype Corsiva", cursive',
11778  smallcaps: '"Andale Mono", "Lucida Console", monospace, sans-serif'
11779};
11780
11781/**
11782 * Construct an rgba color from a given hex color code.
11783 *
11784 * @param {number} color
11785 *        Hex number for color, like #f0e.
11786 *
11787 * @param {number} opacity
11788 *        Value for opacity, 0.0 - 1.0.
11789 *
11790 * @return {string}
11791 *         The rgba color that was created, like 'rgba(255, 0, 0, 0.3)'.
11792 *
11793 * @private
11794 */
11795function constructColor(color, opacity) {
11796  return 'rgba(' +
11797  // color looks like "#f0e"
11798  parseInt(color[1] + color[1], 16) + ',' + parseInt(color[2] + color[2], 16) + ',' + parseInt(color[3] + color[3], 16) + ',' + opacity + ')';
11799}
11800
11801/**
11802 * Try to update the style of a DOM element. Some style changes will throw an error,
11803 * particularly in IE8. Those should be noops.
11804 *
11805 * @param {Element} el
11806 *        The DOM element to be styled.
11807 *
11808 * @param {string} style
11809 *        The CSS property on the element that should be styled.
11810 *
11811 * @param {string} rule
11812 *        The style rule that should be applied to the property.
11813 *
11814 * @private
11815 */
11816function tryUpdateStyle(el, style, rule) {
11817  try {
11818    el.style[style] = rule;
11819  } catch (e) {
11820
11821    // Satisfies linter.
11822    return;
11823  }
11824}
11825
11826/**
11827 * The component for displaying text track cues.
11828 *
11829 * @extends Component
11830 */
11831
11832var TextTrackDisplay = function (_Component) {
11833  inherits(TextTrackDisplay, _Component);
11834
11835  /**
11836   * Creates an instance of this class.
11837   *
11838   * @param {Player} player
11839   *        The `Player` that this class should be attached to.
11840   *
11841   * @param {Object} [options]
11842   *        The key/value store of player options.
11843   *
11844   * @param {Component~ReadyCallback} [ready]
11845   *        The function to call when `TextTrackDisplay` is ready.
11846   */
11847  function TextTrackDisplay(player, options, ready) {
11848    classCallCheck(this, TextTrackDisplay);
11849
11850    var _this = possibleConstructorReturn(this, _Component.call(this, player, options, ready));
11851
11852    player.on('loadstart', bind(_this, _this.toggleDisplay));
11853    player.on('texttrackchange', bind(_this, _this.updateDisplay));
11854    player.on('loadstart', bind(_this, _this.preselectTrack));
11855
11856    // This used to be called during player init, but was causing an error
11857    // if a track should show by default and the display hadn't loaded yet.
11858    // Should probably be moved to an external track loader when we support
11859    // tracks that don't need a display.
11860    player.ready(bind(_this, function () {
11861      if (player.tech_ && player.tech_.featuresNativeTextTracks) {
11862        this.hide();
11863        return;
11864      }
11865
11866      player.on('fullscreenchange', bind(this, this.updateDisplay));
11867
11868      var tracks = this.options_.playerOptions.tracks || [];
11869
11870      for (var i = 0; i < tracks.length; i++) {
11871        this.player_.addRemoteTextTrack(tracks[i], true);
11872      }
11873
11874      this.preselectTrack();
11875    }));
11876    return _this;
11877  }
11878
11879  /**
11880  * Preselect a track following this precedence:
11881  * - matches the previously selected {@link TextTrack}'s language and kind
11882  * - matches the previously selected {@link TextTrack}'s language only
11883  * - is the first default captions track
11884  * - is the first default descriptions track
11885  *
11886  * @listens Player#loadstart
11887  */
11888
11889
11890  TextTrackDisplay.prototype.preselectTrack = function preselectTrack() {
11891    var modes = { captions: 1, subtitles: 1 };
11892    var trackList = this.player_.textTracks();
11893    var userPref = this.player_.cache_.selectedLanguage;
11894    var firstDesc = void 0;
11895    var firstCaptions = void 0;
11896    var preferredTrack = void 0;
11897
11898    for (var i = 0; i < trackList.length; i++) {
11899      var track = trackList[i];
11900
11901      if (userPref && userPref.enabled && userPref.language === track.language) {
11902        // Always choose the track that matches both language and kind
11903        if (track.kind === userPref.kind) {
11904          preferredTrack = track;
11905          // or choose the first track that matches language
11906        } else if (!preferredTrack) {
11907          preferredTrack = track;
11908        }
11909
11910        // clear everything if offTextTrackMenuItem was clicked
11911      } else if (userPref && !userPref.enabled) {
11912        preferredTrack = null;
11913        firstDesc = null;
11914        firstCaptions = null;
11915      } else if (track['default']) {
11916        if (track.kind === 'descriptions' && !firstDesc) {
11917          firstDesc = track;
11918        } else if (track.kind in modes && !firstCaptions) {
11919          firstCaptions = track;
11920        }
11921      }
11922    }
11923
11924    // The preferredTrack matches the user preference and takes
11925    // precendence over all the other tracks.
11926    // So, display the preferredTrack before the first default track
11927    // and the subtitles/captions track before the descriptions track
11928    if (preferredTrack) {
11929      preferredTrack.mode = 'showing';
11930    } else if (firstCaptions) {
11931      firstCaptions.mode = 'showing';
11932    } else if (firstDesc) {
11933      firstDesc.mode = 'showing';
11934    }
11935  };
11936
11937  /**
11938   * Turn display of {@link TextTrack}'s from the current state into the other state.
11939   * There are only two states:
11940   * - 'shown'
11941   * - 'hidden'
11942   *
11943   * @listens Player#loadstart
11944   */
11945
11946
11947  TextTrackDisplay.prototype.toggleDisplay = function toggleDisplay() {
11948    if (this.player_.tech_ && this.player_.tech_.featuresNativeTextTracks) {
11949      this.hide();
11950    } else {
11951      this.show();
11952    }
11953  };
11954
11955  /**
11956   * Create the {@link Component}'s DOM element.
11957   *
11958   * @return {Element}
11959   *         The element that was created.
11960   */
11961
11962
11963  TextTrackDisplay.prototype.createEl = function createEl() {
11964    return _Component.prototype.createEl.call(this, 'div', {
11965      className: 'vjs-text-track-display'
11966    }, {
11967      'aria-live': 'off',
11968      'aria-atomic': 'true'
11969    });
11970  };
11971
11972  /**
11973   * Clear all displayed {@link TextTrack}s.
11974   */
11975
11976
11977  TextTrackDisplay.prototype.clearDisplay = function clearDisplay() {
11978    if (typeof window_1.WebVTT === 'function') {
11979      window_1.WebVTT.processCues(window_1, [], this.el_);
11980    }
11981  };
11982
11983  /**
11984   * Update the displayed TextTrack when a either a {@link Player#texttrackchange} or
11985   * a {@link Player#fullscreenchange} is fired.
11986   *
11987   * @listens Player#texttrackchange
11988   * @listens Player#fullscreenchange
11989   */
11990
11991
11992  TextTrackDisplay.prototype.updateDisplay = function updateDisplay() {
11993    var tracks = this.player_.textTracks();
11994
11995    this.clearDisplay();
11996
11997    // Track display prioritization model: if multiple tracks are 'showing',
11998    //  display the first 'subtitles' or 'captions' track which is 'showing',
11999    //  otherwise display the first 'descriptions' track which is 'showing'
12000
12001    var descriptionsTrack = null;
12002    var captionsSubtitlesTrack = null;
12003    var i = tracks.length;
12004
12005    while (i--) {
12006      var track = tracks[i];
12007
12008      if (track.mode === 'showing') {
12009        if (track.kind === 'descriptions') {
12010          descriptionsTrack = track;
12011        } else {
12012          captionsSubtitlesTrack = track;
12013        }
12014      }
12015    }
12016
12017    if (captionsSubtitlesTrack) {
12018      if (this.getAttribute('aria-live') !== 'off') {
12019        this.setAttribute('aria-live', 'off');
12020      }
12021      this.updateForTrack(captionsSubtitlesTrack);
12022    } else if (descriptionsTrack) {
12023      if (this.getAttribute('aria-live') !== 'assertive') {
12024        this.setAttribute('aria-live', 'assertive');
12025      }
12026      this.updateForTrack(descriptionsTrack);
12027    }
12028  };
12029
12030  /**
12031   * Add an {@link Texttrack} to to the {@link Tech}s {@link TextTrackList}.
12032   *
12033   * @param {TextTrack} track
12034   *        Text track object to be added to the list.
12035   */
12036
12037
12038  TextTrackDisplay.prototype.updateForTrack = function updateForTrack(track) {
12039    if (typeof window_1.WebVTT !== 'function' || !track.activeCues) {
12040      return;
12041    }
12042
12043    var overrides = this.player_.textTrackSettings.getValues();
12044    var cues = [];
12045
12046    for (var _i = 0; _i < track.activeCues.length; _i++) {
12047      cues.push(track.activeCues[_i]);
12048    }
12049
12050    window_1.WebVTT.processCues(window_1, cues, this.el_);
12051
12052    var i = cues.length;
12053
12054    while (i--) {
12055      var cue = cues[i];
12056
12057      if (!cue) {
12058        continue;
12059      }
12060
12061      var cueDiv = cue.displayState;
12062
12063      if (overrides.color) {
12064        cueDiv.firstChild.style.color = overrides.color;
12065      }
12066      if (overrides.textOpacity) {
12067        tryUpdateStyle(cueDiv.firstChild, 'color', constructColor(overrides.color || '#fff', overrides.textOpacity));
12068      }
12069      if (overrides.backgroundColor) {
12070        cueDiv.firstChild.style.backgroundColor = overrides.backgroundColor;
12071      }
12072      if (overrides.backgroundOpacity) {
12073        tryUpdateStyle(cueDiv.firstChild, 'backgroundColor', constructColor(overrides.backgroundColor || '#000', overrides.backgroundOpacity));
12074      }
12075      if (overrides.windowColor) {
12076        if (overrides.windowOpacity) {
12077          tryUpdateStyle(cueDiv, 'backgroundColor', constructColor(overrides.windowColor, overrides.windowOpacity));
12078        } else {
12079          cueDiv.style.backgroundColor = overrides.windowColor;
12080        }
12081      }
12082      if (overrides.edgeStyle) {
12083        if (overrides.edgeStyle === 'dropshadow') {
12084          cueDiv.firstChild.style.textShadow = '2px 2px 3px ' + darkGray + ', 2px 2px 4px ' + darkGray + ', 2px 2px 5px ' + darkGray;
12085        } else if (overrides.edgeStyle === 'raised') {
12086          cueDiv.firstChild.style.textShadow = '1px 1px ' + darkGray + ', 2px 2px ' + darkGray + ', 3px 3px ' + darkGray;
12087        } else if (overrides.edgeStyle === 'depressed') {
12088          cueDiv.firstChild.style.textShadow = '1px 1px ' + lightGray + ', 0 1px ' + lightGray + ', -1px -1px ' + darkGray + ', 0 -1px ' + darkGray;
12089        } else if (overrides.edgeStyle === 'uniform') {
12090          cueDiv.firstChild.style.textShadow = '0 0 4px ' + darkGray + ', 0 0 4px ' + darkGray + ', 0 0 4px ' + darkGray + ', 0 0 4px ' + darkGray;
12091        }
12092      }
12093      if (overrides.fontPercent && overrides.fontPercent !== 1) {
12094        var fontSize = window_1.parseFloat(cueDiv.style.fontSize);
12095
12096        cueDiv.style.fontSize = fontSize * overrides.fontPercent + 'px';
12097        cueDiv.style.height = 'auto';
12098        cueDiv.style.top = 'auto';
12099        cueDiv.style.bottom = '2px';
12100      }
12101      if (overrides.fontFamily && overrides.fontFamily !== 'default') {
12102        if (overrides.fontFamily === 'small-caps') {
12103          cueDiv.firstChild.style.fontVariant = 'small-caps';
12104        } else {
12105          cueDiv.firstChild.style.fontFamily = fontMap[overrides.fontFamily];
12106        }
12107      }
12108    }
12109  };
12110
12111  return TextTrackDisplay;
12112}(Component);
12113
12114Component.registerComponent('TextTrackDisplay', TextTrackDisplay);
12115
12116/**
12117 * @file loading-spinner.js
12118 */
12119/**
12120 * A loading spinner for use during waiting/loading events.
12121 *
12122 * @extends Component
12123 */
12124
12125var LoadingSpinner = function (_Component) {
12126  inherits(LoadingSpinner, _Component);
12127
12128  function LoadingSpinner() {
12129    classCallCheck(this, LoadingSpinner);
12130    return possibleConstructorReturn(this, _Component.apply(this, arguments));
12131  }
12132
12133  /**
12134   * Create the `LoadingSpinner`s DOM element.
12135   *
12136   * @return {Element}
12137   *         The dom element that gets created.
12138   */
12139  LoadingSpinner.prototype.createEl = function createEl() {
12140    return _Component.prototype.createEl.call(this, 'div', {
12141      className: 'vjs-loading-spinner',
12142      dir: 'ltr'
12143    });
12144  };
12145
12146  return LoadingSpinner;
12147}(Component);
12148
12149Component.registerComponent('LoadingSpinner', LoadingSpinner);
12150
12151/**
12152 * @file button.js
12153 */
12154/**
12155 * Base class for all buttons.
12156 *
12157 * @extends ClickableComponent
12158 */
12159
12160var Button = function (_ClickableComponent) {
12161  inherits(Button, _ClickableComponent);
12162
12163  function Button() {
12164    classCallCheck(this, Button);
12165    return possibleConstructorReturn(this, _ClickableComponent.apply(this, arguments));
12166  }
12167
12168  /**
12169   * Create the `Button`s DOM element.
12170   *
12171   * @param {string} [tag="button"]
12172   *        The element's node type. This argument is IGNORED: no matter what
12173   *        is passed, it will always create a `button` element.
12174   *
12175   * @param {Object} [props={}]
12176   *        An object of properties that should be set on the element.
12177   *
12178   * @param {Object} [attributes={}]
12179   *        An object of attributes that should be set on the element.
12180   *
12181   * @return {Element}
12182   *         The element that gets created.
12183   */
12184  Button.prototype.createEl = function createEl(tag) {
12185    var props = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
12186    var attributes = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
12187
12188    tag = 'button';
12189
12190    props = assign({
12191      innerHTML: '<span aria-hidden="true" class="vjs-icon-placeholder"></span>',
12192      className: this.buildCSSClass()
12193    }, props);
12194
12195    // Add attributes for button element
12196    attributes = assign({
12197
12198      // Necessary since the default button type is "submit"
12199      'type': 'button',
12200
12201      // let the screen reader user know that the text of the button may change
12202      'aria-live': 'polite'
12203    }, attributes);
12204
12205    var el = Component.prototype.createEl.call(this, tag, props, attributes);
12206
12207    this.createControlTextEl(el);
12208
12209    return el;
12210  };
12211
12212  /**
12213   * Add a child `Component` inside of this `Button`.
12214   *
12215   * @param {string|Component} child
12216   *        The name or instance of a child to add.
12217   *
12218   * @param {Object} [options={}]
12219   *        The key/value store of options that will get passed to children of
12220   *        the child.
12221   *
12222   * @return {Component}
12223   *         The `Component` that gets added as a child. When using a string the
12224   *         `Component` will get created by this process.
12225   *
12226   * @deprecated since version 5
12227   */
12228
12229
12230  Button.prototype.addChild = function addChild(child) {
12231    var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
12232
12233    var className = this.constructor.name;
12234
12235    log$1.warn('Adding an actionable (user controllable) child to a Button (' + className + ') is not supported; use a ClickableComponent instead.');
12236
12237    // Avoid the error message generated by ClickableComponent's addChild method
12238    return Component.prototype.addChild.call(this, child, options);
12239  };
12240
12241  /**
12242   * Enable the `Button` element so that it can be activated or clicked. Use this with
12243   * {@link Button#disable}.
12244   */
12245
12246
12247  Button.prototype.enable = function enable() {
12248    _ClickableComponent.prototype.enable.call(this);
12249    this.el_.removeAttribute('disabled');
12250  };
12251
12252  /**
12253   * Enable the `Button` element so that it cannot be activated or clicked. Use this with
12254   * {@link Button#enable}.
12255   */
12256
12257
12258  Button.prototype.disable = function disable() {
12259    _ClickableComponent.prototype.disable.call(this);
12260    this.el_.setAttribute('disabled', 'disabled');
12261  };
12262
12263  /**
12264   * This gets called when a `Button` has focus and `keydown` is triggered via a key
12265   * press.
12266   *
12267   * @param {EventTarget~Event} event
12268   *        The event that caused this function to get called.
12269   *
12270   * @listens keydown
12271   */
12272
12273
12274  Button.prototype.handleKeyPress = function handleKeyPress(event) {
12275
12276    // Ignore Space (32) or Enter (13) key operation, which is handled by the browser for a button.
12277    if (event.which === 32 || event.which === 13) {
12278      return;
12279    }
12280
12281    // Pass keypress handling up for unsupported keys
12282    _ClickableComponent.prototype.handleKeyPress.call(this, event);
12283  };
12284
12285  return Button;
12286}(ClickableComponent);
12287
12288Component.registerComponent('Button', Button);
12289
12290/**
12291 * @file big-play-button.js
12292 */
12293/**
12294 * The initial play button that shows before the video has played. The hiding of the
12295 * `BigPlayButton` get done via CSS and `Player` states.
12296 *
12297 * @extends Button
12298 */
12299
12300var BigPlayButton = function (_Button) {
12301  inherits(BigPlayButton, _Button);
12302
12303  function BigPlayButton(player, options) {
12304    classCallCheck(this, BigPlayButton);
12305
12306    var _this = possibleConstructorReturn(this, _Button.call(this, player, options));
12307
12308    _this.mouseused_ = false;
12309
12310    _this.on('mousedown', _this.handleMouseDown);
12311    return _this;
12312  }
12313
12314  /**
12315   * Builds the default DOM `className`.
12316   *
12317   * @return {string}
12318   *         The DOM `className` for this object. Always returns 'vjs-big-play-button'.
12319   */
12320
12321
12322  BigPlayButton.prototype.buildCSSClass = function buildCSSClass() {
12323    return 'vjs-big-play-button';
12324  };
12325
12326  /**
12327   * This gets called when a `BigPlayButton` "clicked". See {@link ClickableComponent}
12328   * for more detailed information on what a click can be.
12329   *
12330   * @param {EventTarget~Event} event
12331   *        The `keydown`, `tap`, or `click` event that caused this function to be
12332   *        called.
12333   *
12334   * @listens tap
12335   * @listens click
12336   */
12337
12338
12339  BigPlayButton.prototype.handleClick = function handleClick(event) {
12340    var playPromise = this.player_.play();
12341
12342    // exit early if clicked via the mouse
12343    if (this.mouseused_ && event.clientX && event.clientY) {
12344      return;
12345    }
12346
12347    var cb = this.player_.getChild('controlBar');
12348    var playToggle = cb && cb.getChild('playToggle');
12349
12350    if (!playToggle) {
12351      this.player_.focus();
12352      return;
12353    }
12354
12355    var playFocus = function playFocus() {
12356      return playToggle.focus();
12357    };
12358
12359    if (playPromise && playPromise.then) {
12360      var ignoreRejectedPlayPromise = function ignoreRejectedPlayPromise() {};
12361
12362      playPromise.then(playFocus, ignoreRejectedPlayPromise);
12363    } else {
12364      this.setTimeout(playFocus, 1);
12365    }
12366  };
12367
12368  BigPlayButton.prototype.handleKeyPress = function handleKeyPress(event) {
12369    this.mouseused_ = false;
12370
12371    _Button.prototype.handleKeyPress.call(this, event);
12372  };
12373
12374  BigPlayButton.prototype.handleMouseDown = function handleMouseDown(event) {
12375    this.mouseused_ = true;
12376  };
12377
12378  return BigPlayButton;
12379}(Button);
12380
12381/**
12382 * The text that should display over the `BigPlayButton`s controls. Added to for localization.
12383 *
12384 * @type {string}
12385 * @private
12386 */
12387
12388
12389BigPlayButton.prototype.controlText_ = 'Play Video';
12390
12391Component.registerComponent('BigPlayButton', BigPlayButton);
12392
12393/**
12394 * @file close-button.js
12395 */
12396/**
12397 * The `CloseButton` is a `{@link Button}` that fires a `close` event when
12398 * it gets clicked.
12399 *
12400 * @extends Button
12401 */
12402
12403var CloseButton = function (_Button) {
12404  inherits(CloseButton, _Button);
12405
12406  /**
12407   * Creates an instance of the this class.
12408   *
12409   * @param  {Player} player
12410   *         The `Player` that this class should be attached to.
12411   *
12412   * @param  {Object} [options]
12413   *         The key/value store of player options.
12414   */
12415  function CloseButton(player, options) {
12416    classCallCheck(this, CloseButton);
12417
12418    var _this = possibleConstructorReturn(this, _Button.call(this, player, options));
12419
12420    _this.controlText(options && options.controlText || _this.localize('Close'));
12421    return _this;
12422  }
12423
12424  /**
12425   * Builds the default DOM `className`.
12426   *
12427   * @return {string}
12428   *         The DOM `className` for this object.
12429   */
12430
12431
12432  CloseButton.prototype.buildCSSClass = function buildCSSClass() {
12433    return 'vjs-close-button ' + _Button.prototype.buildCSSClass.call(this);
12434  };
12435
12436  /**
12437   * This gets called when a `CloseButton` gets clicked. See
12438   * {@link ClickableComponent#handleClick} for more information on when this will be
12439   * triggered
12440   *
12441   * @param {EventTarget~Event} event
12442   *        The `keydown`, `tap`, or `click` event that caused this function to be
12443   *        called.
12444   *
12445   * @listens tap
12446   * @listens click
12447   * @fires CloseButton#close
12448   */
12449
12450
12451  CloseButton.prototype.handleClick = function handleClick(event) {
12452
12453    /**
12454     * Triggered when the a `CloseButton` is clicked.
12455     *
12456     * @event CloseButton#close
12457     * @type {EventTarget~Event}
12458     *
12459     * @property {boolean} [bubbles=false]
12460     *           set to false so that the close event does not
12461     *           bubble up to parents if there is no listener
12462     */
12463    this.trigger({ type: 'close', bubbles: false });
12464  };
12465
12466  return CloseButton;
12467}(Button);
12468
12469Component.registerComponent('CloseButton', CloseButton);
12470
12471/**
12472 * @file play-toggle.js
12473 */
12474/**
12475 * Button to toggle between play and pause.
12476 *
12477 * @extends Button
12478 */
12479
12480var PlayToggle = function (_Button) {
12481  inherits(PlayToggle, _Button);
12482
12483  /**
12484   * Creates an instance of this class.
12485   *
12486   * @param {Player} player
12487   *        The `Player` that this class should be attached to.
12488   *
12489   * @param {Object} [options]
12490   *        The key/value store of player options.
12491   */
12492  function PlayToggle(player, options) {
12493    classCallCheck(this, PlayToggle);
12494
12495    var _this = possibleConstructorReturn(this, _Button.call(this, player, options));
12496
12497    _this.on(player, 'play', _this.handlePlay);
12498    _this.on(player, 'pause', _this.handlePause);
12499    _this.on(player, 'ended', _this.handleEnded);
12500    return _this;
12501  }
12502
12503  /**
12504   * Builds the default DOM `className`.
12505   *
12506   * @return {string}
12507   *         The DOM `className` for this object.
12508   */
12509
12510
12511  PlayToggle.prototype.buildCSSClass = function buildCSSClass() {
12512    return 'vjs-play-control ' + _Button.prototype.buildCSSClass.call(this);
12513  };
12514
12515  /**
12516   * This gets called when an `PlayToggle` is "clicked". See
12517   * {@link ClickableComponent} for more detailed information on what a click can be.
12518   *
12519   * @param {EventTarget~Event} [event]
12520   *        The `keydown`, `tap`, or `click` event that caused this function to be
12521   *        called.
12522   *
12523   * @listens tap
12524   * @listens click
12525   */
12526
12527
12528  PlayToggle.prototype.handleClick = function handleClick(event) {
12529    if (this.player_.paused()) {
12530      this.player_.play();
12531    } else {
12532      this.player_.pause();
12533    }
12534  };
12535
12536  /**
12537   * Add the vjs-playing class to the element so it can change appearance.
12538   *
12539   * @param {EventTarget~Event} [event]
12540   *        The event that caused this function to run.
12541   *
12542   * @listens Player#play
12543   */
12544
12545
12546  PlayToggle.prototype.handlePlay = function handlePlay(event) {
12547    this.removeClass('vjs-ended');
12548    this.removeClass('vjs-paused');
12549    this.addClass('vjs-playing');
12550    // change the button text to "Pause"
12551    this.controlText('Pause');
12552  };
12553
12554  /**
12555   * Add the vjs-paused class to the element so it can change appearance.
12556   *
12557   * @param {EventTarget~Event} [event]
12558   *        The event that caused this function to run.
12559   *
12560   * @listens Player#pause
12561   */
12562
12563
12564  PlayToggle.prototype.handlePause = function handlePause(event) {
12565    this.removeClass('vjs-playing');
12566    this.addClass('vjs-paused');
12567    // change the button text to "Play"
12568    this.controlText('Play');
12569  };
12570
12571  /**
12572   * Add the vjs-ended class to the element so it can change appearance
12573   *
12574   */
12575
12576
12577  PlayToggle.prototype.handleEnded = function handleEnded(event) {
12578    this.removeClass('vjs-playing');
12579    this.addClass('vjs-ended');
12580    // change the button text to "Replay"
12581    this.controlText('Replay');
12582  };
12583
12584  return PlayToggle;
12585}(Button);
12586
12587/**
12588 * The text that should display over the `PlayToggle`s controls. Added for localization.
12589 *
12590 * @type {string}
12591 * @private
12592 */
12593
12594
12595PlayToggle.prototype.controlText_ = 'Play';
12596
12597Component.registerComponent('PlayToggle', PlayToggle);
12598
12599/**
12600 * @file format-time.js
12601 * @module Format-time
12602 */
12603
12604/**
12605 * Format seconds as a time string, H:MM:SS or M:SS. Supplying a guide (in seconds)
12606 * will force a number of leading zeros to cover the length of the guide.
12607 *
12608 * @param {number} seconds
12609 *        Number of seconds to be turned into a string
12610 *
12611 * @param {number} guide
12612 *        Number (in seconds) to model the string after
12613 *
12614 * @return {string}
12615 *         Time formatted as H:MM:SS or M:SS
12616 */
12617function formatTime(seconds) {
12618  var guide = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : seconds;
12619
12620  seconds = seconds < 0 ? 0 : seconds;
12621  var s = Math.floor(seconds % 60);
12622  var m = Math.floor(seconds / 60 % 60);
12623  var h = Math.floor(seconds / 3600);
12624  var gm = Math.floor(guide / 60 % 60);
12625  var gh = Math.floor(guide / 3600);
12626
12627  // handle invalid times
12628  if (isNaN(seconds) || seconds === Infinity) {
12629    // '-' is false for all relational operators (e.g. <, >=) so this setting
12630    // will add the minimum number of fields specified by the guide
12631    h = m = s = '-';
12632  }
12633
12634  // Check if we need to show hours
12635  h = h > 0 || gh > 0 ? h + ':' : '';
12636
12637  // If hours are showing, we may need to add a leading zero.
12638  // Always show at least one digit of minutes.
12639  m = ((h || gm >= 10) && m < 10 ? '0' + m : m) + ':';
12640
12641  // Check if leading zero is need for seconds
12642  s = s < 10 ? '0' + s : s;
12643
12644  return h + m + s;
12645}
12646
12647/**
12648 * @file current-time-display.js
12649 */
12650/**
12651 * Displays the current time
12652 *
12653 * @extends Component
12654 */
12655
12656var CurrentTimeDisplay = function (_Component) {
12657  inherits(CurrentTimeDisplay, _Component);
12658
12659  /**
12660   * Creates an instance of this class.
12661   *
12662   * @param {Player} player
12663   *        The `Player` that this class should be attached to.
12664   *
12665   * @param {Object} [options]
12666   *        The key/value store of player options.
12667   */
12668  function CurrentTimeDisplay(player, options) {
12669    classCallCheck(this, CurrentTimeDisplay);
12670
12671    var _this = possibleConstructorReturn(this, _Component.call(this, player, options));
12672
12673    _this.throttledUpdateContent = throttle(bind(_this, _this.updateContent), 25);
12674    _this.on(player, 'timeupdate', _this.throttledUpdateContent);
12675    return _this;
12676  }
12677
12678  /**
12679   * Create the `Component`'s DOM element
12680   *
12681   * @return {Element}
12682   *         The element that was created.
12683   */
12684
12685
12686  CurrentTimeDisplay.prototype.createEl = function createEl$$1() {
12687    var el = _Component.prototype.createEl.call(this, 'div', {
12688      className: 'vjs-current-time vjs-time-control vjs-control'
12689    });
12690
12691    this.contentEl_ = createEl('div', {
12692      className: 'vjs-current-time-display'
12693    }, {
12694      // tell screen readers not to automatically read the time as it changes
12695      'aria-live': 'off'
12696    }, createEl('span', {
12697      className: 'vjs-control-text',
12698      textContent: this.localize('Current Time')
12699    }));
12700
12701    this.updateTextNode_();
12702    el.appendChild(this.contentEl_);
12703    return el;
12704  };
12705
12706  /**
12707   * Updates the "current time" text node with new content using the
12708   * contents of the `formattedTime_` property.
12709   *
12710   * @private
12711   */
12712
12713
12714  CurrentTimeDisplay.prototype.updateTextNode_ = function updateTextNode_() {
12715    if (this.textNode_) {
12716      this.contentEl_.removeChild(this.textNode_);
12717    }
12718    this.textNode_ = document_1.createTextNode(' ' + (this.formattedTime_ || '0:00'));
12719    this.contentEl_.appendChild(this.textNode_);
12720  };
12721
12722  /**
12723   * Update current time display
12724   *
12725   * @param {EventTarget~Event} [event]
12726   *        The `timeupdate` event that caused this function to run.
12727   *
12728   * @listens Player#timeupdate
12729   */
12730
12731
12732  CurrentTimeDisplay.prototype.updateContent = function updateContent(event) {
12733    // Allows for smooth scrubbing, when player can't keep up.
12734    var time = this.player_.scrubbing() ? this.player_.getCache().currentTime : this.player_.currentTime();
12735    var formattedTime = formatTime(time, this.player_.duration());
12736
12737    if (formattedTime !== this.formattedTime_) {
12738      this.formattedTime_ = formattedTime;
12739      this.requestAnimationFrame(this.updateTextNode_);
12740    }
12741  };
12742
12743  return CurrentTimeDisplay;
12744}(Component);
12745
12746Component.registerComponent('CurrentTimeDisplay', CurrentTimeDisplay);
12747
12748/**
12749 * @file duration-display.js
12750 */
12751/**
12752 * Displays the duration
12753 *
12754 * @extends Component
12755 */
12756
12757var DurationDisplay = function (_Component) {
12758  inherits(DurationDisplay, _Component);
12759
12760  /**
12761   * Creates an instance of this class.
12762   *
12763   * @param {Player} player
12764   *        The `Player` that this class should be attached to.
12765   *
12766   * @param {Object} [options]
12767   *        The key/value store of player options.
12768   */
12769  function DurationDisplay(player, options) {
12770    classCallCheck(this, DurationDisplay);
12771
12772    var _this = possibleConstructorReturn(this, _Component.call(this, player, options));
12773
12774    _this.throttledUpdateContent = throttle(bind(_this, _this.updateContent), 25);
12775
12776    _this.on(player, ['durationchange',
12777
12778    // Also listen for timeupdate and loadedmetadata because removing those
12779    // listeners could have broken dependent applications/libraries. These
12780    // can likely be removed for 7.0.
12781    'loadedmetadata', 'timeupdate'], _this.throttledUpdateContent);
12782    return _this;
12783  }
12784
12785  /**
12786   * Create the `Component`'s DOM element
12787   *
12788   * @return {Element}
12789   *         The element that was created.
12790   */
12791
12792
12793  DurationDisplay.prototype.createEl = function createEl$$1() {
12794    var el = _Component.prototype.createEl.call(this, 'div', {
12795      className: 'vjs-duration vjs-time-control vjs-control'
12796    });
12797
12798    this.contentEl_ = createEl('div', {
12799      className: 'vjs-duration-display'
12800    }, {
12801      // tell screen readers not to automatically read the time as it changes
12802      'aria-live': 'off'
12803    }, createEl('span', {
12804      className: 'vjs-control-text',
12805      textContent: this.localize('Duration Time')
12806    }));
12807
12808    this.updateTextNode_();
12809    el.appendChild(this.contentEl_);
12810    return el;
12811  };
12812
12813  /**
12814   * Updates the "current time" text node with new content using the
12815   * contents of the `formattedTime_` property.
12816   *
12817   * @private
12818   */
12819
12820
12821  DurationDisplay.prototype.updateTextNode_ = function updateTextNode_() {
12822    if (this.textNode_) {
12823      this.contentEl_.removeChild(this.textNode_);
12824    }
12825    this.textNode_ = document_1.createTextNode(' ' + (this.formattedTime_ || '0:00'));
12826    this.contentEl_.appendChild(this.textNode_);
12827  };
12828
12829  /**
12830   * Update duration time display.
12831   *
12832   * @param {EventTarget~Event} [event]
12833   *        The `durationchange`, `timeupdate`, or `loadedmetadata` event that caused
12834   *        this function to be called.
12835   *
12836   * @listens Player#durationchange
12837   * @listens Player#timeupdate
12838   * @listens Player#loadedmetadata
12839   */
12840
12841
12842  DurationDisplay.prototype.updateContent = function updateContent(event) {
12843    var duration = this.player_.duration();
12844
12845    if (duration && this.duration_ !== duration) {
12846      this.duration_ = duration;
vendor: 183,937 bytes, lines 12847-19547
12847      this.formattedTime_ = formatTime(duration);
12848      this.requestAnimationFrame(this.updateTextNode_);
12849    }
12850  };
12851
12852  return DurationDisplay;
12853}(Component);
12854
12855Component.registerComponent('DurationDisplay', DurationDisplay);
12856
12857/**
12858 * @file time-divider.js
12859 */
12860/**
12861 * The separator between the current time and duration.
12862 * Can be hidden if it's not needed in the design.
12863 *
12864 * @extends Component
12865 */
12866
12867var TimeDivider = function (_Component) {
12868  inherits(TimeDivider, _Component);
12869
12870  function TimeDivider() {
12871    classCallCheck(this, TimeDivider);
12872    return possibleConstructorReturn(this, _Component.apply(this, arguments));
12873  }
12874
12875  /**
12876   * Create the component's DOM element
12877   *
12878   * @return {Element}
12879   *         The element that was created.
12880   */
12881  TimeDivider.prototype.createEl = function createEl() {
12882    return _Component.prototype.createEl.call(this, 'div', {
12883      className: 'vjs-time-control vjs-time-divider',
12884      innerHTML: '<div><span>/</span></div>'
12885    });
12886  };
12887
12888  return TimeDivider;
12889}(Component);
12890
12891Component.registerComponent('TimeDivider', TimeDivider);
12892
12893/**
12894 * @file remaining-time-display.js
12895 */
12896/**
12897 * Displays the time left in the video
12898 *
12899 * @extends Component
12900 */
12901
12902var RemainingTimeDisplay = function (_Component) {
12903  inherits(RemainingTimeDisplay, _Component);
12904
12905  /**
12906   * Creates an instance of this class.
12907   *
12908   * @param {Player} player
12909   *        The `Player` that this class should be attached to.
12910   *
12911   * @param {Object} [options]
12912   *        The key/value store of player options.
12913   */
12914  function RemainingTimeDisplay(player, options) {
12915    classCallCheck(this, RemainingTimeDisplay);
12916
12917    var _this = possibleConstructorReturn(this, _Component.call(this, player, options));
12918
12919    _this.throttledUpdateContent = throttle(bind(_this, _this.updateContent), 25);
12920    _this.on(player, ['timeupdate', 'durationchange'], _this.throttledUpdateContent);
12921    return _this;
12922  }
12923
12924  /**
12925   * Create the `Component`'s DOM element
12926   *
12927   * @return {Element}
12928   *         The element that was created.
12929   */
12930
12931
12932  RemainingTimeDisplay.prototype.createEl = function createEl$$1() {
12933    var el = _Component.prototype.createEl.call(this, 'div', {
12934      className: 'vjs-remaining-time vjs-time-control vjs-control'
12935    });
12936
12937    this.contentEl_ = createEl('div', {
12938      className: 'vjs-remaining-time-display'
12939    }, {
12940      // tell screen readers not to automatically read the time as it changes
12941      'aria-live': 'off'
12942    }, createEl('span', {
12943      className: 'vjs-control-text',
12944      textContent: this.localize('Remaining Time')
12945    }));
12946
12947    this.updateTextNode_();
12948    el.appendChild(this.contentEl_);
12949    return el;
12950  };
12951
12952  /**
12953   * Updates the "remaining time" text node with new content using the
12954   * contents of the `formattedTime_` property.
12955   *
12956   * @private
12957   */
12958
12959
12960  RemainingTimeDisplay.prototype.updateTextNode_ = function updateTextNode_() {
12961    if (this.textNode_) {
12962      this.contentEl_.removeChild(this.textNode_);
12963    }
12964    this.textNode_ = document_1.createTextNode(' -' + (this.formattedTime_ || '0:00'));
12965    this.contentEl_.appendChild(this.textNode_);
12966  };
12967
12968  /**
12969   * Update remaining time display.
12970   *
12971   * @param {EventTarget~Event} [event]
12972   *        The `timeupdate` or `durationchange` event that caused this to run.
12973   *
12974   * @listens Player#timeupdate
12975   * @listens Player#durationchange
12976   */
12977
12978
12979  RemainingTimeDisplay.prototype.updateContent = function updateContent(event) {
12980    if (this.player_.duration()) {
12981      var formattedTime = formatTime(this.player_.remainingTime());
12982
12983      if (formattedTime !== this.formattedTime_) {
12984        this.formattedTime_ = formattedTime;
12985        this.requestAnimationFrame(this.updateTextNode_);
12986      }
12987    }
12988  };
12989
12990  return RemainingTimeDisplay;
12991}(Component);
12992
12993Component.registerComponent('RemainingTimeDisplay', RemainingTimeDisplay);
12994
12995/**
12996 * @file live-display.js
12997 */
12998// TODO - Future make it click to snap to live
12999
13000/**
13001 * Displays the live indicator when duration is Infinity.
13002 *
13003 * @extends Component
13004 */
13005
13006var LiveDisplay = function (_Component) {
13007  inherits(LiveDisplay, _Component);
13008
13009  /**
13010   * Creates an instance of this class.
13011   *
13012   * @param {Player} player
13013   *        The `Player` that this class should be attached to.
13014   *
13015   * @param {Object} [options]
13016   *        The key/value store of player options.
13017   */
13018  function LiveDisplay(player, options) {
13019    classCallCheck(this, LiveDisplay);
13020
13021    var _this = possibleConstructorReturn(this, _Component.call(this, player, options));
13022
13023    _this.updateShowing();
13024    _this.on(_this.player(), 'durationchange', _this.updateShowing);
13025    return _this;
13026  }
13027
13028  /**
13029   * Create the `Component`'s DOM element
13030   *
13031   * @return {Element}
13032   *         The element that was created.
13033   */
13034
13035
13036  LiveDisplay.prototype.createEl = function createEl$$1() {
13037    var el = _Component.prototype.createEl.call(this, 'div', {
13038      className: 'vjs-live-control vjs-control'
13039    });
13040
13041    this.contentEl_ = createEl('div', {
13042      className: 'vjs-live-display',
13043      innerHTML: '<span class="vjs-control-text">' + this.localize('Stream Type') + '</span>' + this.localize('LIVE')
13044    }, {
13045      'aria-live': 'off'
13046    });
13047
13048    el.appendChild(this.contentEl_);
13049    return el;
13050  };
13051
13052  /**
13053   * Check the duration to see if the LiveDisplay should be showing or not. Then show/hide
13054   * it accordingly
13055   *
13056   * @param {EventTarget~Event} [event]
13057   *        The {@link Player#durationchange} event that caused this function to run.
13058   *
13059   * @listens Player#durationchange
13060   */
13061
13062
13063  LiveDisplay.prototype.updateShowing = function updateShowing(event) {
13064    if (this.player().duration() === Infinity) {
13065      this.show();
13066    } else {
13067      this.hide();
13068    }
13069  };
13070
13071  return LiveDisplay;
13072}(Component);
13073
13074Component.registerComponent('LiveDisplay', LiveDisplay);
13075
13076/**
13077 * @file slider.js
13078 */
13079/**
13080 * The base functionality for a slider. Can be vertical or horizontal.
13081 * For instance the volume bar or the seek bar on a video is a slider.
13082 *
13083 * @extends Component
13084 */
13085
13086var Slider = function (_Component) {
13087  inherits(Slider, _Component);
13088
13089  /**
13090   * Create an instance of this class
13091   *
13092   * @param {Player} player
13093   *        The `Player` that this class should be attached to.
13094   *
13095   * @param {Object} [options]
13096   *        The key/value store of player options.
13097   */
13098  function Slider(player, options) {
13099    classCallCheck(this, Slider);
13100
13101    // Set property names to bar to match with the child Slider class is looking for
13102    var _this = possibleConstructorReturn(this, _Component.call(this, player, options));
13103
13104    _this.bar = _this.getChild(_this.options_.barName);
13105
13106    // Set a horizontal or vertical class on the slider depending on the slider type
13107    _this.vertical(!!_this.options_.vertical);
13108
13109    _this.on('mousedown', _this.handleMouseDown);
13110    _this.on('touchstart', _this.handleMouseDown);
13111    _this.on('focus', _this.handleFocus);
13112    _this.on('blur', _this.handleBlur);
13113    _this.on('click', _this.handleClick);
13114
13115    _this.on(player, 'controlsvisible', _this.update);
13116
13117    if (_this.playerEvent) {
13118      _this.on(player, _this.playerEvent, _this.update);
13119    }
13120    return _this;
13121  }
13122
13123  /**
13124   * Create the `Button`s DOM element.
13125   *
13126   * @param {string} type
13127   *        Type of element to create.
13128   *
13129   * @param {Object} [props={}]
13130   *        List of properties in Object form.
13131   *
13132   * @param {Object} [attributes={}]
13133   *        list of attributes in Object form.
13134   *
13135   * @return {Element}
13136   *         The element that gets created.
13137   */
13138
13139
13140  Slider.prototype.createEl = function createEl$$1(type) {
13141    var props = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
13142    var attributes = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
13143
13144    // Add the slider element class to all sub classes
13145    props.className = props.className + ' vjs-slider';
13146    props = assign({
13147      tabIndex: 0
13148    }, props);
13149
13150    attributes = assign({
13151      'role': 'slider',
13152      'aria-valuenow': 0,
13153      'aria-valuemin': 0,
13154      'aria-valuemax': 100,
13155      'tabIndex': 0
13156    }, attributes);
13157
13158    return _Component.prototype.createEl.call(this, type, props, attributes);
13159  };
13160
13161  /**
13162   * Handle `mousedown` or `touchstart` events on the `Slider`.
13163   *
13164   * @param {EventTarget~Event} event
13165   *        `mousedown` or `touchstart` event that triggered this function
13166   *
13167   * @listens mousedown
13168   * @listens touchstart
13169   * @fires Slider#slideractive
13170   */
13171
13172
13173  Slider.prototype.handleMouseDown = function handleMouseDown(event) {
13174    var doc = this.bar.el_.ownerDocument;
13175
13176    event.preventDefault();
13177    blockTextSelection();
13178
13179    this.addClass('vjs-sliding');
13180    /**
13181     * Triggered when the slider is in an active state
13182     *
13183     * @event Slider#slideractive
13184     * @type {EventTarget~Event}
13185     */
13186    this.trigger('slideractive');
13187
13188    this.on(doc, 'mousemove', this.handleMouseMove);
13189    this.on(doc, 'mouseup', this.handleMouseUp);
13190    this.on(doc, 'touchmove', this.handleMouseMove);
13191    this.on(doc, 'touchend', this.handleMouseUp);
13192
13193    this.handleMouseMove(event);
13194  };
13195
13196  /**
13197   * Handle the `mousemove`, `touchmove`, and `mousedown` events on this `Slider`.
13198   * The `mousemove` and `touchmove` events will only only trigger this function during
13199   * `mousedown` and `touchstart`. This is due to {@link Slider#handleMouseDown} and
13200   * {@link Slider#handleMouseUp}.
13201   *
13202   * @param {EventTarget~Event} event
13203   *        `mousedown`, `mousemove`, `touchstart`, or `touchmove` event that triggered
13204   *        this function
13205   *
13206   * @listens mousemove
13207   * @listens touchmove
13208   */
13209
13210
13211  Slider.prototype.handleMouseMove = function handleMouseMove(event) {};
13212
13213  /**
13214   * Handle `mouseup` or `touchend` events on the `Slider`.
13215   *
13216   * @param {EventTarget~Event} event
13217   *        `mouseup` or `touchend` event that triggered this function.
13218   *
13219   * @listens touchend
13220   * @listens mouseup
13221   * @fires Slider#sliderinactive
13222   */
13223
13224
13225  Slider.prototype.handleMouseUp = function handleMouseUp() {
13226    var doc = this.bar.el_.ownerDocument;
13227
13228    unblockTextSelection();
13229
13230    this.removeClass('vjs-sliding');
13231    /**
13232     * Triggered when the slider is no longer in an active state.
13233     *
13234     * @event Slider#sliderinactive
13235     * @type {EventTarget~Event}
13236     */
13237    this.trigger('sliderinactive');
13238
13239    this.off(doc, 'mousemove', this.handleMouseMove);
13240    this.off(doc, 'mouseup', this.handleMouseUp);
13241    this.off(doc, 'touchmove', this.handleMouseMove);
13242    this.off(doc, 'touchend', this.handleMouseUp);
13243
13244    this.update();
13245  };
13246
13247  /**
13248   * Update the progress bar of the `Slider`.
13249   *
13250   * @returns {number}
13251   *          The percentage of progress the progress bar represents as a
13252   *          number from 0 to 1.
13253   */
13254
13255
13256  Slider.prototype.update = function update() {
13257
13258    // In VolumeBar init we have a setTimeout for update that pops and update
13259    // to the end of the execution stack. The player is destroyed before then
13260    // update will cause an error
13261    if (!this.el_) {
13262      return;
13263    }
13264
13265    // If scrubbing, we could use a cached value to make the handle keep up
13266    // with the user's mouse. On HTML5 browsers scrubbing is really smooth, but
13267    // some flash players are slow, so we might want to utilize this later.
13268    // var progress =  (this.player_.scrubbing()) ? this.player_.getCache().currentTime / this.player_.duration() : this.player_.currentTime() / this.player_.duration();
13269    var progress = this.getPercent();
13270    var bar = this.bar;
13271
13272    // If there's no bar...
13273    if (!bar) {
13274      return;
13275    }
13276
13277    // Protect against no duration and other division issues
13278    if (typeof progress !== 'number' || progress !== progress || progress < 0 || progress === Infinity) {
13279      progress = 0;
13280    }
13281
13282    // Convert to a percentage for setting
13283    var percentage = (progress * 100).toFixed(2) + '%';
13284    var style = bar.el().style;
13285
13286    // Set the new bar width or height
13287    if (this.vertical()) {
13288      style.height = percentage;
13289    } else {
13290      style.width = percentage;
13291    }
13292
13293    return progress;
13294  };
13295
13296  /**
13297   * Calculate distance for slider
13298   *
13299   * @param {EventTarget~Event} event
13300   *        The event that caused this function to run.
13301   *
13302   * @return {number}
13303   *         The current position of the Slider.
13304   *         - postition.x for vertical `Slider`s
13305   *         - postition.y for horizontal `Slider`s
13306   */
13307
13308
13309  Slider.prototype.calculateDistance = function calculateDistance(event) {
13310    var position = getPointerPosition(this.el_, event);
13311
13312    if (this.vertical()) {
13313      return position.y;
13314    }
13315    return position.x;
13316  };
13317
13318  /**
13319   * Handle a `focus` event on this `Slider`.
13320   *
13321   * @param {EventTarget~Event} event
13322   *        The `focus` event that caused this function to run.
13323   *
13324   * @listens focus
13325   */
13326
13327
13328  Slider.prototype.handleFocus = function handleFocus() {
13329    this.on(this.bar.el_.ownerDocument, 'keydown', this.handleKeyPress);
13330  };
13331
13332  /**
13333   * Handle a `keydown` event on the `Slider`. Watches for left, rigth, up, and down
13334   * arrow keys. This function will only be called when the slider has focus. See
13335   * {@link Slider#handleFocus} and {@link Slider#handleBlur}.
13336   *
13337   * @param {EventTarget~Event} event
13338   *        the `keydown` event that caused this function to run.
13339   *
13340   * @listens keydown
13341   */
13342
13343
13344  Slider.prototype.handleKeyPress = function handleKeyPress(event) {
13345    // Left and Down Arrows
13346    if (event.which === 37 || event.which === 40) {
13347      event.preventDefault();
13348      this.stepBack();
13349
13350      // Up and Right Arrows
13351    } else if (event.which === 38 || event.which === 39) {
13352      event.preventDefault();
13353      this.stepForward();
13354    }
13355  };
13356
13357  /**
13358   * Handle a `blur` event on this `Slider`.
13359   *
13360   * @param {EventTarget~Event} event
13361   *        The `blur` event that caused this function to run.
13362   *
13363   * @listens blur
13364   */
13365
13366  Slider.prototype.handleBlur = function handleBlur() {
13367    this.off(this.bar.el_.ownerDocument, 'keydown', this.handleKeyPress);
13368  };
13369
13370  /**
13371   * Listener for click events on slider, used to prevent clicks
13372   *   from bubbling up to parent elements like button menus.
13373   *
13374   * @param {Object} event
13375   *        Event that caused this object to run
13376   */
13377
13378
13379  Slider.prototype.handleClick = function handleClick(event) {
13380    event.stopImmediatePropagation();
13381    event.preventDefault();
13382  };
13383
13384  /**
13385   * Get/set if slider is horizontal for vertical
13386   *
13387   * @param {boolean} [bool]
13388   *        - true if slider is vertical,
13389   *        - false is horizontal
13390   *
13391   * @return {boolean}
13392   *         - true if slider is vertical, and getting
13393   *         - false if the slider is horizontal, and getting
13394   */
13395
13396
13397  Slider.prototype.vertical = function vertical(bool) {
13398    if (bool === undefined) {
13399      return this.vertical_ || false;
13400    }
13401
13402    this.vertical_ = !!bool;
13403
13404    if (this.vertical_) {
13405      this.addClass('vjs-slider-vertical');
13406    } else {
13407      this.addClass('vjs-slider-horizontal');
13408    }
13409  };
13410
13411  return Slider;
13412}(Component);
13413
13414Component.registerComponent('Slider', Slider);
13415
13416/**
13417 * @file load-progress-bar.js
13418 */
13419/**
13420 * Shows loading progress
13421 *
13422 * @extends Component
13423 */
13424
13425var LoadProgressBar = function (_Component) {
13426  inherits(LoadProgressBar, _Component);
13427
13428  /**
13429   * Creates an instance of this class.
13430   *
13431   * @param {Player} player
13432   *        The `Player` that this class should be attached to.
13433   *
13434   * @param {Object} [options]
13435   *        The key/value store of player options.
13436   */
13437  function LoadProgressBar(player, options) {
13438    classCallCheck(this, LoadProgressBar);
13439
13440    var _this = possibleConstructorReturn(this, _Component.call(this, player, options));
13441
13442    _this.partEls_ = [];
13443    _this.on(player, 'progress', _this.update);
13444    return _this;
13445  }
13446
13447  /**
13448   * Create the `Component`'s DOM element
13449   *
13450   * @return {Element}
13451   *         The element that was created.
13452   */
13453
13454
13455  LoadProgressBar.prototype.createEl = function createEl$$1() {
13456    return _Component.prototype.createEl.call(this, 'div', {
13457      className: 'vjs-load-progress',
13458      innerHTML: '<span class="vjs-control-text"><span>' + this.localize('Loaded') + '</span>: 0%</span>'
13459    });
13460  };
13461
13462  /**
13463   * Update progress bar
13464   *
13465   * @param {EventTarget~Event} [event]
13466   *        The `progress` event that caused this function to run.
13467   *
13468   * @listens Player#progress
13469   */
13470
13471
13472  LoadProgressBar.prototype.update = function update(event) {
13473    var buffered = this.player_.buffered();
13474    var duration = this.player_.duration();
13475    var bufferedEnd = this.player_.bufferedEnd();
13476    var children = this.partEls_;
13477
13478    // get the percent width of a time compared to the total end
13479    var percentify = function percentify(time, end) {
13480      // no NaN
13481      var percent = time / end || 0;
13482
13483      return (percent >= 1 ? 1 : percent) * 100 + '%';
13484    };
13485
13486    // update the width of the progress bar
13487    this.el_.style.width = percentify(bufferedEnd, duration);
13488
13489    // add child elements to represent the individual buffered time ranges
13490    for (var i = 0; i < buffered.length; i++) {
13491      var start = buffered.start(i);
13492      var end = buffered.end(i);
13493      var part = children[i];
13494
13495      if (!part) {
13496        part = this.el_.appendChild(createEl());
13497        children[i] = part;
13498      }
13499
13500      // set the percent based on the width of the progress bar (bufferedEnd)
13501      part.style.left = percentify(start, bufferedEnd);
13502      part.style.width = percentify(end - start, bufferedEnd);
13503    }
13504
13505    // remove unused buffered range elements
13506    for (var _i = children.length; _i > buffered.length; _i--) {
13507      this.el_.removeChild(children[_i - 1]);
13508    }
13509    children.length = buffered.length;
13510  };
13511
13512  return LoadProgressBar;
13513}(Component);
13514
13515Component.registerComponent('LoadProgressBar', LoadProgressBar);
13516
13517/**
13518 * @file time-tooltip.js
13519 */
13520/**
13521 * Time tooltips display a time above the progress bar.
13522 *
13523 * @extends Component
13524 */
13525
13526var TimeTooltip = function (_Component) {
13527  inherits(TimeTooltip, _Component);
13528
13529  function TimeTooltip() {
13530    classCallCheck(this, TimeTooltip);
13531    return possibleConstructorReturn(this, _Component.apply(this, arguments));
13532  }
13533
13534  /**
13535   * Create the time tooltip DOM element
13536   *
13537   * @return {Element}
13538   *         The element that was created.
13539   */
13540  TimeTooltip.prototype.createEl = function createEl$$1() {
13541    return _Component.prototype.createEl.call(this, 'div', {
13542      className: 'vjs-time-tooltip'
13543    });
13544  };
13545
13546  /**
13547   * Updates the position of the time tooltip relative to the `SeekBar`.
13548   *
13549   * @param {Object} seekBarRect
13550   *        The `ClientRect` for the {@link SeekBar} element.
13551   *
13552   * @param {number} seekBarPoint
13553   *        A number from 0 to 1, representing a horizontal reference point
13554   *        from the left edge of the {@link SeekBar}
13555   */
13556
13557
13558  TimeTooltip.prototype.update = function update(seekBarRect, seekBarPoint, content) {
13559    var tooltipRect = getBoundingClientRect(this.el_);
13560    var playerRect = getBoundingClientRect(this.player_.el());
13561    var seekBarPointPx = seekBarRect.width * seekBarPoint;
13562
13563    // do nothing if either rect isn't available
13564    // for example, if the player isn't in the DOM for testing
13565    if (!playerRect || !tooltipRect) {
13566      return;
13567    }
13568
13569    // This is the space left of the `seekBarPoint` available within the bounds
13570    // of the player. We calculate any gap between the left edge of the player
13571    // and the left edge of the `SeekBar` and add the number of pixels in the
13572    // `SeekBar` before hitting the `seekBarPoint`
13573    var spaceLeftOfPoint = seekBarRect.left - playerRect.left + seekBarPointPx;
13574
13575    // This is the space right of the `seekBarPoint` available within the bounds
13576    // of the player. We calculate the number of pixels from the `seekBarPoint`
13577    // to the right edge of the `SeekBar` and add to that any gap between the
13578    // right edge of the `SeekBar` and the player.
13579    var spaceRightOfPoint = seekBarRect.width - seekBarPointPx + (playerRect.right - seekBarRect.right);
13580
13581    // This is the number of pixels by which the tooltip will need to be pulled
13582    // further to the right to center it over the `seekBarPoint`.
13583    var pullTooltipBy = tooltipRect.width / 2;
13584
13585    // Adjust the `pullTooltipBy` distance to the left or right depending on
13586    // the results of the space calculations above.
13587    if (spaceLeftOfPoint < pullTooltipBy) {
13588      pullTooltipBy += pullTooltipBy - spaceLeftOfPoint;
13589    } else if (spaceRightOfPoint < pullTooltipBy) {
13590      pullTooltipBy = spaceRightOfPoint;
13591    }
13592
13593    // Due to the imprecision of decimal/ratio based calculations and varying
13594    // rounding behaviors, there are cases where the spacing adjustment is off
13595    // by a pixel or two. This adds insurance to these calculations.
13596    if (pullTooltipBy < 0) {
13597      pullTooltipBy = 0;
13598    } else if (pullTooltipBy > tooltipRect.width) {
13599      pullTooltipBy = tooltipRect.width;
13600    }
13601
13602    this.el_.style.right = '-' + pullTooltipBy + 'px';
13603    textContent(this.el_, content);
13604  };
13605
13606  return TimeTooltip;
13607}(Component);
13608
13609Component.registerComponent('TimeTooltip', TimeTooltip);
13610
13611/**
13612 * @file play-progress-bar.js
13613 */
13614/**
13615 * Used by {@link SeekBar} to display media playback progress as part of the
13616 * {@link ProgressControl}.
13617 *
13618 * @extends Component
13619 */
13620
13621var PlayProgressBar = function (_Component) {
13622  inherits(PlayProgressBar, _Component);
13623
13624  function PlayProgressBar() {
13625    classCallCheck(this, PlayProgressBar);
13626    return possibleConstructorReturn(this, _Component.apply(this, arguments));
13627  }
13628
13629  /**
13630   * Create the the DOM element for this class.
13631   *
13632   * @return {Element}
13633   *         The element that was created.
13634   */
13635  PlayProgressBar.prototype.createEl = function createEl() {
13636    return _Component.prototype.createEl.call(this, 'div', {
13637      className: 'vjs-play-progress vjs-slider-bar',
13638      innerHTML: '<span class="vjs-control-text"><span>' + this.localize('Progress') + '</span>: 0%</span>'
13639    });
13640  };
13641
13642  /**
13643   * Enqueues updates to its own DOM as well as the DOM of its
13644   * {@link TimeTooltip} child.
13645   *
13646   * @param {Object} seekBarRect
13647   *        The `ClientRect` for the {@link SeekBar} element.
13648   *
13649   * @param {number} seekBarPoint
13650   *        A number from 0 to 1, representing a horizontal reference point
13651   *        from the left edge of the {@link SeekBar}
13652   */
13653
13654
13655  PlayProgressBar.prototype.update = function update(seekBarRect, seekBarPoint) {
13656    var _this2 = this;
13657
13658    // If there is an existing rAF ID, cancel it so we don't over-queue.
13659    if (this.rafId_) {
13660      this.cancelAnimationFrame(this.rafId_);
13661    }
13662
13663    this.rafId_ = this.requestAnimationFrame(function () {
13664      var time = _this2.player_.scrubbing() ? _this2.player_.getCache().currentTime : _this2.player_.currentTime();
13665
13666      var content = formatTime(time, _this2.player_.duration());
13667      var timeTooltip = _this2.getChild('timeTooltip');
13668
13669      if (timeTooltip) {
13670        timeTooltip.update(seekBarRect, seekBarPoint, content);
13671      }
13672    });
13673  };
13674
13675  return PlayProgressBar;
13676}(Component);
13677
13678/**
13679 * Default options for {@link PlayProgressBar}.
13680 *
13681 * @type {Object}
13682 * @private
13683 */
13684
13685
13686PlayProgressBar.prototype.options_ = {
13687  children: []
13688};
13689
13690// Time tooltips should not be added to a player on mobile devices or IE8
13691if ((!IE_VERSION || IE_VERSION > 8) && !IS_IOS && !IS_ANDROID) {
13692  PlayProgressBar.prototype.options_.children.push('timeTooltip');
13693}
13694
13695Component.registerComponent('PlayProgressBar', PlayProgressBar);
13696
13697/**
13698 * @file mouse-time-display.js
13699 */
13700/**
13701 * The {@link MouseTimeDisplay} component tracks mouse movement over the
13702 * {@link ProgressControl}. It displays an indicator and a {@link TimeTooltip}
13703 * indicating the time which is represented by a given point in the
13704 * {@link ProgressControl}.
13705 *
13706 * @extends Component
13707 */
13708
13709var MouseTimeDisplay = function (_Component) {
13710  inherits(MouseTimeDisplay, _Component);
13711
13712  /**
13713   * Creates an instance of this class.
13714   *
13715   * @param {Player} player
13716   *        The {@link Player} that this class should be attached to.
13717   *
13718   * @param {Object} [options]
13719   *        The key/value store of player options.
13720   */
13721  function MouseTimeDisplay(player, options) {
13722    classCallCheck(this, MouseTimeDisplay);
13723
13724    var _this = possibleConstructorReturn(this, _Component.call(this, player, options));
13725
13726    _this.update = throttle(bind(_this, _this.update), 25);
13727    return _this;
13728  }
13729
13730  /**
13731   * Create the DOM element for this class.
13732   *
13733   * @return {Element}
13734   *         The element that was created.
13735   */
13736
13737
13738  MouseTimeDisplay.prototype.createEl = function createEl() {
13739    return _Component.prototype.createEl.call(this, 'div', {
13740      className: 'vjs-mouse-display'
13741    });
13742  };
13743
13744  /**
13745   * Enqueues updates to its own DOM as well as the DOM of its
13746   * {@link TimeTooltip} child.
13747   *
13748   * @param {Object} seekBarRect
13749   *        The `ClientRect` for the {@link SeekBar} element.
13750   *
13751   * @param {number} seekBarPoint
13752   *        A number from 0 to 1, representing a horizontal reference point
13753   *        from the left edge of the {@link SeekBar}
13754   */
13755
13756
13757  MouseTimeDisplay.prototype.update = function update(seekBarRect, seekBarPoint) {
13758    var _this2 = this;
13759
13760    // If there is an existing rAF ID, cancel it so we don't over-queue.
13761    if (this.rafId_) {
13762      this.cancelAnimationFrame(this.rafId_);
13763    }
13764
13765    this.rafId_ = this.requestAnimationFrame(function () {
13766      var duration = _this2.player_.duration();
13767      var content = formatTime(seekBarPoint * duration, duration);
13768
13769      _this2.el_.style.left = seekBarRect.width * seekBarPoint + 'px';
13770      _this2.getChild('timeTooltip').update(seekBarRect, seekBarPoint, content);
13771    });
13772  };
13773
13774  return MouseTimeDisplay;
13775}(Component);
13776
13777/**
13778 * Default options for `MouseTimeDisplay`
13779 *
13780 * @type {Object}
13781 * @private
13782 */
13783
13784
13785MouseTimeDisplay.prototype.options_ = {
13786  children: ['timeTooltip']
13787};
13788
13789Component.registerComponent('MouseTimeDisplay', MouseTimeDisplay);
13790
13791/**
13792 * @file seek-bar.js
13793 */
13794// The number of seconds the `step*` functions move the timeline.
13795var STEP_SECONDS = 5;
13796
13797/**
13798 * Seek bar and container for the progress bars. Uses {@link PlayProgressBar}
13799 * as its `bar`.
13800 *
13801 * @extends Slider
13802 */
13803
13804var SeekBar = function (_Slider) {
13805  inherits(SeekBar, _Slider);
13806
13807  /**
13808   * Creates an instance of this class.
13809   *
13810   * @param {Player} player
13811   *        The `Player` that this class should be attached to.
13812   *
13813   * @param {Object} [options]
13814   *        The key/value store of player options.
13815   */
13816  function SeekBar(player, options) {
13817    classCallCheck(this, SeekBar);
13818
13819    var _this = possibleConstructorReturn(this, _Slider.call(this, player, options));
13820
13821    _this.update = throttle(bind(_this, _this.update), 50);
13822    _this.on(player, ['timeupdate', 'ended'], _this.update);
13823    return _this;
13824  }
13825
13826  /**
13827   * Create the `Component`'s DOM element
13828   *
13829   * @return {Element}
13830   *         The element that was created.
13831   */
13832
13833
13834  SeekBar.prototype.createEl = function createEl$$1() {
13835    return _Slider.prototype.createEl.call(this, 'div', {
13836      className: 'vjs-progress-holder'
13837    }, {
13838      'aria-label': this.localize('Progress Bar')
13839    });
13840  };
13841
13842  /**
13843   * Update the seek bar's UI.
13844   *
13845   * @param {EventTarget~Event} [event]
13846   *        The `timeupdate` or `ended` event that caused this to run.
13847   *
13848   * @listens Player#timeupdate
13849   * @listens Player#ended
13850   */
13851
13852
13853  SeekBar.prototype.update = function update() {
13854    var percent = _Slider.prototype.update.call(this);
13855    var duration = this.player_.duration();
13856
13857    // Allows for smooth scrubbing, when player can't keep up.
13858    var time = this.player_.scrubbing() ? this.player_.getCache().currentTime : this.player_.currentTime();
13859
13860    // machine readable value of progress bar (percentage complete)
13861    this.el_.setAttribute('aria-valuenow', (percent * 100).toFixed(2));
13862
13863    // human readable value of progress bar (time complete)
13864    this.el_.setAttribute('aria-valuetext', this.localize('progress bar timing: currentTime={1} duration={2}', [formatTime(time, duration), formatTime(duration, duration)], '{1} of {2}'));
13865
13866    // Update the `PlayProgressBar`.
13867    this.bar.update(getBoundingClientRect(this.el_), percent);
13868
13869    return percent;
13870  };
13871
13872  /**
13873   * Get the percentage of media played so far.
13874   *
13875   * @return {number}
13876   *         The percentage of media played so far (0 to 1).
13877   */
13878
13879
13880  SeekBar.prototype.getPercent = function getPercent() {
13881
13882    // Allows for smooth scrubbing, when player can't keep up.
13883    var time = this.player_.scrubbing() ? this.player_.getCache().currentTime : this.player_.currentTime();
13884
13885    var percent = time / this.player_.duration();
13886
13887    return percent >= 1 ? 1 : percent;
13888  };
13889
13890  /**
13891   * Handle mouse down on seek bar
13892   *
13893   * @param {EventTarget~Event} event
13894   *        The `mousedown` event that caused this to run.
13895   *
13896   * @listens mousedown
13897   */
13898
13899
13900  SeekBar.prototype.handleMouseDown = function handleMouseDown(event) {
13901    this.player_.scrubbing(true);
13902
13903    this.videoWasPlaying = !this.player_.paused();
13904    this.player_.pause();
13905
13906    _Slider.prototype.handleMouseDown.call(this, event);
13907  };
13908
13909  /**
13910   * Handle mouse move on seek bar
13911   *
13912   * @param {EventTarget~Event} event
13913   *        The `mousemove` event that caused this to run.
13914   *
13915   * @listens mousemove
13916   */
13917
13918
13919  SeekBar.prototype.handleMouseMove = function handleMouseMove(event) {
13920    var newTime = this.calculateDistance(event) * this.player_.duration();
13921
13922    // Don't let video end while scrubbing.
13923    if (newTime === this.player_.duration()) {
13924      newTime = newTime - 0.1;
13925    }
13926
13927    // Set new time (tell player to seek to new time)
13928    this.player_.currentTime(newTime);
13929  };
13930
13931  /**
13932   * Handle mouse up on seek bar
13933   *
13934   * @param {EventTarget~Event} event
13935   *        The `mouseup` event that caused this to run.
13936   *
13937   * @listens mouseup
13938   */
13939
13940
13941  SeekBar.prototype.handleMouseUp = function handleMouseUp(event) {
13942    _Slider.prototype.handleMouseUp.call(this, event);
13943
13944    this.player_.scrubbing(false);
13945    if (this.videoWasPlaying) {
13946      this.player_.play();
13947    }
13948  };
13949
13950  /**
13951   * Move more quickly fast forward for keyboard-only users
13952   */
13953
13954
13955  SeekBar.prototype.stepForward = function stepForward() {
13956    this.player_.currentTime(this.player_.currentTime() + STEP_SECONDS);
13957  };
13958
13959  /**
13960   * Move more quickly rewind for keyboard-only users
13961   */
13962
13963
13964  SeekBar.prototype.stepBack = function stepBack() {
13965    this.player_.currentTime(this.player_.currentTime() - STEP_SECONDS);
13966  };
13967
13968  /**
13969   * Toggles the playback state of the player
13970   * This gets called when enter or space is used on the seekbar
13971   *
13972   * @param {EventTarget~Event} event
13973   *        The `keydown` event that caused this function to be called
13974   *
13975   */
13976
13977
13978  SeekBar.prototype.handleAction = function handleAction(event) {
13979    if (this.player_.paused()) {
13980      this.player_.play();
13981    } else {
13982      this.player_.pause();
13983    }
13984  };
13985
13986  /**
13987   * Called when this SeekBar has focus and a key gets pressed down. By
13988   * default it will call `this.handleAction` when the key is space or enter.
13989   *
13990   * @param {EventTarget~Event} event
13991   *        The `keydown` event that caused this function to be called.
13992   *
13993   * @listens keydown
13994   */
13995
13996
13997  SeekBar.prototype.handleKeyPress = function handleKeyPress(event) {
13998
13999    // Support Space (32) or Enter (13) key operation to fire a click event
14000    if (event.which === 32 || event.which === 13) {
14001      event.preventDefault();
14002      this.handleAction(event);
14003    } else if (_Slider.prototype.handleKeyPress) {
14004
14005      // Pass keypress handling up for unsupported keys
14006      _Slider.prototype.handleKeyPress.call(this, event);
14007    }
14008  };
14009
14010  return SeekBar;
14011}(Slider);
14012
14013/**
14014 * Default options for the `SeekBar`
14015 *
14016 * @type {Object}
14017 * @private
14018 */
14019
14020
14021SeekBar.prototype.options_ = {
14022  children: ['loadProgressBar', 'playProgressBar'],
14023  barName: 'playProgressBar'
14024};
14025
14026// MouseTimeDisplay tooltips should not be added to a player on mobile devices or IE8
14027if ((!IE_VERSION || IE_VERSION > 8) && !IS_IOS && !IS_ANDROID) {
14028  SeekBar.prototype.options_.children.splice(1, 0, 'mouseTimeDisplay');
14029}
14030
14031/**
14032 * Call the update event for this Slider when this event happens on the player.
14033 *
14034 * @type {string}
14035 */
14036SeekBar.prototype.playerEvent = 'timeupdate';
14037
14038Component.registerComponent('SeekBar', SeekBar);
14039
14040/**
14041 * @file progress-control.js
14042 */
14043/**
14044 * The Progress Control component contains the seek bar, load progress,
14045 * and play progress.
14046 *
14047 * @extends Component
14048 */
14049
14050var ProgressControl = function (_Component) {
14051  inherits(ProgressControl, _Component);
14052
14053  /**
14054   * Creates an instance of this class.
14055   *
14056   * @param {Player} player
14057   *        The `Player` that this class should be attached to.
14058   *
14059   * @param {Object} [options]
14060   *        The key/value store of player options.
14061   */
14062  function ProgressControl(player, options) {
14063    classCallCheck(this, ProgressControl);
14064
14065    var _this = possibleConstructorReturn(this, _Component.call(this, player, options));
14066
14067    _this.handleMouseMove = throttle(bind(_this, _this.handleMouseMove), 25);
14068    _this.on(_this.el_, 'mousemove', _this.handleMouseMove);
14069
14070    _this.throttledHandleMouseSeek = throttle(bind(_this, _this.handleMouseSeek), 25);
14071    _this.on(['mousedown', 'touchstart'], _this.handleMouseDown);
14072    return _this;
14073  }
14074
14075  /**
14076   * Create the `Component`'s DOM element
14077   *
14078   * @return {Element}
14079   *         The element that was created.
14080   */
14081
14082
14083  ProgressControl.prototype.createEl = function createEl$$1() {
14084    return _Component.prototype.createEl.call(this, 'div', {
14085      className: 'vjs-progress-control vjs-control'
14086    });
14087  };
14088
14089  /**
14090   * When the mouse moves over the `ProgressControl`, the pointer position
14091   * gets passed down to the `MouseTimeDisplay` component.
14092   *
14093   * @param {EventTarget~Event} event
14094   *        The `mousemove` event that caused this function to run.
14095   *
14096   * @listen mousemove
14097   */
14098
14099
14100  ProgressControl.prototype.handleMouseMove = function handleMouseMove(event) {
14101    var seekBar = this.getChild('seekBar');
14102    var mouseTimeDisplay = seekBar.getChild('mouseTimeDisplay');
14103    var seekBarEl = seekBar.el();
14104    var seekBarRect = getBoundingClientRect(seekBarEl);
14105    var seekBarPoint = getPointerPosition(seekBarEl, event).x;
14106
14107    // The default skin has a gap on either side of the `SeekBar`. This means
14108    // that it's possible to trigger this behavior outside the boundaries of
14109    // the `SeekBar`. This ensures we stay within it at all times.
14110    if (seekBarPoint > 1) {
14111      seekBarPoint = 1;
14112    } else if (seekBarPoint < 0) {
14113      seekBarPoint = 0;
14114    }
14115
14116    if (mouseTimeDisplay) {
14117      mouseTimeDisplay.update(seekBarRect, seekBarPoint);
14118    }
14119  };
14120
14121  /**
14122   * A throttled version of the {@link ProgressControl#handleMouseSeek} listener.
14123   *
14124   * @method ProgressControl#throttledHandleMouseSeek
14125   * @param {EventTarget~Event} event
14126   *        The `mousemove` event that caused this function to run.
14127   *
14128   * @listen mousemove
14129   * @listen touchmove
14130   */
14131
14132  /**
14133   * Handle `mousemove` or `touchmove` events on the `ProgressControl`.
14134   *
14135   * @param {EventTarget~Event} event
14136   *        `mousedown` or `touchstart` event that triggered this function
14137   *
14138   * @listens mousemove
14139   * @listens touchmove
14140   */
14141
14142
14143  ProgressControl.prototype.handleMouseSeek = function handleMouseSeek(event) {
14144    var seekBar = this.getChild('seekBar');
14145
14146    seekBar.handleMouseMove(event);
14147  };
14148
14149  /**
14150   * Handle `mousedown` or `touchstart` events on the `ProgressControl`.
14151   *
14152   * @param {EventTarget~Event} event
14153   *        `mousedown` or `touchstart` event that triggered this function
14154   *
14155   * @listens mousedown
14156   * @listens touchstart
14157   */
14158
14159
14160  ProgressControl.prototype.handleMouseDown = function handleMouseDown(event) {
14161    var doc = this.el_.ownerDocument;
14162
14163    this.on(doc, 'mousemove', this.throttledHandleMouseSeek);
14164    this.on(doc, 'touchmove', this.throttledHandleMouseSeek);
14165    this.on(doc, 'mouseup', this.handleMouseUp);
14166    this.on(doc, 'touchend', this.handleMouseUp);
14167  };
14168
14169  /**
14170   * Handle `mouseup` or `touchend` events on the `ProgressControl`.
14171   *
14172   * @param {EventTarget~Event} event
14173   *        `mouseup` or `touchend` event that triggered this function.
14174   *
14175   * @listens touchend
14176   * @listens mouseup
14177   */
14178
14179
14180  ProgressControl.prototype.handleMouseUp = function handleMouseUp(event) {
14181    var doc = this.el_.ownerDocument;
14182
14183    this.off(doc, 'mousemove', this.throttledHandleMouseSeek);
14184    this.off(doc, 'touchmove', this.throttledHandleMouseSeek);
14185    this.off(doc, 'mouseup', this.handleMouseUp);
14186    this.off(doc, 'touchend', this.handleMouseUp);
14187  };
14188
14189  return ProgressControl;
14190}(Component);
14191
14192/**
14193 * Default options for `ProgressControl`
14194 *
14195 * @type {Object}
14196 * @private
14197 */
14198
14199
14200ProgressControl.prototype.options_ = {
14201  children: ['seekBar']
14202};
14203
14204Component.registerComponent('ProgressControl', ProgressControl);
14205
14206/**
14207 * @file fullscreen-toggle.js
14208 */
14209/**
14210 * Toggle fullscreen video
14211 *
14212 * @extends Button
14213 */
14214
14215var FullscreenToggle = function (_Button) {
14216  inherits(FullscreenToggle, _Button);
14217
14218  /**
14219   * Creates an instance of this class.
14220   *
14221   * @param {Player} player
14222   *        The `Player` that this class should be attached to.
14223   *
14224   * @param {Object} [options]
14225   *        The key/value store of player options.
14226   */
14227  function FullscreenToggle(player, options) {
14228    classCallCheck(this, FullscreenToggle);
14229
14230    var _this = possibleConstructorReturn(this, _Button.call(this, player, options));
14231
14232    _this.on(player, 'fullscreenchange', _this.handleFullscreenChange);
14233    return _this;
14234  }
14235
14236  /**
14237   * Builds the default DOM `className`.
14238   *
14239   * @return {string}
14240   *         The DOM `className` for this object.
14241   */
14242
14243
14244  FullscreenToggle.prototype.buildCSSClass = function buildCSSClass() {
14245    return 'vjs-fullscreen-control ' + _Button.prototype.buildCSSClass.call(this);
14246  };
14247
14248  /**
14249   * Handles fullscreenchange on the player and change control text accordingly.
14250   *
14251   * @param {EventTarget~Event} [event]
14252   *        The {@link Player#fullscreenchange} event that caused this function to be
14253   *        called.
14254   *
14255   * @listens Player#fullscreenchange
14256   */
14257
14258
14259  FullscreenToggle.prototype.handleFullscreenChange = function handleFullscreenChange(event) {
14260    if (this.player_.isFullscreen()) {
14261      this.controlText('Non-Fullscreen');
14262    } else {
14263      this.controlText('Fullscreen');
14264    }
14265  };
14266
14267  /**
14268   * This gets called when an `FullscreenToggle` is "clicked". See
14269   * {@link ClickableComponent} for more detailed information on what a click can be.
14270   *
14271   * @param {EventTarget~Event} [event]
14272   *        The `keydown`, `tap`, or `click` event that caused this function to be
14273   *        called.
14274   *
14275   * @listens tap
14276   * @listens click
14277   */
14278
14279
14280  FullscreenToggle.prototype.handleClick = function handleClick(event) {
14281    if (!this.player_.isFullscreen()) {
14282      this.player_.requestFullscreen();
14283    } else {
14284      this.player_.exitFullscreen();
14285    }
14286  };
14287
14288  return FullscreenToggle;
14289}(Button);
14290
14291/**
14292 * The text that should display over the `FullscreenToggle`s controls. Added for localization.
14293 *
14294 * @type {string}
14295 * @private
14296 */
14297
14298
14299FullscreenToggle.prototype.controlText_ = 'Fullscreen';
14300
14301Component.registerComponent('FullscreenToggle', FullscreenToggle);
14302
14303/**
14304 * Check if volume control is supported and if it isn't hide the
14305 * `Component` that was passed  using the `vjs-hidden` class.
14306 *
14307 * @param {Component} self
14308 *        The component that should be hidden if volume is unsupported
14309 *
14310 * @param {Player} player
14311 *        A reference to the player
14312 *
14313 * @private
14314 */
14315var checkVolumeSupport = function checkVolumeSupport(self, player) {
14316  // hide volume controls when they're not supported by the current tech
14317  if (player.tech_ && !player.tech_.featuresVolumeControl) {
14318    self.addClass('vjs-hidden');
14319  }
14320
14321  self.on(player, 'loadstart', function () {
14322    if (!player.tech_.featuresVolumeControl) {
14323      self.addClass('vjs-hidden');
14324    } else {
14325      self.removeClass('vjs-hidden');
14326    }
14327  });
14328};
14329
14330/**
14331 * @file volume-level.js
14332 */
14333/**
14334 * Shows volume level
14335 *
14336 * @extends Component
14337 */
14338
14339var VolumeLevel = function (_Component) {
14340  inherits(VolumeLevel, _Component);
14341
14342  function VolumeLevel() {
14343    classCallCheck(this, VolumeLevel);
14344    return possibleConstructorReturn(this, _Component.apply(this, arguments));
14345  }
14346
14347  /**
14348   * Create the `Component`'s DOM element
14349   *
14350   * @return {Element}
14351   *         The element that was created.
14352   */
14353  VolumeLevel.prototype.createEl = function createEl() {
14354    return _Component.prototype.createEl.call(this, 'div', {
14355      className: 'vjs-volume-level',
14356      innerHTML: '<span class="vjs-control-text"></span>'
14357    });
14358  };
14359
14360  return VolumeLevel;
14361}(Component);
14362
14363Component.registerComponent('VolumeLevel', VolumeLevel);
14364
14365/**
14366 * @file volume-bar.js
14367 */
14368// Required children
14369/**
14370 * The bar that contains the volume level and can be clicked on to adjust the level
14371 *
14372 * @extends Slider
14373 */
14374
14375var VolumeBar = function (_Slider) {
14376  inherits(VolumeBar, _Slider);
14377
14378  /**
14379   * Creates an instance of this class.
14380   *
14381   * @param {Player} player
14382   *        The `Player` that this class should be attached to.
14383   *
14384   * @param {Object} [options]
14385   *        The key/value store of player options.
14386   */
14387  function VolumeBar(player, options) {
14388    classCallCheck(this, VolumeBar);
14389
14390    var _this = possibleConstructorReturn(this, _Slider.call(this, player, options));
14391
14392    _this.on('slideractive', _this.updateLastVolume_);
14393    _this.on(player, 'volumechange', _this.updateARIAAttributes);
14394    player.ready(function () {
14395      return _this.updateARIAAttributes();
14396    });
14397    return _this;
14398  }
14399
14400  /**
14401   * Create the `Component`'s DOM element
14402   *
14403   * @return {Element}
14404   *         The element that was created.
14405   */
14406
14407
14408  VolumeBar.prototype.createEl = function createEl() {
14409    return _Slider.prototype.createEl.call(this, 'div', {
14410      className: 'vjs-volume-bar vjs-slider-bar'
14411    }, {
14412      'aria-label': this.localize('Volume Level'),
14413      'aria-live': 'polite'
14414    });
14415  };
14416
14417  /**
14418   * Handle movement events on the {@link VolumeMenuButton}.
14419   *
14420   * @param {EventTarget~Event} event
14421   *        The event that caused this function to run.
14422   *
14423   * @listens mousemove
14424   */
14425
14426
14427  VolumeBar.prototype.handleMouseMove = function handleMouseMove(event) {
14428    this.checkMuted();
14429    this.player_.volume(this.calculateDistance(event));
14430  };
14431
14432  /**
14433   * If the player is muted unmute it.
14434   */
14435
14436
14437  VolumeBar.prototype.checkMuted = function checkMuted() {
14438    if (this.player_.muted()) {
14439      this.player_.muted(false);
14440    }
14441  };
14442
14443  /**
14444   * Get percent of volume level
14445   *
14446   * @return {number}
14447   *         Volume level percent as a decimal number.
14448   */
14449
14450
14451  VolumeBar.prototype.getPercent = function getPercent() {
14452    if (this.player_.muted()) {
14453      return 0;
14454    }
14455    return this.player_.volume();
14456  };
14457
14458  /**
14459   * Increase volume level for keyboard users
14460   */
14461
14462
14463  VolumeBar.prototype.stepForward = function stepForward() {
14464    this.checkMuted();
14465    this.player_.volume(this.player_.volume() + 0.1);
14466  };
14467
14468  /**
14469   * Decrease volume level for keyboard users
14470   */
14471
14472
14473  VolumeBar.prototype.stepBack = function stepBack() {
14474    this.checkMuted();
14475    this.player_.volume(this.player_.volume() - 0.1);
14476  };
14477
14478  /**
14479   * Update ARIA accessibility attributes
14480   *
14481   * @param {EventTarget~Event} [event]
14482   *        The `volumechange` event that caused this function to run.
14483   *
14484   * @listens Player#volumechange
14485   */
14486
14487
14488  VolumeBar.prototype.updateARIAAttributes = function updateARIAAttributes(event) {
14489    var ariaValue = this.player_.muted() ? 0 : this.volumeAsPercentage_();
14490
14491    this.el_.setAttribute('aria-valuenow', ariaValue);
14492    this.el_.setAttribute('aria-valuetext', ariaValue + '%');
14493  };
14494
14495  /**
14496   * Returns the current value of the player volume as a percentage
14497   *
14498   * @private
14499   */
14500
14501
14502  VolumeBar.prototype.volumeAsPercentage_ = function volumeAsPercentage_() {
14503    return Math.round(this.player_.volume() * 100);
14504  };
14505
14506  /**
14507   * When user starts dragging the VolumeBar, store the volume and listen for
14508   * the end of the drag. When the drag ends, if the volume was set to zero,
14509   * set lastVolume to the stored volume.
14510   *
14511   * @listens slideractive
14512   * @private
14513   */
14514
14515
14516  VolumeBar.prototype.updateLastVolume_ = function updateLastVolume_() {
14517    var _this2 = this;
14518
14519    var volumeBeforeDrag = this.player_.volume();
14520
14521    this.one('sliderinactive', function () {
14522      if (_this2.player_.volume() === 0) {
14523        _this2.player_.lastVolume_(volumeBeforeDrag);
14524      }
14525    });
14526  };
14527
14528  return VolumeBar;
14529}(Slider);
14530
14531/**
14532 * Default options for the `VolumeBar`
14533 *
14534 * @type {Object}
14535 * @private
14536 */
14537
14538
14539VolumeBar.prototype.options_ = {
14540  children: ['volumeLevel'],
14541  barName: 'volumeLevel'
14542};
14543
14544/**
14545 * Call the update event for this Slider when this event happens on the player.
14546 *
14547 * @type {string}
14548 */
14549VolumeBar.prototype.playerEvent = 'volumechange';
14550
14551Component.registerComponent('VolumeBar', VolumeBar);
14552
14553/**
14554 * @file volume-control.js
14555 */
14556// Required children
14557/**
14558 * The component for controlling the volume level
14559 *
14560 * @extends Component
14561 */
14562
14563var VolumeControl = function (_Component) {
14564  inherits(VolumeControl, _Component);
14565
14566  /**
14567   * Creates an instance of this class.
14568   *
14569   * @param {Player} player
14570   *        The `Player` that this class should be attached to.
14571   *
14572   * @param {Object} [options={}]
14573   *        The key/value store of player options.
14574   */
14575  function VolumeControl(player) {
14576    var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
14577    classCallCheck(this, VolumeControl);
14578
14579    options.vertical = options.vertical || false;
14580
14581    // Pass the vertical option down to the VolumeBar if
14582    // the VolumeBar is turned on.
14583    if (typeof options.volumeBar === 'undefined' || isPlain(options.volumeBar)) {
14584      options.volumeBar = options.volumeBar || {};
14585      options.volumeBar.vertical = options.vertical;
14586    }
14587
14588    // hide this control if volume support is missing
14589    var _this = possibleConstructorReturn(this, _Component.call(this, player, options));
14590
14591    checkVolumeSupport(_this, player);
14592
14593    _this.throttledHandleMouseMove = throttle(bind(_this, _this.handleMouseMove), 25);
14594
14595    _this.on('mousedown', _this.handleMouseDown);
14596    _this.on('touchstart', _this.handleMouseDown);
14597
14598    // while the slider is active (the mouse has been pressed down and
14599    // is dragging) or in focus we do not want to hide the VolumeBar
14600    _this.on(_this.volumeBar, ['focus', 'slideractive'], function () {
14601      _this.volumeBar.addClass('vjs-slider-active');
14602      _this.addClass('vjs-slider-active');
14603      _this.trigger('slideractive');
14604    });
14605
14606    _this.on(_this.volumeBar, ['blur', 'sliderinactive'], function () {
14607      _this.volumeBar.removeClass('vjs-slider-active');
14608      _this.removeClass('vjs-slider-active');
14609      _this.trigger('sliderinactive');
14610    });
14611    return _this;
14612  }
14613
14614  /**
14615   * Create the `Component`'s DOM element
14616   *
14617   * @return {Element}
14618   *         The element that was created.
14619   */
14620
14621
14622  VolumeControl.prototype.createEl = function createEl() {
14623    var orientationClass = 'vjs-volume-horizontal';
14624
14625    if (this.options_.vertical) {
14626      orientationClass = 'vjs-volume-vertical';
14627    }
14628
14629    return _Component.prototype.createEl.call(this, 'div', {
14630      className: 'vjs-volume-control vjs-control ' + orientationClass
14631    });
14632  };
14633
14634  /**
14635   * Handle `mousedown` or `touchstart` events on the `VolumeControl`.
14636   *
14637   * @param {EventTarget~Event} event
14638   *        `mousedown` or `touchstart` event that triggered this function
14639   *
14640   * @listens mousedown
14641   * @listens touchstart
14642   */
14643
14644
14645  VolumeControl.prototype.handleMouseDown = function handleMouseDown(event) {
14646    var doc = this.el_.ownerDocument;
14647
14648    this.on(doc, 'mousemove', this.throttledHandleMouseMove);
14649    this.on(doc, 'touchmove', this.throttledHandleMouseMove);
14650    this.on(doc, 'mouseup', this.handleMouseUp);
14651    this.on(doc, 'touchend', this.handleMouseUp);
14652  };
14653
14654  /**
14655   * Handle `mouseup` or `touchend` events on the `VolumeControl`.
14656   *
14657   * @param {EventTarget~Event} event
14658   *        `mouseup` or `touchend` event that triggered this function.
14659   *
14660   * @listens touchend
14661   * @listens mouseup
14662   */
14663
14664
14665  VolumeControl.prototype.handleMouseUp = function handleMouseUp(event) {
14666    var doc = this.el_.ownerDocument;
14667
14668    this.off(doc, 'mousemove', this.throttledHandleMouseMove);
14669    this.off(doc, 'touchmove', this.throttledHandleMouseMove);
14670    this.off(doc, 'mouseup', this.handleMouseUp);
14671    this.off(doc, 'touchend', this.handleMouseUp);
14672  };
14673
14674  /**
14675   * Handle `mousedown` or `touchstart` events on the `VolumeControl`.
14676   *
14677   * @param {EventTarget~Event} event
14678   *        `mousedown` or `touchstart` event that triggered this function
14679   *
14680   * @listens mousedown
14681   * @listens touchstart
14682   */
14683
14684
14685  VolumeControl.prototype.handleMouseMove = function handleMouseMove(event) {
14686    this.volumeBar.handleMouseMove(event);
14687  };
14688
14689  return VolumeControl;
14690}(Component);
14691
14692/**
14693 * Default options for the `VolumeControl`
14694 *
14695 * @type {Object}
14696 * @private
14697 */
14698
14699
14700VolumeControl.prototype.options_ = {
14701  children: ['volumeBar']
14702};
14703
14704Component.registerComponent('VolumeControl', VolumeControl);
14705
14706/**
14707 * @file mute-toggle.js
14708 */
14709/**
14710 * A button component for muting the audio.
14711 *
14712 * @extends Button
14713 */
14714
14715var MuteToggle = function (_Button) {
14716  inherits(MuteToggle, _Button);
14717
14718  /**
14719   * Creates an instance of this class.
14720   *
14721   * @param {Player} player
14722   *        The `Player` that this class should be attached to.
14723   *
14724   * @param {Object} [options]
14725   *        The key/value store of player options.
14726   */
14727  function MuteToggle(player, options) {
14728    classCallCheck(this, MuteToggle);
14729
14730    // hide this control if volume support is missing
14731    var _this = possibleConstructorReturn(this, _Button.call(this, player, options));
14732
14733    checkVolumeSupport(_this, player);
14734
14735    _this.on(player, ['loadstart', 'volumechange'], _this.update);
14736    return _this;
14737  }
14738
14739  /**
14740   * Builds the default DOM `className`.
14741   *
14742   * @return {string}
14743   *         The DOM `className` for this object.
14744   */
14745
14746
14747  MuteToggle.prototype.buildCSSClass = function buildCSSClass() {
14748    return 'vjs-mute-control ' + _Button.prototype.buildCSSClass.call(this);
14749  };
14750
14751  /**
14752   * This gets called when an `MuteToggle` is "clicked". See
14753   * {@link ClickableComponent} for more detailed information on what a click can be.
14754   *
14755   * @param {EventTarget~Event} [event]
14756   *        The `keydown`, `tap`, or `click` event that caused this function to be
14757   *        called.
14758   *
14759   * @listens tap
14760   * @listens click
14761   */
14762
14763
14764  MuteToggle.prototype.handleClick = function handleClick(event) {
14765    var vol = this.player_.volume();
14766    var lastVolume = this.player_.lastVolume_();
14767
14768    if (vol === 0) {
14769      var volumeToSet = lastVolume < 0.1 ? 0.1 : lastVolume;
14770
14771      this.player_.volume(volumeToSet);
14772      this.player_.muted(false);
14773    } else {
14774      this.player_.muted(this.player_.muted() ? false : true);
14775    }
14776  };
14777
14778  /**
14779   * Update the `MuteToggle` button based on the state of `volume` and `muted`
14780   * on the player.
14781   *
14782   * @param {EventTarget~Event} [event]
14783   *        The {@link Player#loadstart} event if this function was called
14784   *        through an event.
14785   *
14786   * @listens Player#loadstart
14787   * @listens Player#volumechange
14788   */
14789
14790
14791  MuteToggle.prototype.update = function update(event) {
14792    this.updateIcon_();
14793    this.updateControlText_();
14794  };
14795
14796  /**
14797   * Update the appearance of the `MuteToggle` icon.
14798   *
14799   * Possible states (given `level` variable below):
14800   * - 0: crossed out
14801   * - 1: zero bars of volume
14802   * - 2: one bar of volume
14803   * - 3: two bars of volume
14804   *
14805   * @private
14806   */
14807
14808
14809  MuteToggle.prototype.updateIcon_ = function updateIcon_() {
14810    var vol = this.player_.volume();
14811    var level = 3;
14812
14813    if (vol === 0 || this.player_.muted()) {
14814      level = 0;
14815    } else if (vol < 0.33) {
14816      level = 1;
14817    } else if (vol < 0.67) {
14818      level = 2;
14819    }
14820
14821    // TODO improve muted icon classes
14822    for (var i = 0; i < 4; i++) {
14823      removeClass(this.el_, 'vjs-vol-' + i);
14824    }
14825    addClass(this.el_, 'vjs-vol-' + level);
14826  };
14827
14828  /**
14829   * If `muted` has changed on the player, update the control text
14830   * (`title` attribute on `vjs-mute-control` element and content of
14831   * `vjs-control-text` element).
14832   *
14833   * @private
14834   */
14835
14836
14837  MuteToggle.prototype.updateControlText_ = function updateControlText_() {
14838    var soundOff = this.player_.muted() || this.player_.volume() === 0;
14839    var text = soundOff ? 'Unmute' : 'Mute';
14840
14841    if (this.controlText() !== text) {
14842      this.controlText(text);
14843    }
14844  };
14845
14846  return MuteToggle;
14847}(Button);
14848
14849/**
14850 * The text that should display over the `MuteToggle`s controls. Added for localization.
14851 *
14852 * @type {string}
14853 * @private
14854 */
14855
14856
14857MuteToggle.prototype.controlText_ = 'Mute';
14858
14859Component.registerComponent('MuteToggle', MuteToggle);
14860
14861/**
14862 * @file volume-control.js
14863 */
14864// Required children
14865/**
14866 * A Component to contain the MuteToggle and VolumeControl so that
14867 * they can work together.
14868 *
14869 * @extends Component
14870 */
14871
14872var VolumePanel = function (_Component) {
14873  inherits(VolumePanel, _Component);
14874
14875  /**
14876   * Creates an instance of this class.
14877   *
14878   * @param {Player} player
14879   *        The `Player` that this class should be attached to.
14880   *
14881   * @param {Object} [options={}]
14882   *        The key/value store of player options.
14883   */
14884  function VolumePanel(player) {
14885    var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
14886    classCallCheck(this, VolumePanel);
14887
14888    if (typeof options.inline !== 'undefined') {
14889      options.inline = options.inline;
14890    } else {
14891      options.inline = true;
14892    }
14893
14894    // pass the inline option down to the VolumeControl as vertical if
14895    // the VolumeControl is on.
14896    if (typeof options.volumeControl === 'undefined' || isPlain(options.volumeControl)) {
14897      options.volumeControl = options.volumeControl || {};
14898      options.volumeControl.vertical = !options.inline;
14899    }
14900
14901    // hide this control if volume support is missing
14902    var _this = possibleConstructorReturn(this, _Component.call(this, player, options));
14903
14904    checkVolumeSupport(_this, player);
14905
14906    // while the slider is active (the mouse has been pressed down and
14907    // is dragging) or in focus we do not want to hide the VolumeBar
14908    _this.on(_this.volumeControl, ['slideractive'], _this.sliderActive_);
14909    _this.on(_this.muteToggle, 'focus', _this.sliderActive_);
14910
14911    _this.on(_this.volumeControl, ['sliderinactive'], _this.sliderInactive_);
14912    _this.on(_this.muteToggle, 'blur', _this.sliderInactive_);
14913    return _this;
14914  }
14915
14916  /**
14917   * Add vjs-slider-active class to the VolumePanel
14918   *
14919   * @listens VolumeControl#slideractive
14920   * @private
14921   */
14922
14923
14924  VolumePanel.prototype.sliderActive_ = function sliderActive_() {
14925    this.addClass('vjs-slider-active');
14926  };
14927
14928  /**
14929   * Removes vjs-slider-active class to the VolumePanel
14930   *
14931   * @listens VolumeControl#sliderinactive
14932   * @private
14933   */
14934
14935
14936  VolumePanel.prototype.sliderInactive_ = function sliderInactive_() {
14937    this.removeClass('vjs-slider-active');
14938  };
14939
14940  /**
14941   * Create the `Component`'s DOM element
14942   *
14943   * @return {Element}
14944   *         The element that was created.
14945   */
14946
14947
14948  VolumePanel.prototype.createEl = function createEl() {
14949    var orientationClass = 'vjs-volume-panel-horizontal';
14950
14951    if (!this.options_.inline) {
14952      orientationClass = 'vjs-volume-panel-vertical';
14953    }
14954
14955    return _Component.prototype.createEl.call(this, 'div', {
14956      className: 'vjs-volume-panel vjs-control ' + orientationClass
14957    });
14958  };
14959
14960  return VolumePanel;
14961}(Component);
14962
14963/**
14964 * Default options for the `VolumeControl`
14965 *
14966 * @type {Object}
14967 * @private
14968 */
14969
14970
14971VolumePanel.prototype.options_ = {
14972  children: ['muteToggle', 'volumeControl']
14973};
14974
14975Component.registerComponent('VolumePanel', VolumePanel);
14976
14977/**
14978 * @file menu.js
14979 */
14980/**
14981 * The Menu component is used to build popup menus, including subtitle and
14982 * captions selection menus.
14983 *
14984 * @extends Component
14985 */
14986
14987var Menu = function (_Component) {
14988  inherits(Menu, _Component);
14989
14990  /**
14991   * Create an instance of this class.
14992   *
14993   * @param {Player} player
14994   *        the player that this component should attach to
14995   *
14996   * @param {Object} [options]
14997   *        Object of option names and values
14998   *
14999   */
15000  function Menu(player, options) {
15001    classCallCheck(this, Menu);
15002
15003    var _this = possibleConstructorReturn(this, _Component.call(this, player, options));
15004
15005    if (options) {
15006      _this.menuButton_ = options.menuButton;
15007    }
15008
15009    _this.focusedChild_ = -1;
15010
15011    _this.on('keydown', _this.handleKeyPress);
15012    return _this;
15013  }
15014
15015  /**
15016   * Add a {@link MenuItem} to the menu.
15017   *
15018   * @param {Object|string} component
15019   *        The name or instance of the `MenuItem` to add.
15020   *
15021   */
15022
15023
15024  Menu.prototype.addItem = function addItem(component) {
15025    this.addChild(component);
15026    component.on('click', bind(this, function (event) {
15027      // Unpress the associated MenuButton, and move focus back to it
15028      if (this.menuButton_) {
15029        this.menuButton_.unpressButton();
15030
15031        // don't focus menu button if item is a caption settings item
15032        // because focus will move elsewhere and it logs an error on IE8
15033        if (component.name() !== 'CaptionSettingsMenuItem') {
15034          this.menuButton_.focus();
15035        }
15036      }
15037    }));
15038  };
15039
15040  /**
15041   * Create the `Menu`s DOM element.
15042   *
15043   * @return {Element}
15044   *         the element that was created
15045   */
15046
15047
15048  Menu.prototype.createEl = function createEl$$1() {
15049    var contentElType = this.options_.contentElType || 'ul';
15050
15051    this.contentEl_ = createEl(contentElType, {
15052      className: 'vjs-menu-content'
15053    });
15054
15055    this.contentEl_.setAttribute('role', 'menu');
15056
15057    var el = _Component.prototype.createEl.call(this, 'div', {
15058      append: this.contentEl_,
15059      className: 'vjs-menu'
15060    });
15061
15062    el.appendChild(this.contentEl_);
15063
15064    // Prevent clicks from bubbling up. Needed for Menu Buttons,
15065    // where a click on the parent is significant
15066    on(el, 'click', function (event) {
15067      event.preventDefault();
15068      event.stopImmediatePropagation();
15069    });
15070
15071    return el;
15072  };
15073
15074  /**
15075   * Handle a `keydown` event on this menu. This listener is added in the constructor.
15076   *
15077   * @param {EventTarget~Event} event
15078   *        A `keydown` event that happened on the menu.
15079   *
15080   * @listens keydown
15081   */
15082
15083
15084  Menu.prototype.handleKeyPress = function handleKeyPress(event) {
15085    // Left and Down Arrows
15086    if (event.which === 37 || event.which === 40) {
15087      event.preventDefault();
15088      this.stepForward();
15089
15090      // Up and Right Arrows
15091    } else if (event.which === 38 || event.which === 39) {
15092      event.preventDefault();
15093      this.stepBack();
15094    }
15095  };
15096
15097  /**
15098   * Move to next (lower) menu item for keyboard users.
15099   */
15100
15101
15102  Menu.prototype.stepForward = function stepForward() {
15103    var stepChild = 0;
15104
15105    if (this.focusedChild_ !== undefined) {
15106      stepChild = this.focusedChild_ + 1;
15107    }
15108    this.focus(stepChild);
15109  };
15110
15111  /**
15112   * Move to previous (higher) menu item for keyboard users.
15113   */
15114
15115
15116  Menu.prototype.stepBack = function stepBack() {
15117    var stepChild = 0;
15118
15119    if (this.focusedChild_ !== undefined) {
15120      stepChild = this.focusedChild_ - 1;
15121    }
15122    this.focus(stepChild);
15123  };
15124
15125  /**
15126   * Set focus on a {@link MenuItem} in the `Menu`.
15127   *
15128   * @param {Object|string} [item=0]
15129   *        Index of child item set focus on.
15130   */
15131
15132
15133  Menu.prototype.focus = function focus() {
15134    var item = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 0;
15135
15136    var children = this.children().slice();
15137    var haveTitle = children.length && children[0].className && /vjs-menu-title/.test(children[0].className);
15138
15139    if (haveTitle) {
15140      children.shift();
15141    }
15142
15143    if (children.length > 0) {
15144      if (item < 0) {
15145        item = 0;
15146      } else if (item >= children.length) {
15147        item = children.length - 1;
15148      }
15149
15150      this.focusedChild_ = item;
15151
15152      children[item].el_.focus();
15153    }
15154  };
15155
15156  return Menu;
15157}(Component);
15158
15159Component.registerComponent('Menu', Menu);
15160
15161/**
15162 * @file menu-button.js
15163 */
15164/**
15165 * A `MenuButton` class for any popup {@link Menu}.
15166 *
15167 * @extends Component
15168 */
15169
15170var MenuButton = function (_Component) {
15171  inherits(MenuButton, _Component);
15172
15173  /**
15174   * Creates an instance of this class.
15175   *
15176   * @param {Player} player
15177   *        The `Player` that this class should be attached to.
15178   *
15179   * @param {Object} [options={}]
15180   *        The key/value store of player options.
15181   */
15182  function MenuButton(player) {
15183    var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
15184    classCallCheck(this, MenuButton);
15185
15186    var _this = possibleConstructorReturn(this, _Component.call(this, player, options));
15187
15188    _this.menuButton_ = new Button(player, options);
15189
15190    _this.menuButton_.controlText(_this.controlText_);
15191    _this.menuButton_.el_.setAttribute('aria-haspopup', 'true');
15192
15193    // Add buildCSSClass values to the button, not the wrapper
15194    var buttonClass = Button.prototype.buildCSSClass();
15195
15196    _this.menuButton_.el_.className = _this.buildCSSClass() + ' ' + buttonClass;
15197    _this.menuButton_.removeClass('vjs-control');
15198
15199    _this.addChild(_this.menuButton_);
15200
15201    _this.update();
15202
15203    _this.enabled_ = true;
15204
15205    _this.on(_this.menuButton_, 'tap', _this.handleClick);
15206    _this.on(_this.menuButton_, 'click', _this.handleClick);
15207    _this.on(_this.menuButton_, 'focus', _this.handleFocus);
15208    _this.on(_this.menuButton_, 'blur', _this.handleBlur);
15209
15210    _this.on('keydown', _this.handleSubmenuKeyPress);
15211    return _this;
15212  }
15213
15214  /**
15215   * Update the menu based on the current state of its items.
15216   */
15217
15218
15219  MenuButton.prototype.update = function update() {
15220    var menu = this.createMenu();
15221
15222    if (this.menu) {
15223      this.removeChild(this.menu);
15224    }
15225
15226    this.menu = menu;
15227    this.addChild(menu);
15228
15229    /**
15230     * Track the state of the menu button
15231     *
15232     * @type {Boolean}
15233     * @private
15234     */
15235    this.buttonPressed_ = false;
15236    this.menuButton_.el_.setAttribute('aria-expanded', 'false');
15237
15238    if (this.items && this.items.length <= this.hideThreshold_) {
15239      this.hide();
15240    } else {
15241      this.show();
15242    }
15243  };
15244
15245  /**
15246   * Create the menu and add all items to it.
15247   *
15248   * @return {Menu}
15249   *         The constructed menu
15250   */
15251
15252
15253  MenuButton.prototype.createMenu = function createMenu() {
15254    var menu = new Menu(this.player_, { menuButton: this });
15255
15256    /**
15257     * Hide the menu if the number of items is less than or equal to this threshold. This defaults
15258     * to 0 and whenever we add items which can be hidden to the menu we'll increment it. We list
15259     * it here because every time we run `createMenu` we need to reset the value.
15260     *
15261     * @protected
15262     * @type {Number}
15263     */
15264    this.hideThreshold_ = 0;
15265
15266    // Add a title list item to the top
15267    if (this.options_.title) {
15268      var title = createEl('li', {
15269        className: 'vjs-menu-title',
15270        innerHTML: toTitleCase(this.options_.title),
15271        tabIndex: -1
15272      });
15273
15274      this.hideThreshold_ += 1;
15275
15276      menu.children_.unshift(title);
15277      prependTo(title, menu.contentEl());
15278    }
15279
15280    this.items = this.createItems();
15281
15282    if (this.items) {
15283      // Add menu items to the menu
15284      for (var i = 0; i < this.items.length; i++) {
15285        menu.addItem(this.items[i]);
15286      }
15287    }
15288
15289    return menu;
15290  };
15291
15292  /**
15293   * Create the list of menu items. Specific to each subclass.
15294   *
15295   * @abstract
15296   */
15297
15298
15299  MenuButton.prototype.createItems = function createItems() {};
15300
15301  /**
15302   * Create the `MenuButtons`s DOM element.
15303   *
15304   * @return {Element}
15305   *         The element that gets created.
15306   */
15307
15308
15309  MenuButton.prototype.createEl = function createEl$$1() {
15310    return _Component.prototype.createEl.call(this, 'div', {
15311      className: this.buildWrapperCSSClass()
15312    }, {});
15313  };
15314
15315  /**
15316   * Allow sub components to stack CSS class names for the wrapper element
15317   *
15318   * @return {string}
15319   *         The constructed wrapper DOM `className`
15320   */
15321
15322
15323  MenuButton.prototype.buildWrapperCSSClass = function buildWrapperCSSClass() {
15324    var menuButtonClass = 'vjs-menu-button';
15325
15326    // If the inline option is passed, we want to use different styles altogether.
15327    if (this.options_.inline === true) {
15328      menuButtonClass += '-inline';
15329    } else {
15330      menuButtonClass += '-popup';
15331    }
15332
15333    // TODO: Fix the CSS so that this isn't necessary
15334    var buttonClass = Button.prototype.buildCSSClass();
15335
15336    return 'vjs-menu-button ' + menuButtonClass + ' ' + buttonClass + ' ' + _Component.prototype.buildCSSClass.call(this);
15337  };
15338
15339  /**
15340   * Builds the default DOM `className`.
15341   *
15342   * @return {string}
15343   *         The DOM `className` for this object.
15344   */
15345
15346
15347  MenuButton.prototype.buildCSSClass = function buildCSSClass() {
15348    var menuButtonClass = 'vjs-menu-button';
15349
15350    // If the inline option is passed, we want to use different styles altogether.
15351    if (this.options_.inline === true) {
15352      menuButtonClass += '-inline';
15353    } else {
15354      menuButtonClass += '-popup';
15355    }
15356
15357    return 'vjs-menu-button ' + menuButtonClass + ' ' + _Component.prototype.buildCSSClass.call(this);
15358  };
15359
15360  /**
15361   * Get or set the localized control text that will be used for accessibility.
15362   *
15363   * > NOTE: This will come from the internal `menuButton_` element.
15364   *
15365   * @param {string} [text]
15366   *        Control text for element.
15367   *
15368   * @param {Element} [el=this.menuButton_.el()]
15369   *        Element to set the title on.
15370   *
15371   * @return {string}
15372   *         - The control text when getting
15373   */
15374
15375
15376  MenuButton.prototype.controlText = function controlText(text) {
15377    var el = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : this.menuButton_.el();
15378
15379    return this.menuButton_.controlText(text, el);
15380  };
15381
15382  /**
15383   * Handle a click on a `MenuButton`.
15384   * See {@link ClickableComponent#handleClick} for instances where this is called.
15385   *
15386   * @param {EventTarget~Event} event
15387   *        The `keydown`, `tap`, or `click` event that caused this function to be
15388   *        called.
15389   *
15390   * @listens tap
15391   * @listens click
15392   */
15393
15394
15395  MenuButton.prototype.handleClick = function handleClick(event) {
15396    // When you click the button it adds focus, which will show the menu.
15397    // So we'll remove focus when the mouse leaves the button. Focus is needed
15398    // for tab navigation.
15399
15400    this.one(this.menu.contentEl(), 'mouseleave', bind(this, function (e) {
15401      this.unpressButton();
15402      this.el_.blur();
15403    }));
15404    if (this.buttonPressed_) {
15405      this.unpressButton();
15406    } else {
15407      this.pressButton();
15408    }
15409  };
15410
15411  /**
15412   * Set the focus to the actual button, not to this element
15413   */
15414
15415
15416  MenuButton.prototype.focus = function focus() {
15417    this.menuButton_.focus();
15418  };
15419
15420  /**
15421   * Remove the focus from the actual button, not this element
15422   */
15423
15424
15425  MenuButton.prototype.blur = function blur() {
15426    this.menuButton_.blur();
15427  };
15428
15429  /**
15430   * This gets called when a `MenuButton` gains focus via a `focus` event.
15431   * Turns on listening for `keydown` events. When they happen it
15432   * calls `this.handleKeyPress`.
15433   *
15434   * @param {EventTarget~Event} event
15435   *        The `focus` event that caused this function to be called.
15436   *
15437   * @listens focus
15438   */
15439
15440
15441  MenuButton.prototype.handleFocus = function handleFocus() {
15442    on(document_1, 'keydown', bind(this, this.handleKeyPress));
15443  };
15444
15445  /**
15446   * Called when a `MenuButton` loses focus. Turns off the listener for
15447   * `keydown` events. Which Stops `this.handleKeyPress` from getting called.
15448   *
15449   * @param {EventTarget~Event} event
15450   *        The `blur` event that caused this function to be called.
15451   *
15452   * @listens blur
15453   */
15454
15455
15456  MenuButton.prototype.handleBlur = function handleBlur() {
15457    off(document_1, 'keydown', bind(this, this.handleKeyPress));
15458  };
15459
15460  /**
15461   * Handle tab, escape, down arrow, and up arrow keys for `MenuButton`. See
15462   * {@link ClickableComponent#handleKeyPress} for instances where this is called.
15463   *
15464   * @param {EventTarget~Event} event
15465   *        The `keydown` event that caused this function to be called.
15466   *
15467   * @listens keydown
15468   */
15469
15470
15471  MenuButton.prototype.handleKeyPress = function handleKeyPress(event) {
15472
15473    // Escape (27) key or Tab (9) key unpress the 'button'
15474    if (event.which === 27 || event.which === 9) {
15475      if (this.buttonPressed_) {
15476        this.unpressButton();
15477      }
15478      // Don't preventDefault for Tab key - we still want to lose focus
15479      if (event.which !== 9) {
15480        event.preventDefault();
15481        // Set focus back to the menu button's button
15482        this.menuButton_.el_.focus();
15483      }
15484      // Up (38) key or Down (40) key press the 'button'
15485    } else if (event.which === 38 || event.which === 40) {
15486      if (!this.buttonPressed_) {
15487        this.pressButton();
15488        event.preventDefault();
15489      }
15490    }
15491  };
15492
15493  /**
15494   * Handle a `keydown` event on a sub-menu. The listener for this is added in
15495   * the constructor.
15496   *
15497   * @param {EventTarget~Event} event
15498   *        Key press event
15499   *
15500   * @listens keydown
15501   */
15502
15503
15504  MenuButton.prototype.handleSubmenuKeyPress = function handleSubmenuKeyPress(event) {
15505
15506    // Escape (27) key or Tab (9) key unpress the 'button'
15507    if (event.which === 27 || event.which === 9) {
15508      if (this.buttonPressed_) {
15509        this.unpressButton();
15510      }
15511      // Don't preventDefault for Tab key - we still want to lose focus
15512      if (event.which !== 9) {
15513        event.preventDefault();
15514        // Set focus back to the menu button's button
15515        this.menuButton_.el_.focus();
15516      }
15517    }
15518  };
15519
15520  /**
15521   * Put the current `MenuButton` into a pressed state.
15522   */
15523
15524
15525  MenuButton.prototype.pressButton = function pressButton() {
15526    if (this.enabled_) {
15527      this.buttonPressed_ = true;
15528      this.menu.lockShowing();
15529      this.menuButton_.el_.setAttribute('aria-expanded', 'true');
15530      // set the focus into the submenu
15531      this.menu.focus();
15532    }
15533  };
15534
15535  /**
15536   * Take the current `MenuButton` out of a pressed state.
15537   */
15538
15539
15540  MenuButton.prototype.unpressButton = function unpressButton() {
15541    if (this.enabled_) {
15542      this.buttonPressed_ = false;
15543      this.menu.unlockShowing();
15544      this.menuButton_.el_.setAttribute('aria-expanded', 'false');
15545    }
15546  };
15547
15548  /**
15549   * Disable the `MenuButton`. Don't allow it to be clicked.
15550   */
15551
15552
15553  MenuButton.prototype.disable = function disable() {
15554    this.unpressButton();
15555
15556    this.enabled_ = false;
15557    this.addClass('vjs-disabled');
15558
15559    this.menuButton_.disable();
15560  };
15561
15562  /**
15563   * Enable the `MenuButton`. Allow it to be clicked.
15564   */
15565
15566
15567  MenuButton.prototype.enable = function enable() {
15568    this.enabled_ = true;
15569    this.removeClass('vjs-disabled');
15570
15571    this.menuButton_.enable();
15572  };
15573
15574  return MenuButton;
15575}(Component);
15576
15577Component.registerComponent('MenuButton', MenuButton);
15578
15579/**
15580 * @file track-button.js
15581 */
15582/**
15583 * The base class for buttons that toggle specific  track types (e.g. subtitles).
15584 *
15585 * @extends MenuButton
15586 */
15587
15588var TrackButton = function (_MenuButton) {
15589  inherits(TrackButton, _MenuButton);
15590
15591  /**
15592   * Creates an instance of this class.
15593   *
15594   * @param {Player} player
15595   *        The `Player` that this class should be attached to.
15596   *
15597   * @param {Object} [options]
15598   *        The key/value store of player options.
15599   */
15600  function TrackButton(player, options) {
15601    classCallCheck(this, TrackButton);
15602
15603    var tracks = options.tracks;
15604
15605    var _this = possibleConstructorReturn(this, _MenuButton.call(this, player, options));
15606
15607    if (_this.items.length <= 1) {
15608      _this.hide();
15609    }
15610
15611    if (!tracks) {
15612      return possibleConstructorReturn(_this);
15613    }
15614
15615    var updateHandler = bind(_this, _this.update);
15616
15617    tracks.addEventListener('removetrack', updateHandler);
15618    tracks.addEventListener('addtrack', updateHandler);
15619    _this.player_.on('ready', updateHandler);
15620
15621    _this.player_.on('dispose', function () {
15622      tracks.removeEventListener('removetrack', updateHandler);
15623      tracks.removeEventListener('addtrack', updateHandler);
15624    });
15625    return _this;
15626  }
15627
15628  return TrackButton;
15629}(MenuButton);
15630
15631Component.registerComponent('TrackButton', TrackButton);
15632
15633/**
15634 * @file menu-item.js
15635 */
15636/**
15637 * The component for a menu item. `<li>`
15638 *
15639 * @extends ClickableComponent
15640 */
15641
15642var MenuItem = function (_ClickableComponent) {
15643  inherits(MenuItem, _ClickableComponent);
15644
15645  /**
15646   * Creates an instance of the this class.
15647   *
15648   * @param {Player} player
15649   *        The `Player` that this class should be attached to.
15650   *
15651   * @param {Object} [options={}]
15652   *        The key/value store of player options.
15653   *
15654   */
15655  function MenuItem(player, options) {
15656    classCallCheck(this, MenuItem);
15657
15658    var _this = possibleConstructorReturn(this, _ClickableComponent.call(this, player, options));
15659
15660    _this.selectable = options.selectable;
15661
15662    _this.selected(options.selected);
15663
15664    if (_this.selectable) {
15665      // TODO: May need to be either menuitemcheckbox or menuitemradio,
15666      //       and may need logical grouping of menu items.
15667      _this.el_.setAttribute('role', 'menuitemcheckbox');
15668    } else {
15669      _this.el_.setAttribute('role', 'menuitem');
15670    }
15671    return _this;
15672  }
15673
15674  /**
15675   * Create the `MenuItem's DOM element
15676   *
15677   * @param {string} [type=li]
15678   *        Element's node type, not actually used, always set to `li`.
15679   *
15680   * @param {Object} [props={}]
15681   *        An object of properties that should be set on the element
15682   *
15683   * @param {Object} [attrs={}]
15684   *        An object of attributes that should be set on the element
15685   *
15686   * @return {Element}
15687   *         The element that gets created.
15688   */
15689
15690
15691  MenuItem.prototype.createEl = function createEl(type, props, attrs) {
15692    // The control is textual, not just an icon
15693    this.nonIconControl = true;
15694
15695    return _ClickableComponent.prototype.createEl.call(this, 'li', assign({
15696      className: 'vjs-menu-item',
15697      innerHTML: '<span class="vjs-menu-item-text">' + this.localize(this.options_.label) + '</span>',
15698      tabIndex: -1
15699    }, props), attrs);
15700  };
15701
15702  /**
15703   * Any click on a `MenuItem` puts int into the selected state.
15704   * See {@link ClickableComponent#handleClick} for instances where this is called.
15705   *
15706   * @param {EventTarget~Event} event
15707   *        The `keydown`, `tap`, or `click` event that caused this function to be
15708   *        called.
15709   *
15710   * @listens tap
15711   * @listens click
15712   */
15713
15714
15715  MenuItem.prototype.handleClick = function handleClick(event) {
15716    this.selected(true);
15717  };
15718
15719  /**
15720   * Set the state for this menu item as selected or not.
15721   *
15722   * @param {boolean} selected
15723   *        if the menu item is selected or not
15724   */
15725
15726
15727  MenuItem.prototype.selected = function selected(_selected) {
15728    if (this.selectable) {
15729      if (_selected) {
15730        this.addClass('vjs-selected');
15731        this.el_.setAttribute('aria-checked', 'true');
15732        // aria-checked isn't fully supported by browsers/screen readers,
15733        // so indicate selected state to screen reader in the control text.
15734        this.controlText(', selected');
15735      } else {
15736        this.removeClass('vjs-selected');
15737        this.el_.setAttribute('aria-checked', 'false');
15738        // Indicate un-selected state to screen reader
15739        // Note that a space clears out the selected state text
15740        this.controlText(' ');
15741      }
15742    }
15743  };
15744
15745  return MenuItem;
15746}(ClickableComponent);
15747
15748Component.registerComponent('MenuItem', MenuItem);
15749
15750/**
15751 * @file text-track-menu-item.js
15752 */
15753/**
15754 * The specific menu item type for selecting a language within a text track kind
15755 *
15756 * @extends MenuItem
15757 */
15758
15759var TextTrackMenuItem = function (_MenuItem) {
15760  inherits(TextTrackMenuItem, _MenuItem);
15761
15762  /**
15763   * Creates an instance of this class.
15764   *
15765   * @param {Player} player
15766   *        The `Player` that this class should be attached to.
15767   *
15768   * @param {Object} [options]
15769   *        The key/value store of player options.
15770   */
15771  function TextTrackMenuItem(player, options) {
15772    classCallCheck(this, TextTrackMenuItem);
15773
15774    var track = options.track;
15775    var tracks = player.textTracks();
15776
15777    // Modify options for parent MenuItem class's init.
15778    options.label = track.label || track.language || 'Unknown';
15779    options.selected = track.mode === 'showing';
15780
15781    var _this = possibleConstructorReturn(this, _MenuItem.call(this, player, options));
15782
15783    _this.track = track;
15784    var changeHandler = bind(_this, _this.handleTracksChange);
15785    var selectedLanguageChangeHandler = bind(_this, _this.handleSelectedLanguageChange);
15786
15787    player.on(['loadstart', 'texttrackchange'], changeHandler);
15788    tracks.addEventListener('change', changeHandler);
15789    tracks.addEventListener('selectedlanguagechange', selectedLanguageChangeHandler);
15790    _this.on('dispose', function () {
15791      tracks.removeEventListener('change', changeHandler);
15792      tracks.removeEventListener('selectedlanguagechange', selectedLanguageChangeHandler);
15793    });
15794
15795    // iOS7 doesn't dispatch change events to TextTrackLists when an
15796    // associated track's mode changes. Without something like
15797    // Object.observe() (also not present on iOS7), it's not
15798    // possible to detect changes to the mode attribute and polyfill
15799    // the change event. As a poor substitute, we manually dispatch
15800    // change events whenever the controls modify the mode.
15801    if (tracks.onchange === undefined) {
15802      var event = void 0;
15803
15804      _this.on(['tap', 'click'], function () {
15805        if (_typeof(window_1.Event) !== 'object') {
15806          // Android 2.3 throws an Illegal Constructor error for window.Event
15807          try {
15808            event = new window_1.Event('change');
15809          } catch (err) {
15810            // continue regardless of error
15811          }
15812        }
15813
15814        if (!event) {
15815          event = document_1.createEvent('Event');
15816          event.initEvent('change', true, true);
15817        }
15818
15819        tracks.dispatchEvent(event);
15820      });
15821    }
15822    return _this;
15823  }
15824
15825  /**
15826   * This gets called when an `TextTrackMenuItem` is "clicked". See
15827   * {@link ClickableComponent} for more detailed information on what a click can be.
15828   *
15829   * @param {EventTarget~Event} event
15830   *        The `keydown`, `tap`, or `click` event that caused this function to be
15831   *        called.
15832   *
15833   * @listens tap
15834   * @listens click
15835   */
15836
15837
15838  TextTrackMenuItem.prototype.handleClick = function handleClick(event) {
15839    var kind = this.track.kind;
15840    var kinds = this.track.kinds;
15841    var tracks = this.player_.textTracks();
15842
15843    if (!kinds) {
15844      kinds = [kind];
15845    }
15846
15847    _MenuItem.prototype.handleClick.call(this, event);
15848
15849    if (!tracks) {
15850      return;
15851    }
15852
15853    for (var i = 0; i < tracks.length; i++) {
15854      var track = tracks[i];
15855
15856      if (track === this.track && kinds.indexOf(track.kind) > -1) {
15857        if (track.mode !== 'showing') {
15858          track.mode = 'showing';
15859        }
15860      } else if (track.mode !== 'disabled') {
15861        track.mode = 'disabled';
15862      }
15863    }
15864  };
15865
15866  /**
15867   * Handle text track list change
15868   *
15869   * @param {EventTarget~Event} event
15870   *        The `change` event that caused this function to be called.
15871   *
15872   * @listens TextTrackList#change
15873   */
15874
15875
15876  TextTrackMenuItem.prototype.handleTracksChange = function handleTracksChange(event) {
15877    this.selected(this.track.mode === 'showing');
15878  };
15879
15880  TextTrackMenuItem.prototype.handleSelectedLanguageChange = function handleSelectedLanguageChange(event) {
15881    if (this.track.mode === 'showing') {
15882      var selectedLanguage = this.player_.cache_.selectedLanguage;
15883
15884      // Don't replace the kind of track across the same language
15885      if (selectedLanguage && selectedLanguage.enabled && selectedLanguage.language === this.track.language && selectedLanguage.kind !== this.track.kind) {
15886        return;
15887      }
15888
15889      this.player_.cache_.selectedLanguage = {
15890        enabled: true,
15891        language: this.track.language,
15892        kind: this.track.kind
15893      };
15894    }
15895  };
15896
15897  return TextTrackMenuItem;
15898}(MenuItem);
15899
15900Component.registerComponent('TextTrackMenuItem', TextTrackMenuItem);
15901
15902/**
15903 * @file off-text-track-menu-item.js
15904 */
15905/**
15906 * A special menu item for turning of a specific type of text track
15907 *
15908 * @extends TextTrackMenuItem
15909 */
15910
15911var OffTextTrackMenuItem = function (_TextTrackMenuItem) {
15912  inherits(OffTextTrackMenuItem, _TextTrackMenuItem);
15913
15914  /**
15915   * Creates an instance of this class.
15916   *
15917   * @param {Player} player
15918   *        The `Player` that this class should be attached to.
15919   *
15920   * @param {Object} [options]
15921   *        The key/value store of player options.
15922   */
15923  function OffTextTrackMenuItem(player, options) {
15924    classCallCheck(this, OffTextTrackMenuItem);
15925
15926    // Create pseudo track info
15927    // Requires options['kind']
15928    options.track = {
15929      player: player,
15930      kind: options.kind,
15931      kinds: options.kinds,
15932      'default': false,
15933      mode: 'disabled'
15934    };
15935
15936    if (!options.kinds) {
15937      options.kinds = [options.kind];
15938    }
15939
15940    if (options.label) {
15941      options.track.label = options.label;
15942    } else {
15943      options.track.label = options.kinds.join(' and ') + ' off';
15944    }
15945
15946    // MenuItem is selectable
15947    options.selectable = true;
15948
15949    var _this = possibleConstructorReturn(this, _TextTrackMenuItem.call(this, player, options));
15950
15951    _this.selected(true);
15952    return _this;
15953  }
15954
15955  /**
15956   * Handle text track change
15957   *
15958   * @param {EventTarget~Event} event
15959   *        The event that caused this function to run
15960   */
15961
15962
15963  OffTextTrackMenuItem.prototype.handleTracksChange = function handleTracksChange(event) {
15964    var tracks = this.player().textTracks();
15965    var selected = true;
15966
15967    for (var i = 0, l = tracks.length; i < l; i++) {
15968      var track = tracks[i];
15969
15970      if (this.options_.kinds.indexOf(track.kind) > -1 && track.mode === 'showing') {
15971        selected = false;
15972        break;
15973      }
15974    }
15975
15976    this.selected(selected);
15977  };
15978
15979  OffTextTrackMenuItem.prototype.handleSelectedLanguageChange = function handleSelectedLanguageChange(event) {
15980    var tracks = this.player().textTracks();
15981    var allHidden = true;
15982
15983    for (var i = 0, l = tracks.length; i < l; i++) {
15984      var track = tracks[i];
15985
15986      if (['captions', 'descriptions', 'subtitles'].indexOf(track.kind) > -1 && track.mode === 'showing') {
15987        allHidden = false;
15988        break;
15989      }
15990    }
15991
15992    if (allHidden) {
15993      this.player_.cache_.selectedLanguage = {
15994        enabled: false
15995      };
15996    }
15997  };
15998
15999  return OffTextTrackMenuItem;
16000}(TextTrackMenuItem);
16001
16002Component.registerComponent('OffTextTrackMenuItem', OffTextTrackMenuItem);
16003
16004/**
16005 * @file text-track-button.js
16006 */
16007/**
16008 * The base class for buttons that toggle specific text track types (e.g. subtitles)
16009 *
16010 * @extends MenuButton
16011 */
16012
16013var TextTrackButton = function (_TrackButton) {
16014  inherits(TextTrackButton, _TrackButton);
16015
16016  /**
16017   * Creates an instance of this class.
16018   *
16019   * @param {Player} player
16020   *        The `Player` that this class should be attached to.
16021   *
16022   * @param {Object} [options={}]
16023   *        The key/value store of player options.
16024   */
16025  function TextTrackButton(player) {
16026    var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
16027    classCallCheck(this, TextTrackButton);
16028
16029    options.tracks = player.textTracks();
16030
16031    return possibleConstructorReturn(this, _TrackButton.call(this, player, options));
16032  }
16033
16034  /**
16035   * Create a menu item for each text track
16036   *
16037   * @param {TextTrackMenuItem[]} [items=[]]
16038   *        Existing array of items to use during creation
16039   *
16040   * @return {TextTrackMenuItem[]}
16041   *         Array of menu items that were created
16042   */
16043
16044
16045  TextTrackButton.prototype.createItems = function createItems() {
16046    var items = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : [];
16047    var TrackMenuItem = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : TextTrackMenuItem;
16048
16049
16050    // Label is an overide for the [track] off label
16051    // USed to localise captions/subtitles
16052    var label = void 0;
16053
16054    if (this.label_) {
16055      label = this.label_ + ' off';
16056    }
16057    // Add an OFF menu item to turn all tracks off
16058    items.push(new OffTextTrackMenuItem(this.player_, {
16059      kinds: this.kinds_,
16060      kind: this.kind_,
16061      label: label
16062    }));
16063
16064    this.hideThreshold_ += 1;
16065
16066    var tracks = this.player_.textTracks();
16067
16068    if (!Array.isArray(this.kinds_)) {
16069      this.kinds_ = [this.kind_];
16070    }
16071
16072    for (var i = 0; i < tracks.length; i++) {
16073      var track = tracks[i];
16074
16075      // only add tracks that are of an appropriate kind and have a label
16076      if (this.kinds_.indexOf(track.kind) > -1) {
16077
16078        var item = new TrackMenuItem(this.player_, {
16079          track: track,
16080          // MenuItem is selectable
16081          selectable: true
16082        });
16083
16084        item.addClass('vjs-' + track.kind + '-menu-item');
16085        items.push(item);
16086      }
16087    }
16088
16089    return items;
16090  };
16091
16092  return TextTrackButton;
16093}(TrackButton);
16094
16095Component.registerComponent('TextTrackButton', TextTrackButton);
16096
16097/**
16098 * @file chapters-track-menu-item.js
16099 */
16100/**
16101 * The chapter track menu item
16102 *
16103 * @extends MenuItem
16104 */
16105
16106var ChaptersTrackMenuItem = function (_MenuItem) {
16107  inherits(ChaptersTrackMenuItem, _MenuItem);
16108
16109  /**
16110   * Creates an instance of this class.
16111   *
16112   * @param {Player} player
16113   *        The `Player` that this class should be attached to.
16114   *
16115   * @param {Object} [options]
16116   *        The key/value store of player options.
16117   */
16118  function ChaptersTrackMenuItem(player, options) {
16119    classCallCheck(this, ChaptersTrackMenuItem);
16120
16121    var track = options.track;
16122    var cue = options.cue;
16123    var currentTime = player.currentTime();
16124
16125    // Modify options for parent MenuItem class's init.
16126    options.selectable = true;
16127    options.label = cue.text;
16128    options.selected = cue.startTime <= currentTime && currentTime < cue.endTime;
16129
16130    var _this = possibleConstructorReturn(this, _MenuItem.call(this, player, options));
16131
16132    _this.track = track;
16133    _this.cue = cue;
16134    track.addEventListener('cuechange', bind(_this, _this.update));
16135    return _this;
16136  }
16137
16138  /**
16139   * This gets called when an `ChaptersTrackMenuItem` is "clicked". See
16140   * {@link ClickableComponent} for more detailed information on what a click can be.
16141   *
16142   * @param {EventTarget~Event} [event]
16143   *        The `keydown`, `tap`, or `click` event that caused this function to be
16144   *        called.
16145   *
16146   * @listens tap
16147   * @listens click
16148   */
16149
16150
16151  ChaptersTrackMenuItem.prototype.handleClick = function handleClick(event) {
16152    _MenuItem.prototype.handleClick.call(this);
16153    this.player_.currentTime(this.cue.startTime);
16154    this.update(this.cue.startTime);
16155  };
16156
16157  /**
16158   * Update chapter menu item
16159   *
16160   * @param {EventTarget~Event} [event]
16161   *        The `cuechange` event that caused this function to run.
16162   *
16163   * @listens TextTrack#cuechange
16164   */
16165
16166
16167  ChaptersTrackMenuItem.prototype.update = function update(event) {
16168    var cue = this.cue;
16169    var currentTime = this.player_.currentTime();
16170
16171    // vjs.log(currentTime, cue.startTime);
16172    this.selected(cue.startTime <= currentTime && currentTime < cue.endTime);
16173  };
16174
16175  return ChaptersTrackMenuItem;
16176}(MenuItem);
16177
16178Component.registerComponent('ChaptersTrackMenuItem', ChaptersTrackMenuItem);
16179
16180/**
16181 * @file chapters-button.js
16182 */
16183/**
16184 * The button component for toggling and selecting chapters
16185 * Chapters act much differently than other text tracks
16186 * Cues are navigation vs. other tracks of alternative languages
16187 *
16188 * @extends TextTrackButton
16189 */
16190
16191var ChaptersButton = function (_TextTrackButton) {
16192  inherits(ChaptersButton, _TextTrackButton);
16193
16194  /**
16195   * Creates an instance of this class.
16196   *
16197   * @param {Player} player
16198   *        The `Player` that this class should be attached to.
16199   *
16200   * @param {Object} [options]
16201   *        The key/value store of player options.
16202   *
16203   * @param {Component~ReadyCallback} [ready]
16204   *        The function to call when this function is ready.
16205   */
16206  function ChaptersButton(player, options, ready) {
16207    classCallCheck(this, ChaptersButton);
16208    return possibleConstructorReturn(this, _TextTrackButton.call(this, player, options, ready));
16209  }
16210
16211  /**
16212   * Builds the default DOM `className`.
16213   *
16214   * @return {string}
16215   *         The DOM `className` for this object.
16216   */
16217
16218
16219  ChaptersButton.prototype.buildCSSClass = function buildCSSClass() {
16220    return 'vjs-chapters-button ' + _TextTrackButton.prototype.buildCSSClass.call(this);
16221  };
16222
16223  ChaptersButton.prototype.buildWrapperCSSClass = function buildWrapperCSSClass() {
16224    return 'vjs-chapters-button ' + _TextTrackButton.prototype.buildWrapperCSSClass.call(this);
16225  };
16226
16227  /**
16228   * Update the menu based on the current state of its items.
16229   *
16230   * @param {EventTarget~Event} [event]
16231   *        An event that triggered this function to run.
16232   *
16233   * @listens TextTrackList#addtrack
16234   * @listens TextTrackList#removetrack
16235   * @listens TextTrackList#change
16236   */
16237
16238
16239  ChaptersButton.prototype.update = function update(event) {
16240    if (!this.track_ || event && (event.type === 'addtrack' || event.type === 'removetrack')) {
16241      this.setTrack(this.findChaptersTrack());
16242    }
16243    _TextTrackButton.prototype.update.call(this);
16244  };
16245
16246  /**
16247   * Set the currently selected track for the chapters button.
16248   *
16249   * @param {TextTrack} track
16250   *        The new track to select. Nothing will change if this is the currently selected
16251   *        track.
16252   */
16253
16254
16255  ChaptersButton.prototype.setTrack = function setTrack(track) {
16256    if (this.track_ === track) {
16257      return;
16258    }
16259
16260    if (!this.updateHandler_) {
16261      this.updateHandler_ = this.update.bind(this);
16262    }
16263
16264    // here this.track_ refers to the old track instance
16265    if (this.track_) {
16266      var remoteTextTrackEl = this.player_.remoteTextTrackEls().getTrackElementByTrack_(this.track_);
16267
16268      if (remoteTextTrackEl) {
16269        remoteTextTrackEl.removeEventListener('load', this.updateHandler_);
16270      }
16271
16272      this.track_ = null;
16273    }
16274
16275    this.track_ = track;
16276
16277    // here this.track_ refers to the new track instance
16278    if (this.track_) {
16279      this.track_.mode = 'hidden';
16280
16281      var _remoteTextTrackEl = this.player_.remoteTextTrackEls().getTrackElementByTrack_(this.track_);
16282
16283      if (_remoteTextTrackEl) {
16284        _remoteTextTrackEl.addEventListener('load', this.updateHandler_);
16285      }
16286    }
16287  };
16288
16289  /**
16290   * Find the track object that is currently in use by this ChaptersButton
16291   *
16292   * @return {TextTrack|undefined}
16293   *         The current track or undefined if none was found.
16294   */
16295
16296
16297  ChaptersButton.prototype.findChaptersTrack = function findChaptersTrack() {
16298    var tracks = this.player_.textTracks() || [];
16299
16300    for (var i = tracks.length - 1; i >= 0; i--) {
16301      // We will always choose the last track as our chaptersTrack
16302      var track = tracks[i];
16303
16304      if (track.kind === this.kind_) {
16305        return track;
16306      }
16307    }
16308  };
16309
16310  /**
16311   * Get the caption for the ChaptersButton based on the track label. This will also
16312   * use the current tracks localized kind as a fallback if a label does not exist.
16313   *
16314   * @return {string}
16315   *         The tracks current label or the localized track kind.
16316   */
16317
16318
16319  ChaptersButton.prototype.getMenuCaption = function getMenuCaption() {
16320    if (this.track_ && this.track_.label) {
16321      return this.track_.label;
16322    }
16323    return this.localize(toTitleCase(this.kind_));
16324  };
16325
16326  /**
16327   * Create menu from chapter track
16328   *
16329   * @return {Menu}
16330   *         New menu for the chapter buttons
16331   */
16332
16333
16334  ChaptersButton.prototype.createMenu = function createMenu() {
16335    this.options_.title = this.getMenuCaption();
16336    return _TextTrackButton.prototype.createMenu.call(this);
16337  };
16338
16339  /**
16340   * Create a menu item for each text track
16341   *
16342   * @return {TextTrackMenuItem[]}
16343   *         Array of menu items
16344   */
16345
16346
16347  ChaptersButton.prototype.createItems = function createItems() {
16348    var items = [];
16349
16350    if (!this.track_) {
16351      return items;
16352    }
16353
16354    var cues = this.track_.cues;
16355
16356    if (!cues) {
16357      return items;
16358    }
16359
16360    for (var i = 0, l = cues.length; i < l; i++) {
16361      var cue = cues[i];
16362      var mi = new ChaptersTrackMenuItem(this.player_, { track: this.track_, cue: cue });
16363
16364      items.push(mi);
16365    }
16366
16367    return items;
16368  };
16369
16370  return ChaptersButton;
16371}(TextTrackButton);
16372
16373/**
16374 * `kind` of TextTrack to look for to associate it with this menu.
16375 *
16376 * @type {string}
16377 * @private
16378 */
16379
16380
16381ChaptersButton.prototype.kind_ = 'chapters';
16382
16383/**
16384 * The text that should display over the `ChaptersButton`s controls. Added for localization.
16385 *
16386 * @type {string}
16387 * @private
16388 */
16389ChaptersButton.prototype.controlText_ = 'Chapters';
16390
16391Component.registerComponent('ChaptersButton', ChaptersButton);
16392
16393/**
16394 * @file descriptions-button.js
16395 */
16396/**
16397 * The button component for toggling and selecting descriptions
16398 *
16399 * @extends TextTrackButton
16400 */
16401
16402var DescriptionsButton = function (_TextTrackButton) {
16403  inherits(DescriptionsButton, _TextTrackButton);
16404
16405  /**
16406   * Creates an instance of this class.
16407   *
16408   * @param {Player} player
16409   *        The `Player` that this class should be attached to.
16410   *
16411   * @param {Object} [options]
16412   *        The key/value store of player options.
16413   *
16414   * @param {Component~ReadyCallback} [ready]
16415   *        The function to call when this component is ready.
16416   */
16417  function DescriptionsButton(player, options, ready) {
16418    classCallCheck(this, DescriptionsButton);
16419
16420    var _this = possibleConstructorReturn(this, _TextTrackButton.call(this, player, options, ready));
16421
16422    var tracks = player.textTracks();
16423    var changeHandler = bind(_this, _this.handleTracksChange);
16424
16425    tracks.addEventListener('change', changeHandler);
16426    _this.on('dispose', function () {
16427      tracks.removeEventListener('change', changeHandler);
16428    });
16429    return _this;
16430  }
16431
16432  /**
16433   * Handle text track change
16434   *
16435   * @param {EventTarget~Event} event
16436   *        The event that caused this function to run
16437   *
16438   * @listens TextTrackList#change
16439   */
16440
16441
16442  DescriptionsButton.prototype.handleTracksChange = function handleTracksChange(event) {
16443    var tracks = this.player().textTracks();
16444    var disabled = false;
16445
16446    // Check whether a track of a different kind is showing
16447    for (var i = 0, l = tracks.length; i < l; i++) {
16448      var track = tracks[i];
16449
16450      if (track.kind !== this.kind_ && track.mode === 'showing') {
16451        disabled = true;
16452        break;
16453      }
16454    }
16455
16456    // If another track is showing, disable this menu button
16457    if (disabled) {
16458      this.disable();
16459    } else {
16460      this.enable();
16461    }
16462  };
16463
16464  /**
16465   * Builds the default DOM `className`.
16466   *
16467   * @return {string}
16468   *         The DOM `className` for this object.
16469   */
16470
16471
16472  DescriptionsButton.prototype.buildCSSClass = function buildCSSClass() {
16473    return 'vjs-descriptions-button ' + _TextTrackButton.prototype.buildCSSClass.call(this);
16474  };
16475
16476  DescriptionsButton.prototype.buildWrapperCSSClass = function buildWrapperCSSClass() {
16477    return 'vjs-descriptions-button ' + _TextTrackButton.prototype.buildWrapperCSSClass.call(this);
16478  };
16479
16480  return DescriptionsButton;
16481}(TextTrackButton);
16482
16483/**
16484 * `kind` of TextTrack to look for to associate it with this menu.
16485 *
16486 * @type {string}
16487 * @private
16488 */
16489
16490
16491DescriptionsButton.prototype.kind_ = 'descriptions';
16492
16493/**
16494 * The text that should display over the `DescriptionsButton`s controls. Added for localization.
16495 *
16496 * @type {string}
16497 * @private
16498 */
16499DescriptionsButton.prototype.controlText_ = 'Descriptions';
16500
16501Component.registerComponent('DescriptionsButton', DescriptionsButton);
16502
16503/**
16504 * @file subtitles-button.js
16505 */
16506/**
16507 * The button component for toggling and selecting subtitles
16508 *
16509 * @extends TextTrackButton
16510 */
16511
16512var SubtitlesButton = function (_TextTrackButton) {
16513  inherits(SubtitlesButton, _TextTrackButton);
16514
16515  /**
16516   * Creates an instance of this class.
16517   *
16518   * @param {Player} player
16519   *        The `Player` that this class should be attached to.
16520   *
16521   * @param {Object} [options]
16522   *        The key/value store of player options.
16523   *
16524   * @param {Component~ReadyCallback} [ready]
16525   *        The function to call when this component is ready.
16526   */
16527  function SubtitlesButton(player, options, ready) {
16528    classCallCheck(this, SubtitlesButton);
16529    return possibleConstructorReturn(this, _TextTrackButton.call(this, player, options, ready));
16530  }
16531
16532  /**
16533   * Builds the default DOM `className`.
16534   *
16535   * @return {string}
16536   *         The DOM `className` for this object.
16537   */
16538
16539
16540  SubtitlesButton.prototype.buildCSSClass = function buildCSSClass() {
16541    return 'vjs-subtitles-button ' + _TextTrackButton.prototype.buildCSSClass.call(this);
16542  };
16543
16544  SubtitlesButton.prototype.buildWrapperCSSClass = function buildWrapperCSSClass() {
16545    return 'vjs-subtitles-button ' + _TextTrackButton.prototype.buildWrapperCSSClass.call(this);
16546  };
16547
16548  return SubtitlesButton;
16549}(TextTrackButton);
16550
16551/**
16552 * `kind` of TextTrack to look for to associate it with this menu.
16553 *
16554 * @type {string}
16555 * @private
16556 */
16557
16558
16559SubtitlesButton.prototype.kind_ = 'subtitles';
16560
16561/**
16562 * The text that should display over the `SubtitlesButton`s controls. Added for localization.
16563 *
16564 * @type {string}
16565 * @private
16566 */
16567SubtitlesButton.prototype.controlText_ = 'Subtitles';
16568
16569Component.registerComponent('SubtitlesButton', SubtitlesButton);
16570
16571/**
16572 * @file caption-settings-menu-item.js
16573 */
16574/**
16575 * The menu item for caption track settings menu
16576 *
16577 * @extends TextTrackMenuItem
16578 */
16579
16580var CaptionSettingsMenuItem = function (_TextTrackMenuItem) {
16581  inherits(CaptionSettingsMenuItem, _TextTrackMenuItem);
16582
16583  /**
16584   * Creates an instance of this class.
16585   *
16586   * @param {Player} player
16587   *        The `Player` that this class should be attached to.
16588   *
16589   * @param {Object} [options]
16590   *        The key/value store of player options.
16591   */
16592  function CaptionSettingsMenuItem(player, options) {
16593    classCallCheck(this, CaptionSettingsMenuItem);
16594
16595    options.track = {
16596      player: player,
16597      kind: options.kind,
16598      label: options.kind + ' settings',
16599      selectable: false,
16600      'default': false,
16601      mode: 'disabled'
16602    };
16603
16604    // CaptionSettingsMenuItem has no concept of 'selected'
16605    options.selectable = false;
16606
16607    options.name = 'CaptionSettingsMenuItem';
16608
16609    var _this = possibleConstructorReturn(this, _TextTrackMenuItem.call(this, player, options));
16610
16611    _this.addClass('vjs-texttrack-settings');
16612    _this.controlText(', opens ' + options.kind + ' settings dialog');
16613    return _this;
16614  }
16615
16616  /**
16617   * This gets called when an `CaptionSettingsMenuItem` is "clicked". See
16618   * {@link ClickableComponent} for more detailed information on what a click can be.
16619   *
16620   * @param {EventTarget~Event} [event]
16621   *        The `keydown`, `tap`, or `click` event that caused this function to be
16622   *        called.
16623   *
16624   * @listens tap
16625   * @listens click
16626   */
16627
16628
16629  CaptionSettingsMenuItem.prototype.handleClick = function handleClick(event) {
16630    this.player().getChild('textTrackSettings').open();
16631  };
16632
16633  return CaptionSettingsMenuItem;
16634}(TextTrackMenuItem);
16635
16636Component.registerComponent('CaptionSettingsMenuItem', CaptionSettingsMenuItem);
16637
16638/**
16639 * @file captions-button.js
16640 */
16641/**
16642 * The button component for toggling and selecting captions
16643 *
16644 * @extends TextTrackButton
16645 */
16646
16647var CaptionsButton = function (_TextTrackButton) {
16648  inherits(CaptionsButton, _TextTrackButton);
16649
16650  /**
16651   * Creates an instance of this class.
16652   *
16653   * @param {Player} player
16654   *        The `Player` that this class should be attached to.
16655   *
16656   * @param {Object} [options]
16657   *        The key/value store of player options.
16658   *
16659   * @param {Component~ReadyCallback} [ready]
16660   *        The function to call when this component is ready.
16661   */
16662  function CaptionsButton(player, options, ready) {
16663    classCallCheck(this, CaptionsButton);
16664    return possibleConstructorReturn(this, _TextTrackButton.call(this, player, options, ready));
16665  }
16666
16667  /**
16668   * Builds the default DOM `className`.
16669   *
16670   * @return {string}
16671   *         The DOM `className` for this object.
16672   */
16673
16674
16675  CaptionsButton.prototype.buildCSSClass = function buildCSSClass() {
16676    return 'vjs-captions-button ' + _TextTrackButton.prototype.buildCSSClass.call(this);
16677  };
16678
16679  CaptionsButton.prototype.buildWrapperCSSClass = function buildWrapperCSSClass() {
16680    return 'vjs-captions-button ' + _TextTrackButton.prototype.buildWrapperCSSClass.call(this);
16681  };
16682
16683  /**
16684   * Create caption menu items
16685   *
16686   * @return {CaptionSettingsMenuItem[]}
16687   *         The array of current menu items.
16688   */
16689
16690
16691  CaptionsButton.prototype.createItems = function createItems() {
16692    var items = [];
16693
16694    if (!(this.player().tech_ && this.player().tech_.featuresNativeTextTracks)) {
16695      items.push(new CaptionSettingsMenuItem(this.player_, { kind: this.kind_ }));
16696
16697      this.hideThreshold_ += 1;
16698    }
16699
16700    return _TextTrackButton.prototype.createItems.call(this, items);
16701  };
16702
16703  return CaptionsButton;
16704}(TextTrackButton);
16705
16706/**
16707 * `kind` of TextTrack to look for to associate it with this menu.
16708 *
16709 * @type {string}
16710 * @private
16711 */
16712
16713
16714CaptionsButton.prototype.kind_ = 'captions';
16715
16716/**
16717 * The text that should display over the `CaptionsButton`s controls. Added for localization.
16718 *
16719 * @type {string}
16720 * @private
16721 */
16722CaptionsButton.prototype.controlText_ = 'Captions';
16723
16724Component.registerComponent('CaptionsButton', CaptionsButton);
16725
16726/**
16727 * @file subs-caps-menu-item.js
16728 */
16729/**
16730 * SubsCapsMenuItem has an [cc] icon to distinguish captions from subtitles
16731 * in the SubsCapsMenu.
16732 *
16733 * @extends TextTrackMenuItem
16734 */
16735
16736var SubsCapsMenuItem = function (_TextTrackMenuItem) {
16737  inherits(SubsCapsMenuItem, _TextTrackMenuItem);
16738
16739  function SubsCapsMenuItem() {
16740    classCallCheck(this, SubsCapsMenuItem);
16741    return possibleConstructorReturn(this, _TextTrackMenuItem.apply(this, arguments));
16742  }
16743
16744  SubsCapsMenuItem.prototype.createEl = function createEl(type, props, attrs) {
16745    var innerHTML = '<span class="vjs-menu-item-text">' + this.localize(this.options_.label);
16746
16747    if (this.options_.track.kind === 'captions') {
16748      innerHTML += '\n        <span aria-hidden="true" class="vjs-icon-placeholder"></span>\n        <span class="vjs-control-text"> ' + this.localize('Captions') + '</span>\n      ';
16749    }
16750
16751    innerHTML += '</span>';
16752
16753    var el = _TextTrackMenuItem.prototype.createEl.call(this, type, assign({
16754      innerHTML: innerHTML
16755    }, props), attrs);
16756
16757    return el;
16758  };
16759
16760  return SubsCapsMenuItem;
16761}(TextTrackMenuItem);
16762
16763Component.registerComponent('SubsCapsMenuItem', SubsCapsMenuItem);
16764
16765/**
16766 * @file sub-caps-button.js
16767 */
16768/**
16769 * The button component for toggling and selecting captions and/or subtitles
16770 *
16771 * @extends TextTrackButton
16772 */
16773
16774var SubsCapsButton = function (_TextTrackButton) {
16775  inherits(SubsCapsButton, _TextTrackButton);
16776
16777  function SubsCapsButton(player) {
16778    var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
16779    classCallCheck(this, SubsCapsButton);
16780
16781    // Although North America uses "captions" in most cases for
16782    // "captions and subtitles" other locales use "subtitles"
16783    var _this = possibleConstructorReturn(this, _TextTrackButton.call(this, player, options));
16784
16785    _this.label_ = 'subtitles';
16786    if (['en', 'en-us', 'en-ca', 'fr-ca'].indexOf(_this.player_.language_) > -1) {
16787      _this.label_ = 'captions';
16788    }
16789    _this.menuButton_.controlText(toTitleCase(_this.label_));
16790    return _this;
16791  }
16792
16793  /**
16794   * Builds the default DOM `className`.
16795   *
16796   * @return {string}
16797   *         The DOM `className` for this object.
16798   */
16799
16800
16801  SubsCapsButton.prototype.buildCSSClass = function buildCSSClass() {
16802    return 'vjs-subs-caps-button ' + _TextTrackButton.prototype.buildCSSClass.call(this);
16803  };
16804
16805  SubsCapsButton.prototype.buildWrapperCSSClass = function buildWrapperCSSClass() {
16806    return 'vjs-subs-caps-button ' + _TextTrackButton.prototype.buildWrapperCSSClass.call(this);
16807  };
16808
16809  /**
16810   * Create caption/subtitles menu items
16811   *
16812   * @return {CaptionSettingsMenuItem[]}
16813   *         The array of current menu items.
16814   */
16815
16816
16817  SubsCapsButton.prototype.createItems = function createItems() {
16818    var items = [];
16819
16820    if (!(this.player().tech_ && this.player().tech_.featuresNativeTextTracks)) {
16821      items.push(new CaptionSettingsMenuItem(this.player_, { kind: this.label_ }));
16822
16823      this.hideThreshold_ += 1;
16824    }
16825
16826    items = _TextTrackButton.prototype.createItems.call(this, items, SubsCapsMenuItem);
16827    return items;
16828  };
16829
16830  return SubsCapsButton;
16831}(TextTrackButton);
16832
16833/**
16834 * `kind`s of TextTrack to look for to associate it with this menu.
16835 *
16836 * @type {array}
16837 * @private
16838 */
16839
16840
16841SubsCapsButton.prototype.kinds_ = ['captions', 'subtitles'];
16842
16843/**
16844 * The text that should display over the `SubsCapsButton`s controls.
16845 *
16846 *
16847 * @type {string}
16848 * @private
16849 */
16850SubsCapsButton.prototype.controlText_ = 'Subtitles';
16851
16852Component.registerComponent('SubsCapsButton', SubsCapsButton);
16853
16854/**
16855 * @file audio-track-menu-item.js
16856 */
16857/**
16858 * An {@link AudioTrack} {@link MenuItem}
16859 *
16860 * @extends MenuItem
16861 */
16862
16863var AudioTrackMenuItem = function (_MenuItem) {
16864  inherits(AudioTrackMenuItem, _MenuItem);
16865
16866  /**
16867   * Creates an instance of this class.
16868   *
16869   * @param {Player} player
16870   *        The `Player` that this class should be attached to.
16871   *
16872   * @param {Object} [options]
16873   *        The key/value store of player options.
16874   */
16875  function AudioTrackMenuItem(player, options) {
16876    classCallCheck(this, AudioTrackMenuItem);
16877
16878    var track = options.track;
16879    var tracks = player.audioTracks();
16880
16881    // Modify options for parent MenuItem class's init.
16882    options.label = track.label || track.language || 'Unknown';
16883    options.selected = track.enabled;
16884
16885    var _this = possibleConstructorReturn(this, _MenuItem.call(this, player, options));
16886
16887    _this.track = track;
16888
16889    var changeHandler = bind(_this, _this.handleTracksChange);
16890
16891    tracks.addEventListener('change', changeHandler);
16892    _this.on('dispose', function () {
16893      tracks.removeEventListener('change', changeHandler);
16894    });
16895    return _this;
16896  }
16897
16898  /**
16899   * This gets called when an `AudioTrackMenuItem is "clicked". See {@link ClickableComponent}
16900   * for more detailed information on what a click can be.
16901   *
16902   * @param {EventTarget~Event} [event]
16903   *        The `keydown`, `tap`, or `click` event that caused this function to be
16904   *        called.
16905   *
16906   * @listens tap
16907   * @listens click
16908   */
16909
16910
16911  AudioTrackMenuItem.prototype.handleClick = function handleClick(event) {
16912    var tracks = this.player_.audioTracks();
16913
16914    _MenuItem.prototype.handleClick.call(this, event);
16915
16916    for (var i = 0; i < tracks.length; i++) {
16917      var track = tracks[i];
16918
16919      track.enabled = track === this.track;
16920    }
16921  };
16922
16923  /**
16924   * Handle any {@link AudioTrack} change.
16925   *
16926   * @param {EventTarget~Event} [event]
16927   *        The {@link AudioTrackList#change} event that caused this to run.
16928   *
16929   * @listens AudioTrackList#change
16930   */
16931
16932
16933  AudioTrackMenuItem.prototype.handleTracksChange = function handleTracksChange(event) {
16934    this.selected(this.track.enabled);
16935  };
16936
16937  return AudioTrackMenuItem;
16938}(MenuItem);
16939
16940Component.registerComponent('AudioTrackMenuItem', AudioTrackMenuItem);
16941
16942/**
16943 * @file audio-track-button.js
16944 */
16945/**
16946 * The base class for buttons that toggle specific {@link AudioTrack} types.
16947 *
16948 * @extends TrackButton
16949 */
16950
16951var AudioTrackButton = function (_TrackButton) {
16952  inherits(AudioTrackButton, _TrackButton);
16953
16954  /**
16955   * Creates an instance of this class.
16956   *
16957   * @param {Player} player
16958   *        The `Player` that this class should be attached to.
16959   *
16960   * @param {Object} [options={}]
16961   *        The key/value store of player options.
16962   */
16963  function AudioTrackButton(player) {
16964    var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
16965    classCallCheck(this, AudioTrackButton);
16966
16967    options.tracks = player.audioTracks();
16968
16969    return possibleConstructorReturn(this, _TrackButton.call(this, player, options));
16970  }
16971
16972  /**
16973   * Builds the default DOM `className`.
16974   *
16975   * @return {string}
16976   *         The DOM `className` for this object.
16977   */
16978
16979
16980  AudioTrackButton.prototype.buildCSSClass = function buildCSSClass() {
16981    return 'vjs-audio-button ' + _TrackButton.prototype.buildCSSClass.call(this);
16982  };
16983
16984  AudioTrackButton.prototype.buildWrapperCSSClass = function buildWrapperCSSClass() {
16985    return 'vjs-audio-button ' + _TrackButton.prototype.buildWrapperCSSClass.call(this);
16986  };
16987
16988  /**
16989   * Create a menu item for each audio track
16990   *
16991   * @param {AudioTrackMenuItem[]} [items=[]]
16992   *        An array of existing menu items to use.
16993   *
16994   * @return {AudioTrackMenuItem[]}
16995   *         An array of menu items
16996   */
16997
16998
16999  AudioTrackButton.prototype.createItems = function createItems() {
17000    var items = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : [];
17001
17002    // if there's only one audio track, there no point in showing it
17003    this.hideThreshold_ = 1;
17004
17005    var tracks = this.player_.audioTracks();
17006
17007    for (var i = 0; i < tracks.length; i++) {
17008      var track = tracks[i];
17009
17010      items.push(new AudioTrackMenuItem(this.player_, {
17011        track: track,
17012        // MenuItem is selectable
17013        selectable: true
17014      }));
17015    }
17016
17017    return items;
17018  };
17019
17020  return AudioTrackButton;
17021}(TrackButton);
17022
17023/**
17024 * The text that should display over the `AudioTrackButton`s controls. Added for localization.
17025 *
17026 * @type {string}
17027 * @private
17028 */
17029
17030
17031AudioTrackButton.prototype.controlText_ = 'Audio Track';
17032Component.registerComponent('AudioTrackButton', AudioTrackButton);
17033
17034/**
17035 * @file playback-rate-menu-item.js
17036 */
17037/**
17038 * The specific menu item type for selecting a playback rate.
17039 *
17040 * @extends MenuItem
17041 */
17042
17043var PlaybackRateMenuItem = function (_MenuItem) {
17044  inherits(PlaybackRateMenuItem, _MenuItem);
17045
17046  /**
17047   * Creates an instance of this class.
17048   *
17049   * @param {Player} player
17050   *        The `Player` that this class should be attached to.
17051   *
17052   * @param {Object} [options]
17053   *        The key/value store of player options.
17054   */
17055  function PlaybackRateMenuItem(player, options) {
17056    classCallCheck(this, PlaybackRateMenuItem);
17057
17058    var label = options.rate;
17059    var rate = parseFloat(label, 10);
17060
17061    // Modify options for parent MenuItem class's init.
17062    options.label = label;
17063    options.selected = rate === 1;
17064    options.selectable = true;
17065
17066    var _this = possibleConstructorReturn(this, _MenuItem.call(this, player, options));
17067
17068    _this.label = label;
17069    _this.rate = rate;
17070
17071    _this.on(player, 'ratechange', _this.update);
17072    return _this;
17073  }
17074
17075  /**
17076   * This gets called when an `PlaybackRateMenuItem` is "clicked". See
17077   * {@link ClickableComponent} for more detailed information on what a click can be.
17078   *
17079   * @param {EventTarget~Event} [event]
17080   *        The `keydown`, `tap`, or `click` event that caused this function to be
17081   *        called.
17082   *
17083   * @listens tap
17084   * @listens click
17085   */
17086
17087
17088  PlaybackRateMenuItem.prototype.handleClick = function handleClick(event) {
17089    _MenuItem.prototype.handleClick.call(this);
17090    this.player().playbackRate(this.rate);
17091  };
17092
17093  /**
17094   * Update the PlaybackRateMenuItem when the playbackrate changes.
17095   *
17096   * @param {EventTarget~Event} [event]
17097   *        The `ratechange` event that caused this function to run.
17098   *
17099   * @listens Player#ratechange
17100   */
17101
17102
17103  PlaybackRateMenuItem.prototype.update = function update(event) {
17104    this.selected(this.player().playbackRate() === this.rate);
17105  };
17106
17107  return PlaybackRateMenuItem;
17108}(MenuItem);
17109
17110/**
17111 * The text that should display over the `PlaybackRateMenuItem`s controls. Added for localization.
17112 *
17113 * @type {string}
17114 * @private
17115 */
17116
17117
17118PlaybackRateMenuItem.prototype.contentElType = 'button';
17119
17120Component.registerComponent('PlaybackRateMenuItem', PlaybackRateMenuItem);
17121
17122/**
17123 * @file playback-rate-menu-button.js
17124 */
17125/**
17126 * The component for controlling the playback rate.
17127 *
17128 * @extends MenuButton
17129 */
17130
17131var PlaybackRateMenuButton = function (_MenuButton) {
17132  inherits(PlaybackRateMenuButton, _MenuButton);
17133
17134  /**
17135   * Creates an instance of this class.
17136   *
17137   * @param {Player} player
17138   *        The `Player` that this class should be attached to.
17139   *
17140   * @param {Object} [options]
17141   *        The key/value store of player options.
17142   */
17143  function PlaybackRateMenuButton(player, options) {
17144    classCallCheck(this, PlaybackRateMenuButton);
17145
17146    var _this = possibleConstructorReturn(this, _MenuButton.call(this, player, options));
17147
17148    _this.updateVisibility();
17149    _this.updateLabel();
17150
17151    _this.on(player, 'loadstart', _this.updateVisibility);
17152    _this.on(player, 'ratechange', _this.updateLabel);
17153    return _this;
17154  }
17155
17156  /**
17157   * Create the `Component`'s DOM element
17158   *
17159   * @return {Element}
17160   *         The element that was created.
17161   */
17162
17163
17164  PlaybackRateMenuButton.prototype.createEl = function createEl$$1() {
17165    var el = _MenuButton.prototype.createEl.call(this);
17166
17167    this.labelEl_ = createEl('div', {
17168      className: 'vjs-playback-rate-value',
17169      innerHTML: '1x'
17170    });
17171
17172    el.appendChild(this.labelEl_);
17173
17174    return el;
17175  };
17176
17177  /**
17178   * Builds the default DOM `className`.
17179   *
17180   * @return {string}
17181   *         The DOM `className` for this object.
17182   */
17183
17184
17185  PlaybackRateMenuButton.prototype.buildCSSClass = function buildCSSClass() {
17186    return 'vjs-playback-rate ' + _MenuButton.prototype.buildCSSClass.call(this);
17187  };
17188
17189  PlaybackRateMenuButton.prototype.buildWrapperCSSClass = function buildWrapperCSSClass() {
17190    return 'vjs-playback-rate ' + _MenuButton.prototype.buildWrapperCSSClass.call(this);
17191  };
17192
17193  /**
17194   * Create the playback rate menu
17195   *
17196   * @return {Menu}
17197   *         Menu object populated with {@link PlaybackRateMenuItem}s
17198   */
17199
17200
17201  PlaybackRateMenuButton.prototype.createMenu = function createMenu() {
17202    var menu = new Menu(this.player());
17203    var rates = this.playbackRates();
17204
17205    if (rates) {
17206      for (var i = rates.length - 1; i >= 0; i--) {
17207        menu.addChild(new PlaybackRateMenuItem(this.player(), { rate: rates[i] + 'x' }));
17208      }
17209    }
17210
17211    return menu;
17212  };
17213
17214  /**
17215   * Updates ARIA accessibility attributes
17216   */
17217
17218
17219  PlaybackRateMenuButton.prototype.updateARIAAttributes = function updateARIAAttributes() {
17220    // Current playback rate
17221    this.el().setAttribute('aria-valuenow', this.player().playbackRate());
17222  };
17223
17224  /**
17225   * This gets called when an `PlaybackRateMenuButton` is "clicked". See
17226   * {@link ClickableComponent} for more detailed information on what a click can be.
17227   *
17228   * @param {EventTarget~Event} [event]
17229   *        The `keydown`, `tap`, or `click` event that caused this function to be
17230   *        called.
17231   *
17232   * @listens tap
17233   * @listens click
17234   */
17235
17236
17237  PlaybackRateMenuButton.prototype.handleClick = function handleClick(event) {
17238    // select next rate option
17239    var currentRate = this.player().playbackRate();
17240    var rates = this.playbackRates();
17241
17242    // this will select first one if the last one currently selected
17243    var newRate = rates[0];
17244
17245    for (var i = 0; i < rates.length; i++) {
17246      if (rates[i] > currentRate) {
17247        newRate = rates[i];
17248        break;
17249      }
17250    }
17251    this.player().playbackRate(newRate);
17252  };
17253
17254  /**
17255   * Get possible playback rates
17256   *
17257   * @return {Array}
17258   *         All possible playback rates
17259   */
17260
17261
17262  PlaybackRateMenuButton.prototype.playbackRates = function playbackRates() {
17263    return this.options_.playbackRates || this.options_.playerOptions && this.options_.playerOptions.playbackRates;
17264  };
17265
17266  /**
17267   * Get whether playback rates is supported by the tech
17268   * and an array of playback rates exists
17269   *
17270   * @return {boolean}
17271   *         Whether changing playback rate is supported
17272   */
17273
17274
17275  PlaybackRateMenuButton.prototype.playbackRateSupported = function playbackRateSupported() {
17276    return this.player().tech_ && this.player().tech_.featuresPlaybackRate && this.playbackRates() && this.playbackRates().length > 0;
17277  };
17278
17279  /**
17280   * Hide playback rate controls when they're no playback rate options to select
17281   *
17282   * @param {EventTarget~Event} [event]
17283   *        The event that caused this function to run.
17284   *
17285   * @listens Player#loadstart
17286   */
17287
17288
17289  PlaybackRateMenuButton.prototype.updateVisibility = function updateVisibility(event) {
17290    if (this.playbackRateSupported()) {
17291      this.removeClass('vjs-hidden');
17292    } else {
17293      this.addClass('vjs-hidden');
17294    }
17295  };
17296
17297  /**
17298   * Update button label when rate changed
17299   *
17300   * @param {EventTarget~Event} [event]
17301   *        The event that caused this function to run.
17302   *
17303   * @listens Player#ratechange
17304   */
17305
17306
17307  PlaybackRateMenuButton.prototype.updateLabel = function updateLabel(event) {
17308    if (this.playbackRateSupported()) {
17309      this.labelEl_.innerHTML = this.player().playbackRate() + 'x';
17310    }
17311  };
17312
17313  return PlaybackRateMenuButton;
17314}(MenuButton);
17315
17316/**
17317 * The text that should display over the `FullscreenToggle`s controls. Added for localization.
17318 *
17319 * @type {string}
17320 * @private
17321 */
17322
17323
17324PlaybackRateMenuButton.prototype.controlText_ = 'Playback Rate';
17325
17326Component.registerComponent('PlaybackRateMenuButton', PlaybackRateMenuButton);
17327
17328/**
17329 * @file spacer.js
17330 */
17331/**
17332 * Just an empty spacer element that can be used as an append point for plugins, etc.
17333 * Also can be used to create space between elements when necessary.
17334 *
17335 * @extends Component
17336 */
17337
17338var Spacer = function (_Component) {
17339  inherits(Spacer, _Component);
17340
17341  function Spacer() {
17342    classCallCheck(this, Spacer);
17343    return possibleConstructorReturn(this, _Component.apply(this, arguments));
17344  }
17345
17346  /**
17347   * Builds the default DOM `className`.
17348   *
17349   * @return {string}
17350   *         The DOM `className` for this object.
17351   */
17352  Spacer.prototype.buildCSSClass = function buildCSSClass() {
17353    return 'vjs-spacer ' + _Component.prototype.buildCSSClass.call(this);
17354  };
17355
17356  /**
17357   * Create the `Component`'s DOM element
17358   *
17359   * @return {Element}
17360   *         The element that was created.
17361   */
17362
17363
17364  Spacer.prototype.createEl = function createEl() {
17365    return _Component.prototype.createEl.call(this, 'div', {
17366      className: this.buildCSSClass()
17367    });
17368  };
17369
17370  return Spacer;
17371}(Component);
17372
17373Component.registerComponent('Spacer', Spacer);
17374
17375/**
17376 * @file custom-control-spacer.js
17377 */
17378/**
17379 * Spacer specifically meant to be used as an insertion point for new plugins, etc.
17380 *
17381 * @extends Spacer
17382 */
17383
17384var CustomControlSpacer = function (_Spacer) {
17385  inherits(CustomControlSpacer, _Spacer);
17386
17387  function CustomControlSpacer() {
17388    classCallCheck(this, CustomControlSpacer);
17389    return possibleConstructorReturn(this, _Spacer.apply(this, arguments));
17390  }
17391
17392  /**
17393   * Builds the default DOM `className`.
17394   *
17395   * @return {string}
17396   *         The DOM `className` for this object.
17397   */
17398  CustomControlSpacer.prototype.buildCSSClass = function buildCSSClass() {
17399    return 'vjs-custom-control-spacer ' + _Spacer.prototype.buildCSSClass.call(this);
17400  };
17401
17402  /**
17403   * Create the `Component`'s DOM element
17404   *
17405   * @return {Element}
17406   *         The element that was created.
17407   */
17408
17409
17410  CustomControlSpacer.prototype.createEl = function createEl() {
17411    var el = _Spacer.prototype.createEl.call(this, {
17412      className: this.buildCSSClass()
17413    });
17414
17415    // No-flex/table-cell mode requires there be some content
17416    // in the cell to fill the remaining space of the table.
17417    el.innerHTML = '&nbsp;';
17418    return el;
17419  };
17420
17421  return CustomControlSpacer;
17422}(Spacer);
17423
17424Component.registerComponent('CustomControlSpacer', CustomControlSpacer);
17425
17426/**
17427 * @file control-bar.js
17428 */
17429// Required children
17430/**
17431 * Container of main controls.
17432 *
17433 * @extends Component
17434 */
17435
17436var ControlBar = function (_Component) {
17437  inherits(ControlBar, _Component);
17438
17439  function ControlBar() {
17440    classCallCheck(this, ControlBar);
17441    return possibleConstructorReturn(this, _Component.apply(this, arguments));
17442  }
17443
17444  /**
17445   * Create the `Component`'s DOM element
17446   *
17447   * @return {Element}
17448   *         The element that was created.
17449   */
17450  ControlBar.prototype.createEl = function createEl() {
17451    return _Component.prototype.createEl.call(this, 'div', {
17452      className: 'vjs-control-bar',
17453      dir: 'ltr'
17454    }, {
17455      // The control bar is a group, but we don't aria-label it to avoid
17456      //  over-announcing by JAWS
17457      role: 'group'
17458    });
17459  };
17460
17461  return ControlBar;
17462}(Component);
17463
17464/**
17465 * Default options for `ControlBar`
17466 *
17467 * @type {Object}
17468 * @private
17469 */
17470
17471
17472ControlBar.prototype.options_ = {
17473  children: ['playToggle', 'volumePanel', 'currentTimeDisplay', 'timeDivider', 'durationDisplay', 'progressControl', 'liveDisplay', 'remainingTimeDisplay', 'customControlSpacer', 'playbackRateMenuButton', 'chaptersButton', 'descriptionsButton', 'subsCapsButton', 'audioTrackButton', 'fullscreenToggle']
17474};
17475
17476Component.registerComponent('ControlBar', ControlBar);
17477
17478/**
17479 * @file error-display.js
17480 */
17481/**
17482 * A display that indicates an error has occurred. This means that the video
17483 * is unplayable.
17484 *
17485 * @extends ModalDialog
17486 */
17487
17488var ErrorDisplay = function (_ModalDialog) {
17489  inherits(ErrorDisplay, _ModalDialog);
17490
17491  /**
17492   * Creates an instance of this class.
17493   *
17494   * @param  {Player} player
17495   *         The `Player` that this class should be attached to.
17496   *
17497   * @param  {Object} [options]
17498   *         The key/value store of player options.
17499   */
17500  function ErrorDisplay(player, options) {
17501    classCallCheck(this, ErrorDisplay);
17502
17503    var _this = possibleConstructorReturn(this, _ModalDialog.call(this, player, options));
17504
17505    _this.on(player, 'error', _this.open);
17506    return _this;
17507  }
17508
17509  /**
17510   * Builds the default DOM `className`.
17511   *
17512   * @return {string}
17513   *         The DOM `className` for this object.
17514   *
17515   * @deprecated Since version 5.
17516   */
17517
17518
17519  ErrorDisplay.prototype.buildCSSClass = function buildCSSClass() {
17520    return 'vjs-error-display ' + _ModalDialog.prototype.buildCSSClass.call(this);
17521  };
17522
17523  /**
17524   * Gets the localized error message based on the `Player`s error.
17525   *
17526   * @return {string}
17527   *         The `Player`s error message localized or an empty string.
17528   */
17529
17530
17531  ErrorDisplay.prototype.content = function content() {
17532    var error = this.player().error();
17533
17534    return error ? this.localize(error.message) : '';
17535  };
17536
17537  return ErrorDisplay;
17538}(ModalDialog);
17539
17540/**
17541 * The default options for an `ErrorDisplay`.
17542 *
17543 * @private
17544 */
17545
17546
17547ErrorDisplay.prototype.options_ = mergeOptions(ModalDialog.prototype.options_, {
17548  pauseOnOpen: false,
17549  fillAlways: true,
17550  temporary: false,
17551  uncloseable: true
17552});
17553
17554Component.registerComponent('ErrorDisplay', ErrorDisplay);
17555
17556/**
17557 * @file text-track-settings.js
17558 */
17559var LOCAL_STORAGE_KEY = 'vjs-text-track-settings';
17560
17561var COLOR_BLACK = ['#000', 'Black'];
17562var COLOR_BLUE = ['#00F', 'Blue'];
17563var COLOR_CYAN = ['#0FF', 'Cyan'];
17564var COLOR_GREEN = ['#0F0', 'Green'];
17565var COLOR_MAGENTA = ['#F0F', 'Magenta'];
17566var COLOR_RED = ['#F00', 'Red'];
17567var COLOR_WHITE = ['#FFF', 'White'];
17568var COLOR_YELLOW = ['#FF0', 'Yellow'];
17569
17570var OPACITY_OPAQUE = ['1', 'Opaque'];
17571var OPACITY_SEMI = ['0.5', 'Semi-Transparent'];
17572var OPACITY_TRANS = ['0', 'Transparent'];
17573
17574// Configuration for the various <select> elements in the DOM of this component.
17575//
17576// Possible keys include:
17577//
17578// `default`:
17579//   The default option index. Only needs to be provided if not zero.
17580// `parser`:
17581//   A function which is used to parse the value from the selected option in
17582//   a customized way.
17583// `selector`:
17584//   The selector used to find the associated <select> element.
17585var selectConfigs = {
17586  backgroundColor: {
17587    selector: '.vjs-bg-color > select',
17588    id: 'captions-background-color-%s',
17589    label: 'Color',
17590    options: [COLOR_BLACK, COLOR_WHITE, COLOR_RED, COLOR_GREEN, COLOR_BLUE, COLOR_YELLOW, COLOR_MAGENTA, COLOR_CYAN]
17591  },
17592
17593  backgroundOpacity: {
17594    selector: '.vjs-bg-opacity > select',
17595    id: 'captions-background-opacity-%s',
17596    label: 'Transparency',
17597    options: [OPACITY_OPAQUE, OPACITY_SEMI, OPACITY_TRANS]
17598  },
17599
17600  color: {
17601    selector: '.vjs-fg-color > select',
17602    id: 'captions-foreground-color-%s',
17603    label: 'Color',
17604    options: [COLOR_WHITE, COLOR_BLACK, COLOR_RED, COLOR_GREEN, COLOR_BLUE, COLOR_YELLOW, COLOR_MAGENTA, COLOR_CYAN]
17605  },
17606
17607  edgeStyle: {
17608    selector: '.vjs-edge-style > select',
17609    id: '%s',
17610    label: 'Text Edge Style',
17611    options: [['none', 'None'], ['raised', 'Raised'], ['depressed', 'Depressed'], ['uniform', 'Uniform'], ['dropshadow', 'Dropshadow']]
17612  },
17613
17614  fontFamily: {
17615    selector: '.vjs-font-family > select',
17616    id: 'captions-font-family-%s',
17617    label: 'Font Family',
17618    options: [['proportionalSansSerif', 'Proportional Sans-Serif'], ['monospaceSansSerif', 'Monospace Sans-Serif'], ['proportionalSerif', 'Proportional Serif'], ['monospaceSerif', 'Monospace Serif'], ['casual', 'Casual'], ['script', 'Script'], ['small-caps', 'Small Caps']]
17619  },
17620
17621  fontPercent: {
17622    selector: '.vjs-font-percent > select',
17623    id: 'captions-font-size-%s',
17624    label: 'Font Size',
17625    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%']],
17626    'default': 2,
17627    parser: function parser(v) {
17628      return v === '1.00' ? null : Number(v);
17629    }
17630  },
17631
17632  textOpacity: {
17633    selector: '.vjs-text-opacity > select',
17634    id: 'captions-foreground-opacity-%s',
17635    label: 'Transparency',
17636    options: [OPACITY_OPAQUE, OPACITY_SEMI]
17637  },
17638
17639  // Options for this object are defined below.
17640  windowColor: {
17641    selector: '.vjs-window-color > select',
17642    id: 'captions-window-color-%s',
17643    label: 'Color'
17644  },
17645
17646  // Options for this object are defined below.
17647  windowOpacity: {
17648    selector: '.vjs-window-opacity > select',
17649    id: 'captions-window-opacity-%s',
17650    label: 'Transparency',
17651    options: [OPACITY_TRANS, OPACITY_SEMI, OPACITY_OPAQUE]
17652  }
17653};
17654
17655selectConfigs.windowColor.options = selectConfigs.backgroundColor.options;
17656
17657/**
17658 * Get the actual value of an option.
17659 *
17660 * @param  {string} value
17661 *         The value to get
17662 *
17663 * @param  {Function} [parser]
17664 *         Optional function to adjust the value.
17665 *
17666 * @return {Mixed}
17667 *         - Will be `undefined` if no value exists
17668 *         - Will be `undefined` if the given value is "none".
17669 *         - Will be the actual value otherwise.
17670 *
17671 * @private
17672 */
17673function parseOptionValue(value, parser) {
17674  if (parser) {
17675    value = parser(value);
17676  }
17677
17678  if (value && value !== 'none') {
17679    return value;
17680  }
17681}
17682
17683/**
17684 * Gets the value of the selected <option> element within a <select> element.
17685 *
17686 * @param  {Element} el
17687 *         the element to look in
17688 *
17689 * @param  {Function} [parser]
17690 *         Optional function to adjust the value.
17691 *
17692 * @return {Mixed}
17693 *         - Will be `undefined` if no value exists
17694 *         - Will be `undefined` if the given value is "none".
17695 *         - Will be the actual value otherwise.
17696 *
17697 * @private
17698 */
17699function getSelectedOptionValue(el, parser) {
17700  var value = el.options[el.options.selectedIndex].value;
17701
17702  return parseOptionValue(value, parser);
17703}
17704
17705/**
17706 * Sets the selected <option> element within a <select> element based on a
17707 * given value.
17708 *
17709 * @param {Element} el
17710 *        The element to look in.
17711 *
17712 * @param {string} value
17713 *        the property to look on.
17714 *
17715 * @param {Function} [parser]
17716 *        Optional function to adjust the value before comparing.
17717 *
17718 * @private
17719 */
17720function setSelectedOption(el, value, parser) {
17721  if (!value) {
17722    return;
17723  }
17724
17725  for (var i = 0; i < el.options.length; i++) {
17726    if (parseOptionValue(el.options[i].value, parser) === value) {
17727      el.selectedIndex = i;
17728      break;
17729    }
17730  }
17731}
17732
17733/**
17734 * Manipulate Text Tracks settings.
17735 *
17736 * @extends ModalDialog
17737 */
17738
17739var TextTrackSettings = function (_ModalDialog) {
17740  inherits(TextTrackSettings, _ModalDialog);
17741
17742  /**
17743   * Creates an instance of this class.
17744   *
17745   * @param {Player} player
17746   *         The `Player` that this class should be attached to.
17747   *
17748   * @param {Object} [options]
17749   *         The key/value store of player options.
17750   */
17751  function TextTrackSettings(player, options) {
17752    classCallCheck(this, TextTrackSettings);
17753
17754    options.temporary = false;
17755
17756    var _this = possibleConstructorReturn(this, _ModalDialog.call(this, player, options));
17757
17758    _this.updateDisplay = bind(_this, _this.updateDisplay);
17759
17760    // fill the modal and pretend we have opened it
17761    _this.fill();
17762    _this.hasBeenOpened_ = _this.hasBeenFilled_ = true;
17763
17764    _this.endDialog = createEl('p', {
17765      className: 'vjs-control-text',
17766      textContent: _this.localize('End of dialog window.')
17767    });
17768    _this.el().appendChild(_this.endDialog);
17769
17770    _this.setDefaults();
17771
17772    // Grab `persistTextTrackSettings` from the player options if not passed in child options
17773    if (options.persistTextTrackSettings === undefined) {
17774      _this.options_.persistTextTrackSettings = _this.options_.playerOptions.persistTextTrackSettings;
17775    }
17776
17777    _this.on(_this.$('.vjs-done-button'), 'click', function () {
17778      _this.saveSettings();
17779      _this.close();
17780    });
17781
17782    _this.on(_this.$('.vjs-default-button'), 'click', function () {
17783      _this.setDefaults();
17784      _this.updateDisplay();
17785    });
17786
17787    each(selectConfigs, function (config) {
17788      _this.on(_this.$(config.selector), 'change', _this.updateDisplay);
17789    });
17790
17791    if (_this.options_.persistTextTrackSettings) {
17792      _this.restoreSettings();
17793    }
17794    return _this;
17795  }
17796
17797  /**
17798   * Create a <select> element with configured options.
17799   *
17800   * @param {string} key
17801   *        Configuration key to use during creation.
17802   *
17803   * @return {string}
17804   *         An HTML string.
17805   *
17806   * @private
17807   */
17808
17809
17810  TextTrackSettings.prototype.createElSelect_ = function createElSelect_(key) {
17811    var _this2 = this;
17812
17813    var legendId = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : '';
17814    var type = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 'label';
17815
17816    var config = selectConfigs[key];
17817    var id = config.id.replace('%s', this.id_);
17818
17819    return ['<' + type + ' id="' + id + '" class="' + (type === 'label' ? 'vjs-label' : '') + '">', this.localize(config.label), '</' + type + '>', '<select aria-labelledby="' + legendId + ' ' + id + '">'].concat(config.options.map(function (o) {
17820      var optionId = id + '-' + o[1];
17821
17822      return ['<option id="' + optionId + '" value="' + o[0] + '" ', 'aria-labelledby="' + legendId + ' ' + id + ' ' + optionId + '">', _this2.localize(o[1]), '</option>'].join('');
17823    })).concat('</select>').join('');
17824  };
17825
17826  /**
17827   * Create foreground color element for the component
17828   *
17829   * @return {string}
17830   *         An HTML string.
17831   *
17832   * @private
17833   */
17834
17835
17836  TextTrackSettings.prototype.createElFgColor_ = function createElFgColor_() {
17837    var legendId = 'captions-text-legend-' + this.id_;
17838
17839    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('');
17840  };
17841
17842  /**
17843   * Create background color element for the component
17844   *
17845   * @return {string}
17846   *         An HTML string.
17847   *
17848   * @private
17849   */
17850
17851
17852  TextTrackSettings.prototype.createElBgColor_ = function createElBgColor_() {
17853    var legendId = 'captions-background-' + this.id_;
17854
17855    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('');
17856  };
17857
17858  /**
17859   * Create window color element for the component
17860   *
17861   * @return {string}
17862   *         An HTML string.
17863   *
17864   * @private
17865   */
17866
17867
17868  TextTrackSettings.prototype.createElWinColor_ = function createElWinColor_() {
17869    var legendId = 'captions-window-' + this.id_;
17870
17871    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('');
17872  };
17873
17874  /**
17875   * Create color elements for the component
17876   *
17877   * @return {Element}
17878   *         The element that was created
17879   *
17880   * @private
17881   */
17882
17883
17884  TextTrackSettings.prototype.createElColors_ = function createElColors_() {
17885    return createEl('div', {
17886      className: 'vjs-track-settings-colors',
17887      innerHTML: [this.createElFgColor_(), this.createElBgColor_(), this.createElWinColor_()].join('')
17888    });
17889  };
17890
17891  /**
17892   * Create font elements for the component
17893   *
17894   * @return {Element}
17895   *         The element that was created.
17896   *
17897   * @private
17898   */
17899
17900
17901  TextTrackSettings.prototype.createElFont_ = function createElFont_() {
17902    return createEl('div', {
17903      className: 'vjs-track-settings-font">',
17904      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('')
17905    });
17906  };
17907
17908  /**
17909   * Create controls for the component
17910   *
17911   * @return {Element}
17912   *         The element that was created.
17913   *
17914   * @private
17915   */
17916
17917
17918  TextTrackSettings.prototype.createElControls_ = function createElControls_() {
17919    var defaultsDescription = this.localize('restore all settings to the default values');
17920
17921    return createEl('div', {
17922      className: 'vjs-track-settings-controls',
17923      innerHTML: ['<button class="vjs-default-button" title="' + defaultsDescription + '">', this.localize('Reset'), '<span class="vjs-control-text"> ' + defaultsDescription + '</span>', '</button>', '<button class="vjs-done-button">' + this.localize('Done') + '</button>'].join('')
17924    });
17925  };
17926
17927  TextTrackSettings.prototype.content = function content() {
17928    return [this.createElColors_(), this.createElFont_(), this.createElControls_()];
17929  };
17930
17931  TextTrackSettings.prototype.label = function label() {
17932    return this.localize('Caption Settings Dialog');
17933  };
17934
17935  TextTrackSettings.prototype.description = function description() {
17936    return this.localize('Beginning of dialog window. Escape will cancel and close the window.');
17937  };
17938
17939  TextTrackSettings.prototype.buildCSSClass = function buildCSSClass() {
17940    return _ModalDialog.prototype.buildCSSClass.call(this) + ' vjs-text-track-settings';
17941  };
17942
17943  /**
17944   * Gets an object of text track settings (or null).
17945   *
17946   * @return {Object}
17947   *         An object with config values parsed from the DOM or localStorage.
17948   */
17949
17950
17951  TextTrackSettings.prototype.getValues = function getValues() {
17952    var _this3 = this;
17953
17954    return reduce(selectConfigs, function (accum, config, key) {
17955      var value = getSelectedOptionValue(_this3.$(config.selector), config.parser);
17956
17957      if (value !== undefined) {
17958        accum[key] = value;
17959      }
17960
17961      return accum;
17962    }, {});
17963  };
17964
17965  /**
17966   * Sets text track settings from an object of values.
17967   *
17968   * @param {Object} values
17969   *        An object with config values parsed from the DOM or localStorage.
17970   */
17971
17972
17973  TextTrackSettings.prototype.setValues = function setValues(values) {
17974    var _this4 = this;
17975
17976    each(selectConfigs, function (config, key) {
17977      setSelectedOption(_this4.$(config.selector), values[key], config.parser);
17978    });
17979  };
17980
17981  /**
17982   * Sets all `<select>` elements to their default values.
17983   */
17984
17985
17986  TextTrackSettings.prototype.setDefaults = function setDefaults() {
17987    var _this5 = this;
17988
17989    each(selectConfigs, function (config) {
17990      var index = config.hasOwnProperty('default') ? config['default'] : 0;
17991
17992      _this5.$(config.selector).selectedIndex = index;
17993    });
17994  };
17995
17996  /**
17997   * Restore texttrack settings from localStorage
17998   */
17999
18000
18001  TextTrackSettings.prototype.restoreSettings = function restoreSettings() {
18002    var values = void 0;
18003
18004    try {
18005      values = JSON.parse(window_1.localStorage.getItem(LOCAL_STORAGE_KEY));
18006    } catch (err) {
18007      log$1.warn(err);
18008    }
18009
18010    if (values) {
18011      this.setValues(values);
18012    }
18013  };
18014
18015  /**
18016   * Save text track settings to localStorage
18017   */
18018
18019
18020  TextTrackSettings.prototype.saveSettings = function saveSettings() {
18021    if (!this.options_.persistTextTrackSettings) {
18022      return;
18023    }
18024
18025    var values = this.getValues();
18026
18027    try {
18028      if (Object.keys(values).length) {
18029        window_1.localStorage.setItem(LOCAL_STORAGE_KEY, JSON.stringify(values));
18030      } else {
18031        window_1.localStorage.removeItem(LOCAL_STORAGE_KEY);
18032      }
18033    } catch (err) {
18034      log$1.warn(err);
18035    }
18036  };
18037
18038  /**
18039   * Update display of text track settings
18040   */
18041
18042
18043  TextTrackSettings.prototype.updateDisplay = function updateDisplay() {
18044    var ttDisplay = this.player_.getChild('textTrackDisplay');
18045
18046    if (ttDisplay) {
18047      ttDisplay.updateDisplay();
18048    }
18049  };
18050
18051  /**
18052   * conditionally blur the element and refocus the captions button
18053   *
18054   * @private
18055   */
18056
18057
18058  TextTrackSettings.prototype.conditionalBlur_ = function conditionalBlur_() {
18059    this.previouslyActiveEl_ = null;
18060    this.off(document_1, 'keydown', this.handleKeyDown);
18061
18062    var cb = this.player_.controlBar;
18063    var subsCapsBtn = cb && cb.subsCapsButton;
18064    var ccBtn = cb && cb.captionsButton;
18065
18066    if (subsCapsBtn) {
18067      subsCapsBtn.focus();
18068    } else if (ccBtn) {
18069      ccBtn.focus();
18070    }
18071  };
18072
18073  return TextTrackSettings;
18074}(ModalDialog);
18075
18076Component.registerComponent('TextTrackSettings', TextTrackSettings);
18077
18078var _templateObject$2 = taggedTemplateLiteralLoose(['Text Tracks are being loaded from another origin but the crossorigin attribute isn\'t used.\n            This may prevent text tracks from loading.'], ['Text Tracks are being loaded from another origin but the crossorigin attribute isn\'t used.\n            This may prevent text tracks from loading.']);
18079
18080/**
18081 * @file html5.js
18082 */
18083/**
18084 * HTML5 Media Controller - Wrapper for HTML5 Media API
18085 *
18086 * @mixes Tech~SouceHandlerAdditions
18087 * @extends Tech
18088 */
18089
18090var Html5 = function (_Tech) {
18091  inherits(Html5, _Tech);
18092
18093  /**
18094   * Create an instance of this Tech.
18095   *
18096   * @param {Object} [options]
18097   *        The key/value store of player options.
18098   *
18099   * @param {Component~ReadyCallback} ready
18100   *        Callback function to call when the `HTML5` Tech is ready.
18101   */
18102  function Html5(options, ready) {
18103    classCallCheck(this, Html5);
18104
18105    var _this = possibleConstructorReturn(this, _Tech.call(this, options, ready));
18106
18107    var source = options.source;
18108    var crossoriginTracks = false;
18109
18110    // Set the source if one is provided
18111    // 1) Check if the source is new (if not, we want to keep the original so playback isn't interrupted)
18112    // 2) Check to see if the network state of the tag was failed at init, and if so, reset the source
18113    // anyway so the error gets fired.
18114    if (source && (_this.el_.currentSrc !== source.src || options.tag && options.tag.initNetworkState_ === 3)) {
18115      _this.setSource(source);
18116    } else {
18117      _this.handleLateInit_(_this.el_);
18118    }
18119
18120    if (_this.el_.hasChildNodes()) {
18121
18122      var nodes = _this.el_.childNodes;
18123      var nodesLength = nodes.length;
18124      var removeNodes = [];
18125
18126      while (nodesLength--) {
18127        var node = nodes[nodesLength];
18128        var nodeName = node.nodeName.toLowerCase();
18129
18130        if (nodeName === 'track') {
18131          if (!_this.featuresNativeTextTracks) {
18132            // Empty video tag tracks so the built-in player doesn't use them also.
18133            // This may not be fast enough to stop HTML5 browsers from reading the tags
18134            // so we'll need to turn off any default tracks if we're manually doing
18135            // captions and subtitles. videoElement.textTracks
18136            removeNodes.push(node);
18137          } else {
18138            // store HTMLTrackElement and TextTrack to remote list
18139            _this.remoteTextTrackEls().addTrackElement_(node);
18140            _this.remoteTextTracks().addTrack(node.track);
18141            _this.textTracks().addTrack(node.track);
18142            if (!crossoriginTracks && !_this.el_.hasAttribute('crossorigin') && isCrossOrigin(node.src)) {
18143              crossoriginTracks = true;
18144            }
18145          }
18146        }
18147      }
18148
18149      for (var i = 0; i < removeNodes.length; i++) {
18150        _this.el_.removeChild(removeNodes[i]);
18151      }
18152    }
18153
18154    _this.proxyNativeTracks_();
18155    if (_this.featuresNativeTextTracks && crossoriginTracks) {
18156      log$1.warn(tsml(_templateObject$2));
18157    }
18158
18159    // prevent iOS Safari from disabling metadata text tracks during native playback
18160    _this.restoreMetadataTracksInIOSNativePlayer_();
18161
18162    // Determine if native controls should be used
18163    // Our goal should be to get the custom controls on mobile solid everywhere
18164    // so we can remove this all together. Right now this will block custom
18165    // controls on touch enabled laptops like the Chrome Pixel
18166    if ((TOUCH_ENABLED || IS_IPHONE || IS_NATIVE_ANDROID) && options.nativeControlsForTouch === true) {
18167      _this.setControls(true);
18168    }
18169
18170    // on iOS, we want to proxy `webkitbeginfullscreen` and `webkitendfullscreen`
18171    // into a `fullscreenchange` event
18172    _this.proxyWebkitFullscreen_();
18173
18174    _this.triggerReady();
18175    return _this;
18176  }
18177
18178  /**
18179   * Dispose of `HTML5` media element and remove all tracks.
18180   */
18181
18182
18183  Html5.prototype.dispose = function dispose() {
18184    Html5.disposeMediaElement(this.el_);
18185    // tech will handle clearing of the emulated track list
18186    _Tech.prototype.dispose.call(this);
18187  };
18188
18189  /**
18190   * When a captions track is enabled in the iOS Safari native player, all other
18191   * tracks are disabled (including metadata tracks), which nulls all of their
18192   * associated cue points. This will restore metadata tracks to their pre-fullscreen
18193   * state in those cases so that cue points are not needlessly lost.
18194   *
18195   * @private
18196   */
18197
18198
18199  Html5.prototype.restoreMetadataTracksInIOSNativePlayer_ = function restoreMetadataTracksInIOSNativePlayer_() {
18200    var textTracks = this.textTracks();
18201    var metadataTracksPreFullscreenState = void 0;
18202
18203    // captures a snapshot of every metadata track's current state
18204    var takeMetadataTrackSnapshot = function takeMetadataTrackSnapshot() {
18205      metadataTracksPreFullscreenState = [];
18206
18207      for (var i = 0; i < textTracks.length; i++) {
18208        var track = textTracks[i];
18209
18210        if (track.kind === 'metadata') {
18211          metadataTracksPreFullscreenState.push({
18212            track: track,
18213            storedMode: track.mode
18214          });
18215        }
18216      }
18217    };
18218
18219    // snapshot each metadata track's initial state, and update the snapshot
18220    // each time there is a track 'change' event
18221    takeMetadataTrackSnapshot();
18222    textTracks.addEventListener('change', takeMetadataTrackSnapshot);
18223
18224    var restoreTrackMode = function restoreTrackMode() {
18225      for (var i = 0; i < metadataTracksPreFullscreenState.length; i++) {
18226        var storedTrack = metadataTracksPreFullscreenState[i];
18227
18228        if (storedTrack.track.mode === 'disabled' && storedTrack.track.mode !== storedTrack.storedMode) {
18229          storedTrack.track.mode = storedTrack.storedMode;
18230        }
18231      }
18232      // we only want this handler to be executed on the first 'change' event
18233      textTracks.removeEventListener('change', restoreTrackMode);
18234    };
18235
18236    // when we enter fullscreen playback, stop updating the snapshot and
18237    // restore all track modes to their pre-fullscreen state
18238    this.on('webkitbeginfullscreen', function () {
18239      textTracks.removeEventListener('change', takeMetadataTrackSnapshot);
18240
18241      // remove the listener before adding it just in case it wasn't previously removed
18242      textTracks.removeEventListener('change', restoreTrackMode);
18243      textTracks.addEventListener('change', restoreTrackMode);
18244    });
18245
18246    // start updating the snapshot again after leaving fullscreen
18247    this.on('webkitendfullscreen', function () {
18248      // remove the listener before adding it just in case it wasn't previously removed
18249      textTracks.removeEventListener('change', takeMetadataTrackSnapshot);
18250      textTracks.addEventListener('change', takeMetadataTrackSnapshot);
18251
18252      // remove the restoreTrackMode handler in case it wasn't triggered during fullscreen playback
18253      textTracks.removeEventListener('change', restoreTrackMode);
18254    });
18255  };
18256
18257  /**
18258   * Proxy all native track list events to our track lists if the browser we are playing
18259   * in supports that type of track list.
18260   *
18261   * @private
18262   */
18263
18264
18265  Html5.prototype.proxyNativeTracks_ = function proxyNativeTracks_() {
18266    var _this2 = this;
18267
18268    NORMAL.names.forEach(function (name) {
18269      var props = NORMAL[name];
18270      var elTracks = _this2.el()[props.getterName];
18271      var techTracks = _this2[props.getterName]();
18272
18273      if (!_this2['featuresNative' + props.capitalName + 'Tracks'] || !elTracks || !elTracks.addEventListener) {
18274        return;
18275      }
18276      var listeners = {
18277        change: function change(e) {
18278          techTracks.trigger({
18279            type: 'change',
18280            target: techTracks,
18281            currentTarget: techTracks,
18282            srcElement: techTracks
18283          });
18284        },
18285        addtrack: function addtrack(e) {
18286          techTracks.addTrack(e.track);
18287        },
18288        removetrack: function removetrack(e) {
18289          techTracks.removeTrack(e.track);
18290        }
18291      };
18292      var removeOldTracks = function removeOldTracks() {
18293        var removeTracks = [];
18294
18295        for (var i = 0; i < techTracks.length; i++) {
18296          var found = false;
18297
18298          for (var j = 0; j < elTracks.length; j++) {
18299            if (elTracks[j] === techTracks[i]) {
18300              found = true;
18301              break;
18302            }
18303          }
18304
18305          if (!found) {
18306            removeTracks.push(techTracks[i]);
18307          }
18308        }
18309
18310        while (removeTracks.length) {
18311          techTracks.removeTrack(removeTracks.shift());
18312        }
18313      };
18314
18315      Object.keys(listeners).forEach(function (eventName) {
18316        var listener = listeners[eventName];
18317
18318        elTracks.addEventListener(eventName, listener);
18319        _this2.on('dispose', function (e) {
18320          return elTracks.removeEventListener(eventName, listener);
18321        });
18322      });
18323
18324      // Remove (native) tracks that are not used anymore
18325      _this2.on('loadstart', removeOldTracks);
18326      _this2.on('dispose', function (e) {
18327        return _this2.off('loadstart', removeOldTracks);
18328      });
18329    });
18330  };
18331
18332  /**
18333   * Create the `Html5` Tech's DOM element.
18334   *
18335   * @return {Element}
18336   *         The element that gets created.
18337   */
18338
18339
18340  Html5.prototype.createEl = function createEl$$1() {
18341    var el = this.options_.tag;
18342
18343    // Check if this browser supports moving the element into the box.
18344    // On the iPhone video will break if you move the element,
18345    // So we have to create a brand new element.
18346    // If we ingested the player div, we do not need to move the media element.
18347    if (!el || !(this.options_.playerElIngest || this.movingMediaElementInDOM)) {
18348
18349      // If the original tag is still there, clone and remove it.
18350      if (el) {
18351        var clone = el.cloneNode(true);
18352
18353        if (el.parentNode) {
18354          el.parentNode.insertBefore(clone, el);
18355        }
18356        Html5.disposeMediaElement(el);
18357        el = clone;
18358      } else {
18359        el = document_1.createElement('video');
18360
18361        // determine if native controls should be used
18362        var tagAttributes = this.options_.tag && getAttributes(this.options_.tag);
18363        var attributes = mergeOptions({}, tagAttributes);
18364
18365        if (!TOUCH_ENABLED || this.options_.nativeControlsForTouch !== true) {
18366          delete attributes.controls;
18367        }
18368
18369        setAttributes(el, assign(attributes, {
18370          id: this.options_.techId,
18371          'class': 'vjs-tech'
18372        }));
18373      }
18374
18375      el.playerId = this.options_.playerId;
18376    }
18377
18378    if (typeof this.options_.preload !== 'undefined') {
18379      setAttribute(el, 'preload', this.options_.preload);
18380    }
18381
18382    // Update specific tag settings, in case they were overridden
18383    // `autoplay` has to be *last* so that `muted` and `playsinline` are present
18384    // when iOS/Safari or other browsers attempt to autoplay.
18385    var settingsAttrs = ['loop', 'muted', 'playsinline', 'autoplay'];
18386
18387    for (var i = settingsAttrs.length - 1; i >= 0; i--) {
18388      var attr = settingsAttrs[i];
18389      var value = this.options_[attr];
18390
18391      if (typeof value !== 'undefined') {
18392        if (value) {
18393          setAttribute(el, attr, attr);
18394        } else {
18395          removeAttribute(el, attr);
18396        }
18397        el[attr] = value;
18398      }
18399    }
18400
18401    return el;
18402  };
18403
18404  /**
18405   * This will be triggered if the loadstart event has already fired, before videojs was
18406   * ready. Two known examples of when this can happen are:
18407   * 1. If we're loading the playback object after it has started loading
18408   * 2. The media is already playing the (often with autoplay on) then
18409   *
18410   * This function will fire another loadstart so that videojs can catchup.
18411   *
18412   * @fires Tech#loadstart
18413   *
18414   * @return {undefined}
18415   *         returns nothing.
18416   */
18417
18418
18419  Html5.prototype.handleLateInit_ = function handleLateInit_(el) {
18420    if (el.networkState === 0 || el.networkState === 3) {
18421      // The video element hasn't started loading the source yet
18422      // or didn't find a source
18423      return;
18424    }
18425
18426    if (el.readyState === 0) {
18427      // NetworkState is set synchronously BUT loadstart is fired at the
18428      // end of the current stack, usually before setInterval(fn, 0).
18429      // So at this point we know loadstart may have already fired or is
18430      // about to fire, and either way the player hasn't seen it yet.
18431      // We don't want to fire loadstart prematurely here and cause a
18432      // double loadstart so we'll wait and see if it happens between now
18433      // and the next loop, and fire it if not.
18434      // HOWEVER, we also want to make sure it fires before loadedmetadata
18435      // which could also happen between now and the next loop, so we'll
18436      // watch for that also.
18437      var loadstartFired = false;
18438      var setLoadstartFired = function setLoadstartFired() {
18439        loadstartFired = true;
18440      };
18441
18442      this.on('loadstart', setLoadstartFired);
18443
18444      var triggerLoadstart = function triggerLoadstart() {
18445        // We did miss the original loadstart. Make sure the player
18446        // sees loadstart before loadedmetadata
18447        if (!loadstartFired) {
18448          this.trigger('loadstart');
18449        }
18450      };
18451
18452      this.on('loadedmetadata', triggerLoadstart);
18453
18454      this.ready(function () {
18455        this.off('loadstart', setLoadstartFired);
18456        this.off('loadedmetadata', triggerLoadstart);
18457
18458        if (!loadstartFired) {
18459          // We did miss the original native loadstart. Fire it now.
18460          this.trigger('loadstart');
18461        }
18462      });
18463
18464      return;
18465    }
18466
18467    // From here on we know that loadstart already fired and we missed it.
18468    // The other readyState events aren't as much of a problem if we double
18469    // them, so not going to go to as much trouble as loadstart to prevent
18470    // that unless we find reason to.
18471    var eventsToTrigger = ['loadstart'];
18472
18473    // loadedmetadata: newly equal to HAVE_METADATA (1) or greater
18474    eventsToTrigger.push('loadedmetadata');
18475
18476    // loadeddata: newly increased to HAVE_CURRENT_DATA (2) or greater
18477    if (el.readyState >= 2) {
18478      eventsToTrigger.push('loadeddata');
18479    }
18480
18481    // canplay: newly increased to HAVE_FUTURE_DATA (3) or greater
18482    if (el.readyState >= 3) {
18483      eventsToTrigger.push('canplay');
18484    }
18485
18486    // canplaythrough: newly equal to HAVE_ENOUGH_DATA (4)
18487    if (el.readyState >= 4) {
18488      eventsToTrigger.push('canplaythrough');
18489    }
18490
18491    // We still need to give the player time to add event listeners
18492    this.ready(function () {
18493      eventsToTrigger.forEach(function (type) {
18494        this.trigger(type);
18495      }, this);
18496    });
18497  };
18498
18499  /**
18500   * Set current time for the `HTML5` tech.
18501   *
18502   * @param {number} seconds
18503   *        Set the current time of the media to this.
18504   */
18505
18506
18507  Html5.prototype.setCurrentTime = function setCurrentTime(seconds) {
18508    try {
18509      this.el_.currentTime = seconds;
18510    } catch (e) {
18511      log$1(e, 'Video is not ready. (Video.js)');
18512      // this.warning(VideoJS.warnings.videoNotReady);
18513    }
18514  };
18515
18516  /**
18517   * Get the current duration of the HTML5 media element.
18518   *
18519   * @return {number}
18520   *         The duration of the media or 0 if there is no duration.
18521   */
18522
18523
18524  Html5.prototype.duration = function duration() {
18525    var _this3 = this;
18526
18527    // Android Chrome will report duration as Infinity for VOD HLS until after
18528    // playback has started, which triggers the live display erroneously.
18529    // Return NaN if playback has not started and trigger a durationupdate once
18530    // the duration can be reliably known.
18531    if (this.el_.duration === Infinity && IS_ANDROID && IS_CHROME && this.el_.currentTime === 0) {
18532      // Wait for the first `timeupdate` with currentTime > 0 - there may be
18533      // several with 0
18534      var checkProgress = function checkProgress() {
18535        if (_this3.el_.currentTime > 0) {
18536          // Trigger durationchange for genuinely live video
18537          if (_this3.el_.duration === Infinity) {
18538            _this3.trigger('durationchange');
18539          }
18540          _this3.off('timeupdate', checkProgress);
18541        }
18542      };
18543
18544      this.on('timeupdate', checkProgress);
18545      return NaN;
18546    }
18547    return this.el_.duration || NaN;
18548  };
18549
18550  /**
18551   * Get the current width of the HTML5 media element.
18552   *
18553   * @return {number}
18554   *         The width of the HTML5 media element.
18555   */
18556
18557
18558  Html5.prototype.width = function width() {
18559    return this.el_.offsetWidth;
18560  };
18561
18562  /**
18563   * Get the current height of the HTML5 media element.
18564   *
18565   * @return {number}
18566   *         The heigth of the HTML5 media element.
18567   */
18568
18569
18570  Html5.prototype.height = function height() {
18571    return this.el_.offsetHeight;
18572  };
18573
18574  /**
18575   * Proxy iOS `webkitbeginfullscreen` and `webkitendfullscreen` into
18576   * `fullscreenchange` event.
18577   *
18578   * @private
18579   * @fires fullscreenchange
18580   * @listens webkitendfullscreen
18581   * @listens webkitbeginfullscreen
18582   * @listens webkitbeginfullscreen
18583   */
18584
18585
18586  Html5.prototype.proxyWebkitFullscreen_ = function proxyWebkitFullscreen_() {
18587    var _this4 = this;
18588
18589    if (!('webkitDisplayingFullscreen' in this.el_)) {
18590      return;
18591    }
18592
18593    var endFn = function endFn() {
18594      this.trigger('fullscreenchange', { isFullscreen: false });
18595    };
18596
18597    var beginFn = function beginFn() {
18598      if ('webkitPresentationMode' in this.el_ && this.el_.webkitPresentationMode !== 'picture-in-picture') {
18599        this.one('webkitendfullscreen', endFn);
18600
18601        this.trigger('fullscreenchange', { isFullscreen: true });
18602      }
18603    };
18604
18605    this.on('webkitbeginfullscreen', beginFn);
18606    this.on('dispose', function () {
18607      _this4.off('webkitbeginfullscreen', beginFn);
18608      _this4.off('webkitendfullscreen', endFn);
18609    });
18610  };
18611
18612  /**
18613   * Check if fullscreen is supported on the current playback device.
18614   *
18615   * @return {boolean}
18616   *         - True if fullscreen is supported.
18617   *         - False if fullscreen is not supported.
18618   */
18619
18620
18621  Html5.prototype.supportsFullScreen = function supportsFullScreen() {
18622    if (typeof this.el_.webkitEnterFullScreen === 'function') {
18623      var userAgent = window_1.navigator && window_1.navigator.userAgent || '';
18624
18625      // Seems to be broken in Chromium/Chrome && Safari in Leopard
18626      if (/Android/.test(userAgent) || !/Chrome|Mac OS X 10.5/.test(userAgent)) {
18627        return true;
18628      }
18629    }
18630    return false;
18631  };
18632
18633  /**
18634   * Request that the `HTML5` Tech enter fullscreen.
18635   */
18636
18637
18638  Html5.prototype.enterFullScreen = function enterFullScreen() {
18639    var video = this.el_;
18640
18641    if (video.paused && video.networkState <= video.HAVE_METADATA) {
18642      // attempt to prime the video element for programmatic access
18643      // this isn't necessary on the desktop but shouldn't hurt
18644      this.el_.play();
18645
18646      // playing and pausing synchronously during the transition to fullscreen
18647      // can get iOS ~6.1 devices into a play/pause loop
18648      this.setTimeout(function () {
18649        video.pause();
18650        video.webkitEnterFullScreen();
18651      }, 0);
18652    } else {
18653      video.webkitEnterFullScreen();
18654    }
18655  };
18656
18657  /**
18658   * Request that the `HTML5` Tech exit fullscreen.
18659   */
18660
18661
18662  Html5.prototype.exitFullScreen = function exitFullScreen() {
18663    this.el_.webkitExitFullScreen();
18664  };
18665
18666  /**
18667   * A getter/setter for the `Html5` Tech's source object.
18668   * > Note: Please use {@link Html5#setSource}
18669   *
18670   * @param {Tech~SourceObject} [src]
18671   *        The source object you want to set on the `HTML5` techs element.
18672   *
18673   * @return {Tech~SourceObject|undefined}
18674   *         - The current source object when a source is not passed in.
18675   *         - undefined when setting
18676   *
18677   * @deprecated Since version 5.
18678   */
18679
18680
18681  Html5.prototype.src = function src(_src) {
18682    if (_src === undefined) {
18683      return this.el_.src;
18684    }
18685
18686    // Setting src through `src` instead of `setSrc` will be deprecated
18687    this.setSrc(_src);
18688  };
18689
18690  /**
18691   * Reset the tech by removing all sources and then calling
18692   * {@link Html5.resetMediaElement}.
18693   */
18694
18695
18696  Html5.prototype.reset = function reset() {
18697    Html5.resetMediaElement(this.el_);
18698  };
18699
18700  /**
18701   * Get the current source on the `HTML5` Tech. Falls back to returning the source from
18702   * the HTML5 media element.
18703   *
18704   * @return {Tech~SourceObject}
18705   *         The current source object from the HTML5 tech. With a fallback to the
18706   *         elements source.
18707   */
18708
18709
18710  Html5.prototype.currentSrc = function currentSrc() {
18711    if (this.currentSource_) {
18712      return this.currentSource_.src;
18713    }
18714    return this.el_.currentSrc;
18715  };
18716
18717  /**
18718   * Set controls attribute for the HTML5 media Element.
18719   *
18720   * @param {string} val
18721   *        Value to set the controls attribute to
18722   */
18723
18724
18725  Html5.prototype.setControls = function setControls(val) {
18726    this.el_.controls = !!val;
18727  };
18728
18729  /**
18730   * Create and returns a remote {@link TextTrack} object.
18731   *
18732   * @param {string} kind
18733   *        `TextTrack` kind (subtitles, captions, descriptions, chapters, or metadata)
18734   *
18735   * @param {string} [label]
18736   *        Label to identify the text track
18737   *
18738   * @param {string} [language]
18739   *        Two letter language abbreviation
18740   *
18741   * @return {TextTrack}
18742   *         The TextTrack that gets created.
18743   */
18744
18745
18746  Html5.prototype.addTextTrack = function addTextTrack(kind, label, language) {
18747    if (!this.featuresNativeTextTracks) {
18748      return _Tech.prototype.addTextTrack.call(this, kind, label, language);
18749    }
18750
18751    return this.el_.addTextTrack(kind, label, language);
18752  };
18753
18754  /**
18755   * Creates either native TextTrack or an emulated TextTrack depending
18756   * on the value of `featuresNativeTextTracks`
18757   *
18758   * @param {Object} options
18759   *        The object should contain the options to intialize the TextTrack with.
18760   *
18761   * @param {string} [options.kind]
18762   *        `TextTrack` kind (subtitles, captions, descriptions, chapters, or metadata).
18763   *
18764   * @param {string} [options.label].
18765   *        Label to identify the text track
18766   *
18767   * @param {string} [options.language]
18768   *        Two letter language abbreviation.
18769   *
18770   * @param {boolean} [options.default]
18771   *        Default this track to on.
18772   *
18773   * @param {string} [options.id]
18774   *        The internal id to assign this track.
18775   *
18776   * @param {string} [options.src]
18777   *        A source url for the track.
18778   *
18779   * @return {HTMLTrackElement}
18780   *         The track element that gets created.
18781   */
18782
18783
18784  Html5.prototype.createRemoteTextTrack = function createRemoteTextTrack(options) {
18785    if (!this.featuresNativeTextTracks) {
18786      return _Tech.prototype.createRemoteTextTrack.call(this, options);
18787    }
18788    var htmlTrackElement = document_1.createElement('track');
18789
18790    if (options.kind) {
18791      htmlTrackElement.kind = options.kind;
18792    }
18793    if (options.label) {
18794      htmlTrackElement.label = options.label;
18795    }
18796    if (options.language || options.srclang) {
18797      htmlTrackElement.srclang = options.language || options.srclang;
18798    }
18799    if (options['default']) {
18800      htmlTrackElement['default'] = options['default'];
18801    }
18802    if (options.id) {
18803      htmlTrackElement.id = options.id;
18804    }
18805    if (options.src) {
18806      htmlTrackElement.src = options.src;
18807    }
18808
18809    return htmlTrackElement;
18810  };
18811
18812  /**
18813   * Creates a remote text track object and returns an html track element.
18814   *
18815   * @param {Object} options The object should contain values for
18816   * kind, language, label, and src (location of the WebVTT file)
18817   * @param {Boolean} [manualCleanup=true] if set to false, the TextTrack will be
18818   * automatically removed from the video element whenever the source changes
18819   * @return {HTMLTrackElement} An Html Track Element.
18820   * This can be an emulated {@link HTMLTrackElement} or a native one.
18821   * @deprecated The default value of the "manualCleanup" parameter will default
18822   * to "false" in upcoming versions of Video.js
18823   */
18824
18825
18826  Html5.prototype.addRemoteTextTrack = function addRemoteTextTrack(options, manualCleanup) {
18827    var htmlTrackElement = _Tech.prototype.addRemoteTextTrack.call(this, options, manualCleanup);
18828
18829    if (this.featuresNativeTextTracks) {
18830      this.el().appendChild(htmlTrackElement);
18831    }
18832
18833    return htmlTrackElement;
18834  };
18835
18836  /**
18837   * Remove remote `TextTrack` from `TextTrackList` object
18838   *
18839   * @param {TextTrack} track
18840   *        `TextTrack` object to remove
18841   */
18842
18843
18844  Html5.prototype.removeRemoteTextTrack = function removeRemoteTextTrack(track) {
18845    _Tech.prototype.removeRemoteTextTrack.call(this, track);
18846
18847    if (this.featuresNativeTextTracks) {
18848      var tracks = this.$$('track');
18849
18850      var i = tracks.length;
18851
18852      while (i--) {
18853        if (track === tracks[i] || track === tracks[i].track) {
18854          this.el().removeChild(tracks[i]);
18855        }
18856      }
18857    }
18858  };
18859
18860  /**
18861   * Gets available media playback quality metrics as specified by the W3C's Media
18862   * Playback Quality API.
18863   *
18864   * @see [Spec]{@link https://wicg.github.io/media-playback-quality}
18865   *
18866   * @return {Object}
18867   *         An object with supported media playback quality metrics
18868   */
18869
18870
18871  Html5.prototype.getVideoPlaybackQuality = function getVideoPlaybackQuality() {
18872    if (typeof this.el().getVideoPlaybackQuality === 'function') {
18873      return this.el().getVideoPlaybackQuality();
18874    }
18875
18876    var videoPlaybackQuality = {};
18877
18878    if (typeof this.el().webkitDroppedFrameCount !== 'undefined' && typeof this.el().webkitDecodedFrameCount !== 'undefined') {
18879      videoPlaybackQuality.droppedVideoFrames = this.el().webkitDroppedFrameCount;
18880      videoPlaybackQuality.totalVideoFrames = this.el().webkitDecodedFrameCount;
18881    }
18882
18883    if (window_1.performance && typeof window_1.performance.now === 'function') {
18884      videoPlaybackQuality.creationTime = window_1.performance.now();
18885    } else if (window_1.performance && window_1.performance.timing && typeof window_1.performance.timing.navigationStart === 'number') {
18886      videoPlaybackQuality.creationTime = window_1.Date.now() - window_1.performance.timing.navigationStart;
18887    }
18888
18889    return videoPlaybackQuality;
18890  };
18891
18892  return Html5;
18893}(Tech);
18894
18895/* HTML5 Support Testing ---------------------------------------------------- */
18896
18897if (isReal()) {
18898
18899  /**
18900   * Element for testing browser HTML5 media capabilities
18901   *
18902   * @type {Element}
18903   * @constant
18904   * @private
18905   */
18906  Html5.TEST_VID = document_1.createElement('video');
18907  var track = document_1.createElement('track');
18908
18909  track.kind = 'captions';
18910  track.srclang = 'en';
18911  track.label = 'English';
18912  Html5.TEST_VID.appendChild(track);
18913}
18914
18915/**
18916 * Check if HTML5 media is supported by this browser/device.
18917 *
18918 * @return {boolean}
18919 *         - True if HTML5 media is supported.
18920 *         - False if HTML5 media is not supported.
18921 */
18922Html5.isSupported = function () {
18923  // IE9 with no Media Player is a LIAR! (#984)
18924  try {
18925    Html5.TEST_VID.volume = 0.5;
18926  } catch (e) {
18927    return false;
18928  }
18929
18930  return !!(Html5.TEST_VID && Html5.TEST_VID.canPlayType);
18931};
18932
18933/**
18934 * Check if the tech can support the given type
18935 *
18936 * @param {string} type
18937 *        The mimetype to check
18938 * @return {string} 'probably', 'maybe', or '' (empty string)
18939 */
18940Html5.canPlayType = function (type) {
18941  return Html5.TEST_VID.canPlayType(type);
18942};
18943
18944/**
18945 * Check if the tech can support the given source
18946 * @param {Object} srcObj
18947 *        The source object
18948 * @param {Object} options
18949 *        The options passed to the tech
18950 * @return {string} 'probably', 'maybe', or '' (empty string)
18951 */
18952Html5.canPlaySource = function (srcObj, options) {
18953  return Html5.canPlayType(srcObj.type);
18954};
18955
18956/**
18957 * Check if the volume can be changed in this browser/device.
18958 * Volume cannot be changed in a lot of mobile devices.
18959 * Specifically, it can't be changed from 1 on iOS.
18960 *
18961 * @return {boolean}
18962 *         - True if volume can be controlled
18963 *         - False otherwise
18964 */
18965Html5.canControlVolume = function () {
18966  // IE will error if Windows Media Player not installed #3315
18967  try {
18968    var volume = Html5.TEST_VID.volume;
18969
18970    Html5.TEST_VID.volume = volume / 2 + 0.1;
18971    return volume !== Html5.TEST_VID.volume;
18972  } catch (e) {
18973    return false;
18974  }
18975};
18976
18977/**
18978 * Check if the playback rate can be changed in this browser/device.
18979 *
18980 * @return {boolean}
18981 *         - True if playback rate can be controlled
18982 *         - False otherwise
18983 */
18984Html5.canControlPlaybackRate = function () {
18985  // Playback rate API is implemented in Android Chrome, but doesn't do anything
18986  // https://github.com/videojs/video.js/issues/3180
18987  if (IS_ANDROID && IS_CHROME && CHROME_VERSION < 58) {
18988    return false;
18989  }
18990  // IE will error if Windows Media Player not installed #3315
18991  try {
18992    var playbackRate = Html5.TEST_VID.playbackRate;
18993
18994    Html5.TEST_VID.playbackRate = playbackRate / 2 + 0.1;
18995    return playbackRate !== Html5.TEST_VID.playbackRate;
18996  } catch (e) {
18997    return false;
18998  }
18999};
19000
19001/**
19002 * Check to see if native `TextTrack`s are supported by this browser/device.
19003 *
19004 * @return {boolean}
19005 *         - True if native `TextTrack`s are supported.
19006 *         - False otherwise
19007 */
19008Html5.supportsNativeTextTracks = function () {
19009  return IS_ANY_SAFARI;
19010};
19011
19012/**
19013 * Check to see if native `VideoTrack`s are supported by this browser/device
19014 *
19015 * @return {boolean}
19016 *        - True if native `VideoTrack`s are supported.
19017 *        - False otherwise
19018 */
19019Html5.supportsNativeVideoTracks = function () {
19020  return !!(Html5.TEST_VID && Html5.TEST_VID.videoTracks);
19021};
19022
19023/**
19024 * Check to see if native `AudioTrack`s are supported by this browser/device
19025 *
19026 * @return {boolean}
19027 *        - True if native `AudioTrack`s are supported.
19028 *        - False otherwise
19029 */
19030Html5.supportsNativeAudioTracks = function () {
19031  return !!(Html5.TEST_VID && Html5.TEST_VID.audioTracks);
19032};
19033
19034/**
19035 * An array of events available on the Html5 tech.
19036 *
19037 * @private
19038 * @type {Array}
19039 */
19040Html5.Events = ['loadstart', 'suspend', 'abort', 'error', 'emptied', 'stalled', 'loadedmetadata', 'loadeddata', 'canplay', 'canplaythrough', 'playing', 'waiting', 'seeking', 'seeked', 'ended', 'durationchange', 'timeupdate', 'progress', 'play', 'pause', 'ratechange', 'resize', 'volumechange'];
19041
19042/**
19043 * Boolean indicating whether the `Tech` supports volume control.
19044 *
19045 * @type {boolean}
19046 * @default {@link Html5.canControlVolume}
19047 */
19048Html5.prototype.featuresVolumeControl = Html5.canControlVolume();
19049
19050/**
19051 * Boolean indicating whether the `Tech` supports changing the speed at which the media
19052 * plays. Examples:
19053 *   - Set player to play 2x (twice) as fast
19054 *   - Set player to play 0.5x (half) as fast
19055 *
19056 * @type {boolean}
19057 * @default {@link Html5.canControlPlaybackRate}
19058 */
19059Html5.prototype.featuresPlaybackRate = Html5.canControlPlaybackRate();
19060
19061/**
19062 * Boolean indicating whether the `HTML5` tech currently supports the media element
19063 * moving in the DOM. iOS breaks if you move the media element, so this is set this to
19064 * false there. Everywhere else this should be true.
19065 *
19066 * @type {boolean}
19067 * @default
19068 */
19069Html5.prototype.movingMediaElementInDOM = !IS_IOS;
19070
19071// TODO: Previous comment: No longer appears to be used. Can probably be removed.
19072//       Is this true?
19073/**
19074 * Boolean indicating whether the `HTML5` tech currently supports automatic media resize
19075 * when going into fullscreen.
19076 *
19077 * @type {boolean}
19078 * @default
19079 */
19080Html5.prototype.featuresFullscreenResize = true;
19081
19082/**
19083 * Boolean indicating whether the `HTML5` tech currently supports the progress event.
19084 * If this is false, manual `progress` events will be triggred instead.
19085 *
19086 * @type {boolean}
19087 * @default
19088 */
19089Html5.prototype.featuresProgressEvents = true;
19090
19091/**
19092 * Boolean indicating whether the `HTML5` tech currently supports the timeupdate event.
19093 * If this is false, manual `timeupdate` events will be triggred instead.
19094 *
19095 * @default
19096 */
19097Html5.prototype.featuresTimeupdateEvents = true;
19098
19099/**
19100 * Boolean indicating whether the `HTML5` tech currently supports native `TextTrack`s.
19101 *
19102 * @type {boolean}
19103 * @default {@link Html5.supportsNativeTextTracks}
19104 */
19105Html5.prototype.featuresNativeTextTracks = Html5.supportsNativeTextTracks();
19106
19107/**
19108 * Boolean indicating whether the `HTML5` tech currently supports native `VideoTrack`s.
19109 *
19110 * @type {boolean}
19111 * @default {@link Html5.supportsNativeVideoTracks}
19112 */
19113Html5.prototype.featuresNativeVideoTracks = Html5.supportsNativeVideoTracks();
19114
19115/**
19116 * Boolean indicating whether the `HTML5` tech currently supports native `AudioTrack`s.
19117 *
19118 * @type {boolean}
19119 * @default {@link Html5.supportsNativeAudioTracks}
19120 */
19121Html5.prototype.featuresNativeAudioTracks = Html5.supportsNativeAudioTracks();
19122
19123// HTML5 Feature detection and Device Fixes --------------------------------- //
19124var canPlayType = Html5.TEST_VID && Html5.TEST_VID.constructor.prototype.canPlayType;
19125var mpegurlRE = /^application\/(?:x-|vnd\.apple\.)mpegurl/i;
19126var mp4RE = /^video\/mp4/i;
19127
19128Html5.patchCanPlayType = function () {
19129
19130  // Android 4.0 and above can play HLS to some extent but it reports being unable to do so
19131  if (ANDROID_VERSION >= 4.0 && !IS_FIREFOX) {
19132    Html5.TEST_VID.constructor.prototype.canPlayType = function (type) {
19133      if (type && mpegurlRE.test(type)) {
19134        return 'maybe';
19135      }
19136      return canPlayType.call(this, type);
19137    };
19138
19139    // Override Android 2.2 and less canPlayType method which is broken
19140  } else if (IS_OLD_ANDROID) {
19141    Html5.TEST_VID.constructor.prototype.canPlayType = function (type) {
19142      if (type && mp4RE.test(type)) {
19143        return 'maybe';
19144      }
19145      return canPlayType.call(this, type);
19146    };
19147  }
19148};
19149
19150Html5.unpatchCanPlayType = function () {
19151  var r = Html5.TEST_VID.constructor.prototype.canPlayType;
19152
19153  Html5.TEST_VID.constructor.prototype.canPlayType = canPlayType;
19154  return r;
19155};
19156
19157// by default, patch the media element
19158Html5.patchCanPlayType();
19159
19160Html5.disposeMediaElement = function (el) {
19161  if (!el) {
19162    return;
19163  }
19164
19165  if (el.parentNode) {
19166    el.parentNode.removeChild(el);
19167  }
19168
19169  // remove any child track or source nodes to prevent their loading
19170  while (el.hasChildNodes()) {
19171    el.removeChild(el.firstChild);
19172  }
19173
19174  // remove any src reference. not setting `src=''` because that causes a warning
19175  // in firefox
19176  el.removeAttribute('src');
19177
19178  // force the media element to update its loading state by calling load()
19179  // however IE on Windows 7N has a bug that throws an error so need a try/catch (#793)
19180  if (typeof el.load === 'function') {
19181    // wrapping in an iife so it's not deoptimized (#1060#discussion_r10324473)
19182    (function () {
19183      try {
19184        el.load();
19185      } catch (e) {
19186        // not supported
19187      }
19188    })();
19189  }
19190};
19191
19192Html5.resetMediaElement = function (el) {
19193  if (!el) {
19194    return;
19195  }
19196
19197  var sources = el.querySelectorAll('source');
19198  var i = sources.length;
19199
19200  while (i--) {
19201    el.removeChild(sources[i]);
19202  }
19203
19204  // remove any src reference.
19205  // not setting `src=''` because that throws an error
19206  el.removeAttribute('src');
19207
19208  if (typeof el.load === 'function') {
19209    // wrapping in an iife so it's not deoptimized (#1060#discussion_r10324473)
19210    (function () {
19211      try {
19212        el.load();
19213      } catch (e) {
19214        // satisfy linter
19215      }
19216    })();
19217  }
19218};
19219
19220/* Native HTML5 element property wrapping ----------------------------------- */
19221// Wrap native boolean attributes with getters that check both property and attribute
19222// The list is as followed:
19223// muted, defaultMuted, autoplay, controls, loop, playsinline
19224[
19225/**
19226 * Get the value of `muted` from the media element. `muted` indicates
19227 * that the volume for the media should be set to silent. This does not actually change
19228 * the `volume` attribute.
19229 *
19230 * @method Html5#muted
19231 * @return {boolean}
19232 *         - True if the value of `volume` should be ignored and the audio set to silent.
19233 *         - False if the value of `volume` should be used.
19234 *
19235 * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-media-muted}
19236 */
19237'muted',
19238
19239/**
19240 * Get the value of `defaultMuted` from the media element. `defaultMuted` indicates
19241 * whether the media should start muted or not. Only changes the default state of the
19242 * media. `muted` and `defaultMuted` can have different values. {@link Html5#muted} indicates the
19243 * current state.
19244 *
19245 * @method Html5#defaultMuted
19246 * @return {boolean}
19247 *         - The value of `defaultMuted` from the media element.
19248 *         - True indicates that the media should start muted.
19249 *         - False indicates that the media should not start muted
19250 *
19251 * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-media-defaultmuted}
19252 */
19253'defaultMuted',
19254
19255/**
19256 * Get the value of `autoplay` from the media element. `autoplay` indicates
19257 * that the media should start to play as soon as the page is ready.
19258 *
19259 * @method Html5#autoplay
19260 * @return {boolean}
19261 *         - The value of `autoplay` from the media element.
19262 *         - True indicates that the media should start as soon as the page loads.
19263 *         - False indicates that the media should not start as soon as the page loads.
19264 *
19265 * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#attr-media-autoplay}
19266 */
19267'autoplay',
19268
19269/**
19270 * Get the value of `controls` from the media element. `controls` indicates
19271 * whether the native media controls should be shown or hidden.
19272 *
19273 * @method Html5#controls
19274 * @return {boolean}
19275 *         - The value of `controls` from the media element.
19276 *         - True indicates that native controls should be showing.
19277 *         - False indicates that native controls should be hidden.
19278 *
19279 * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#attr-media-controls}
19280 */
19281'controls',
19282
19283/**
19284 * Get the value of `loop` from the media element. `loop` indicates
19285 * that the media should return to the start of the media and continue playing once
19286 * it reaches the end.
19287 *
19288 * @method Html5#loop
19289 * @return {boolean}
19290 *         - The value of `loop` from the media element.
19291 *         - True indicates that playback should seek back to start once
19292 *           the end of a media is reached.
19293 *         - False indicates that playback should not loop back to the start when the
19294 *           end of the media is reached.
19295 *
19296 * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#attr-media-loop}
19297 */
19298'loop',
19299
19300/**
19301 * Get the value of `playsinline` from the media element. `playsinline` indicates
19302 * to the browser that non-fullscreen playback is preferred when fullscreen
19303 * playback is the native default, such as in iOS Safari.
19304 *
19305 * @method Html5#playsinline
19306 * @return {boolean}
19307 *         - The value of `playsinline` from the media element.
19308 *         - True indicates that the media should play inline.
19309 *         - False indicates that the media should not play inline.
19310 *
19311 * @see [Spec]{@link https://html.spec.whatwg.org/#attr-video-playsinline}
19312 */
19313'playsinline'].forEach(function (prop) {
19314  Html5.prototype[prop] = function () {
19315    return this.el_[prop] || this.el_.hasAttribute(prop);
19316  };
19317});
19318
19319// Wrap native boolean attributes with setters that set both property and attribute
19320// The list is as followed:
19321// setMuted, setDefaultMuted, setAutoplay, setLoop, setPlaysinline
19322// setControls is special-cased above
19323[
19324/**
19325 * Set the value of `muted` on the media element. `muted` indicates that the current
19326 * audio level should be silent.
19327 *
19328 * @method Html5#setMuted
19329 * @param {boolean} muted
19330 *        - True if the audio should be set to silent
19331 *        - False otherwise
19332 *
19333 * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-media-muted}
19334 */
19335'muted',
19336
19337/**
19338 * Set the value of `defaultMuted` on the media element. `defaultMuted` indicates that the current
19339 * audio level should be silent, but will only effect the muted level on intial playback..
19340 *
19341 * @method Html5.prototype.setDefaultMuted
19342 * @param {boolean} defaultMuted
19343 *        - True if the audio should be set to silent
19344 *        - False otherwise
19345 *
19346 * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-media-defaultmuted}
19347 */
19348'defaultMuted',
19349
19350/**
19351 * Set the value of `autoplay` on the media element. `autoplay` indicates
19352 * that the media should start to play as soon as the page is ready.
19353 *
19354 * @method Html5#setAutoplay
19355 * @param {boolean} autoplay
19356 *         - True indicates that the media should start as soon as the page loads.
19357 *         - False indicates that the media should not start as soon as the page loads.
19358 *
19359 * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#attr-media-autoplay}
19360 */
19361'autoplay',
19362
19363/**
19364 * Set the value of `loop` on the media element. `loop` indicates
19365 * that the media should return to the start of the media and continue playing once
19366 * it reaches the end.
19367 *
19368 * @method Html5#setLoop
19369 * @param {boolean} loop
19370 *         - True indicates that playback should seek back to start once
19371 *           the end of a media is reached.
19372 *         - False indicates that playback should not loop back to the start when the
19373 *           end of the media is reached.
19374 *
19375 * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#attr-media-loop}
19376 */
19377'loop',
19378
19379/**
19380 * Set the value of `playsinline` from the media element. `playsinline` indicates
19381 * to the browser that non-fullscreen playback is preferred when fullscreen
19382 * playback is the native default, such as in iOS Safari.
19383 *
19384 * @method Html5#setPlaysinline
19385 * @param {boolean} playsinline
19386 *         - True indicates that the media should play inline.
19387 *         - False indicates that the media should not play inline.
19388 *
19389 * @see [Spec]{@link https://html.spec.whatwg.org/#attr-video-playsinline}
19390 */
19391'playsinline'].forEach(function (prop) {
19392  Html5.prototype['set' + toTitleCase(prop)] = function (v) {
19393    this.el_[prop] = v;
19394
19395    if (v) {
19396      this.el_.setAttribute(prop, prop);
19397    } else {
19398      this.el_.removeAttribute(prop);
19399    }
19400  };
19401});
19402
19403// Wrap native properties with a getter
19404// The list is as followed
19405// paused, currentTime, buffered, volume, poster, preload, error, seeking
19406// seekable, ended, playbackRate, defaultPlaybackRate, played, networkState
19407// readyState, videoWidth, videoHeight
19408[
19409/**
19410 * Get the value of `paused` from the media element. `paused` indicates whether the media element
19411 * is currently paused or not.
19412 *
19413 * @method Html5#paused
19414 * @return {boolean}
19415 *         The value of `paused` from the media element.
19416 *
19417 * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-media-paused}
19418 */
19419'paused',
19420
19421/**
19422 * Get the value of `currentTime` from the media element. `currentTime` indicates
19423 * the current second that the media is at in playback.
19424 *
19425 * @method Html5#currentTime
19426 * @return {number}
19427 *         The value of `currentTime` from the media element.
19428 *
19429 * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-media-currenttime}
19430 */
19431'currentTime',
19432
19433/**
19434 * Get the value of `buffered` from the media element. `buffered` is a `TimeRange`
19435 * object that represents the parts of the media that are already downloaded and
19436 * available for playback.
19437 *
19438 * @method Html5#buffered
19439 * @return {TimeRange}
19440 *         The value of `buffered` from the media element.
19441 *
19442 * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-media-buffered}
19443 */
19444'buffered',
19445
19446/**
19447 * Get the value of `volume` from the media element. `volume` indicates
19448 * the current playback volume of audio for a media. `volume` will be a value from 0
19449 * (silent) to 1 (loudest and default).
19450 *
19451 * @method Html5#volume
19452 * @return {number}
19453 *         The value of `volume` from the media element. Value will be between 0-1.
19454 *
19455 * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-a-volume}
19456 */
19457'volume',
19458
19459/**
19460 * Get the value of `poster` from the media element. `poster` indicates
19461 * that the url of an image file that can/will be shown when no media data is available.
19462 *
19463 * @method Html5#poster
19464 * @return {string}
19465 *         The value of `poster` from the media element. Value will be a url to an
19466 *         image.
19467 *
19468 * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#attr-video-poster}
19469 */
19470'poster',
19471
19472/**
19473 * Get the value of `preload` from the media element. `preload` indicates
19474 * what should download before the media is interacted with. It can have the following
19475 * values:
19476 * - none: nothing should be downloaded
19477 * - metadata: poster and the first few frames of the media may be downloaded to get
19478 *   media dimensions and other metadata
19479 * - auto: allow the media and metadata for the media to be downloaded before
19480 *    interaction
19481 *
19482 * @method Html5#preload
19483 * @return {string}
19484 *         The value of `preload` from the media element. Will be 'none', 'metadata',
19485 *         or 'auto'.
19486 *
19487 * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#attr-media-preload}
19488 */
19489'preload',
19490
19491/**
19492 * Get the value of the `error` from the media element. `error` indicates any
19493 * MediaError that may have occured during playback. If error returns null there is no
19494 * current error.
19495 *
19496 * @method Html5#error
19497 * @return {MediaError|null}
19498 *         The value of `error` from the media element. Will be `MediaError` if there
19499 *         is a current error and null otherwise.
19500 *
19501 * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-media-error}
19502 */
19503'error',
19504
19505/**
19506 * Get the value of `seeking` from the media element. `seeking` indicates whether the
19507 * media is currently seeking to a new position or not.
19508 *
19509 * @method Html5#seeking
19510 * @return {boolean}
19511 *         - The value of `seeking` from the media element.
19512 *         - True indicates that the media is currently seeking to a new position.
19513 *         - Flase indicates that the media is not seeking to a new position at this time.
19514 *
19515 * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-media-seeking}
19516 */
19517'seeking',
19518
19519/**
19520 * Get the value of `seekable` from the media element. `seekable` returns a
19521 * `TimeRange` object indicating ranges of time that can currently be `seeked` to.
19522 *
19523 * @method Html5#seekable
19524 * @return {TimeRange}
19525 *         The value of `seekable` from the media element. A `TimeRange` object
19526 *         indicating the current ranges of time that can be seeked to.
19527 *
19528 * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-media-seekable}
19529 */
19530'seekable',
19531
19532/**
19533 * Get the value of `ended` from the media element. `ended` indicates whether
19534 * the media has reached the end or not.
19535 *
19536 * @method Html5#ended
19537 * @return {boolean}
19538 *         - The value of `ended` from the media element.
19539 *         - True indicates that the media has ended.
19540 *         - False indicates that the media has not ended.
19541 *
19542 * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-media-ended}
19543 */
19544'ended',
19545
19546/**
19547 * Get the value of `playbackRate` from the media element. `playbackRate` indicates
19548 * the rate at which the media is currently playing back. Examples:
19549 *   - if playbackRate is set to 2, media will play twice as fast.
19550 *   - if playbackRate is set to 0.5, media will play half as fast.
19551 *
19552 * @method Html5#playbackRate
19553 * @return {number}
19554 *         The value of `playbackRate` from the media element. A number indicating
19555 *         the current playback speed of the media, where 1 is normal speed.
19556 *
19557 * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-media-playbackrate}
19558 */
19559'playbackRate',
19560
19561/**
19562 * Get the value of `defaultPlaybackRate` from the media element. `defaultPlaybackRate` indicates
19563 * the rate at which the media is currently playing back. This value will not indicate the current
19564 * `playbackRate` after playback has started, use {@link Html5#playbackRate} for that.
19565 *
19566 * Examples:
19567 *   - if defaultPlaybackRate is set to 2, media will play twice as fast.
19568 *   - if defaultPlaybackRate is set to 0.5, media will play half as fast.
19569 *
19570 * @method Html5.prototype.defaultPlaybackRate
19571 * @return {number}
19572 *         The value of `defaultPlaybackRate` from the media element. A number indicating
19573 *         the current playback speed of the media, where 1 is normal speed.
19574 *
19575 * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-media-playbackrate}
19576 */
19577'defaultPlaybackRate',
19578
19579/**
19580 * Get the value of `played` from the media element. `played` returns a `TimeRange`
19581 * object representing points in the media timeline that have been played.
19582 *
19583 * @method Html5#played
19584 * @return {TimeRange}
19585 *         The value of `played` from the media element. A `TimeRange` object indicating
19586 *         the ranges of time that have been played.
19587 *
19588 * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-media-played}
19589 */
19590'played',
19591
19592/**
19593 * Get the value of `networkState` from the media element. `networkState` indicates
19594 * the current network state. It returns an enumeration from the following list:
19595 * - 0: NETWORK_EMPTY
19596 * - 1: NEWORK_IDLE
19597 * - 2: NETWORK_LOADING
19598 * - 3: NETWORK_NO_SOURCE
19599 *
19600 * @method Html5#networkState
19601 * @return {number}
19602 *         The value of `networkState` from the media element. This will be a number
19603 *         from the list in the description.
19604 *
19605 * @see [Spec] {@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-media-networkstate}
19606 */
19607'networkState',
19608
19609/**
19610 * Get the value of `readyState` from the media element. `readyState` indicates
19611 * the current state of the media element. It returns an enumeration from the
19612 * following list:
19613 * - 0: HAVE_NOTHING
19614 * - 1: HAVE_METADATA
19615 * - 2: HAVE_CURRENT_DATA
19616 * - 3: HAVE_FUTURE_DATA
19617 * - 4: HAVE_ENOUGH_DATA
19618 *
19619 * @method Html5#readyState
19620 * @return {number}
19621 *         The value of `readyState` from the media element. This will be a number
19622 *         from the list in the description.
19623 *
19624 * @see [Spec] {@link https://www.w3.org/TR/html5/embedded-content-0.html#ready-states}
19625 */
19626'readyState',
19627
19628/**
19629 * Get the value of `videoWidth` from the video element. `videoWidth` indicates
19630 * the current width of the video in css pixels.
19631 *
19632 * @method Html5#videoWidth
19633 * @return {number}
19634 *         The value of `videoWidth` from the video element. This will be a number
19635 *         in css pixels.
19636 *
19637 * @see [Spec] {@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-video-videowidth}
19638 */
19639'videoWidth',
19640
19641/**
19642 * Get the value of `videoHeight` from the video element. `videoHeigth` indicates
19643 * the current height of the video in css pixels.
19644 *
19645 * @method Html5#videoHeight
19646 * @return {number}
19647 *         The value of `videoHeight` from the video element. This will be a number
19648 *         in css pixels.
19649 *
19650 * @see [Spec] {@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-video-videowidth}
19651 */
19652'videoHeight'].forEach(function (prop) {
19653  Html5.prototype[prop] = function () {
19654    return this.el_[prop];
19655  };
19656});
19657
19658// Wrap native properties with a setter in this format:
19659// set + toTitleCase(name)
19660// The list is as follows:
19661// setVolume, setSrc, setPoster, setPreload, setPlaybackRate, setDefaultPlaybackRate
19662[
19663/**
19664 * Set the value of `volume` on the media element. `volume` indicates the current
19665 * audio level as a percentage in decimal form. This means that 1 is 100%, 0.5 is 50%, and
19666 * so on.
19667 *
19668 * @method Html5#setVolume
19669 * @param {number} percentAsDecimal
19670 *        The volume percent as a decimal. Valid range is from 0-1.
19671 *
19672 * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-a-volume}
19673 */
19674'volume',
19675
19676/**
19677 * Set the value of `src` on the media element. `src` indicates the current
19678 * {@link Tech~SourceObject} for the media.
19679 *
19680 * @method Html5#setSrc
19681 * @param {Tech~SourceObject} src
19682 *        The source object to set as the current source.
19683 *
19684 * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-media-src}
19685 */
19686'src',
19687
19688/**
19689 * Set the value of `poster` on the media element. `poster` is the url to
19690 * an image file that can/will be shown when no media data is available.
19691 *
19692 * @method Html5#setPoster
19693 * @param {string} poster
19694 *        The url to an image that should be used as the `poster` for the media
19695 *        element.
19696 *
19697 * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#attr-media-poster}
19698 */
19699'poster',
19700
19701/**
19702 * Set the value of `preload` on the media element. `preload` indicates
19703 * what should download before the media is interacted with. It can have the following
19704 * values:
19705 * - none: nothing should be downloaded
19706 * - metadata: poster and the first few frames of the media may be downloaded to get
19707 *   media dimensions and other metadata
19708 * - auto: allow the media and metadata for the media to be downloaded before
19709 *    interaction
19710 *
19711 * @method Html5#setPreload
19712 * @param {string} preload
19713 *         The value of `preload` to set on the media element. Must be 'none', 'metadata',
19714 *         or 'auto'.
19715 *
19716 * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#attr-media-preload}
19717 */
19718'preload',
19719
19720/**
19721 * Set the value of `playbackRate` on the media element. `playbackRate` indicates
19722 * the rate at which the media should play back. Examples:
19723 *   - if playbackRate is set to 2, media will play twice as fast.
19724 *   - if playbackRate is set to 0.5, media will play half as fast.
19725 *
19726 * @method Html5#setPlaybackRate
19727 * @return {number}
19728 *         The value of `playbackRate` from the media element. A number indicating
19729 *         the current playback speed of the media, where 1 is normal speed.
19730 *
19731 * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-media-playbackrate}
19732 */
19733'playbackRate',
19734
19735/**
19736 * Set the value of `defaultPlaybackRate` on the media element. `defaultPlaybackRate` indicates
19737 * the rate at which the media should play back upon initial startup. Changing this value
19738 * after a video has started will do nothing. Instead you should used {@link Html5#setPlaybackRate}.
19739 *
19740 * Example Values:
19741 *   - if playbackRate is set to 2, media will play twice as fast.
19742 *   - if playbackRate is set to 0.5, media will play half as fast.
19743 *
19744 * @method Html5.prototype.setDefaultPlaybackRate
19745 * @return {number}
19746 *         The value of `defaultPlaybackRate` from the media element. A number indicating
19747 *         the current playback speed of the media, where 1 is normal speed.
19748 *
19749 * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-media-defaultplaybackrate}
19750 */
19751'defaultPlaybackRate'].forEach(function (prop) {
19752  Html5.prototype['set' + toTitleCase(prop)] = function (v) {
19753    this.el_[prop] = v;
19754  };
19755});
19756
19757// wrap native functions with a function
19758// The list is as follows:
19759// pause, load play
19760[
19761/**
19762 * A wrapper around the media elements `pause` function. This will call the `HTML5`
19763 * media elements `pause` function.
19764 *
19765 * @method Html5#pause
19766 * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-media-pause}
19767 */
19768'pause',
19769
19770/**
19771 * A wrapper around the media elements `load` function. This will call the `HTML5`s
19772 * media element `load` function.
19773 *
19774 * @method Html5#load
19775 * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-media-load}
19776 */
19777'load',
19778
19779/**
19780 * A wrapper around the media elements `play` function. This will call the `HTML5`s
19781 * media element `play` function.
19782 *
19783 * @method Html5#play
19784 * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-media-play}
19785 */
19786'play'].forEach(function (prop) {
19787  Html5.prototype[prop] = function () {
19788    return this.el_[prop]();
19789  };
19790});
19791
19792Tech.withSourceHandlers(Html5);
19793
19794/**
19795 * Native source handler for Html5, simply passes the source to the media element.
19796 *
19797 * @proprety {Tech~SourceObject} source
19798 *        The source object
19799 *
19800 * @proprety {Html5} tech
19801 *        The instance of the HTML5 tech.
19802 */
19803Html5.nativeSourceHandler = {};
19804
19805/**
19806 * Check if the media element can play the given mime type.
19807 *
19808 * @param {string} type
19809 *        The mimetype to check
19810 *
19811 * @return {string}
19812 *         'probably', 'maybe', or '' (empty string)
19813 */
19814Html5.nativeSourceHandler.canPlayType = function (type) {
19815  // IE9 on Windows 7 without MediaPlayer throws an error here
19816  // https://github.com/videojs/video.js/issues/519
19817  try {
19818    return Html5.TEST_VID.canPlayType(type);
19819  } catch (e) {
19820    return '';
19821  }
19822};
19823
19824/**
19825 * Check if the media element can handle a source natively.
19826 *
19827 * @param {Tech~SourceObject} source
19828 *         The source object
19829 *
19830 * @param {Object} [options]
19831 *         Options to be passed to the tech.
19832 *
19833 * @return {string}
19834 *         'probably', 'maybe', or '' (empty string).
19835 */
19836Html5.nativeSourceHandler.canHandleSource = function (source, options) {
19837
19838  // If a type was provided we should rely on that
19839  if (source.type) {
19840    return Html5.nativeSourceHandler.canPlayType(source.type);
19841
19842    // If no type, fall back to checking 'video/[EXTENSION]'
19843  } else if (source.src) {
19844    var ext = getFileExtension(source.src);
19845
19846    return Html5.nativeSourceHandler.canPlayType('video/' + ext);
19847  }
19848
19849  return '';
19850};
19851
19852/**
19853 * Pass the source to the native media element.
19854 *
19855 * @param {Tech~SourceObject} source
19856 *        The source object
19857 *
19858 * @param {Html5} tech
19859 *        The instance of the Html5 tech
19860 *
19861 * @param {Object} [options]
19862 *        The options to pass to the source
19863 */
19864Html5.nativeSourceHandler.handleSource = function (source, tech, options) {
19865  tech.setSrc(source.src);
19866};
19867
19868/**
19869 * A noop for the native dispose function, as cleanup is not needed.
19870 */
19871Html5.nativeSourceHandler.dispose = function () {};
19872
19873// Register the native source handler
19874Html5.registerSourceHandler(Html5.nativeSourceHandler);
19875
19876Tech.registerTech('Html5', Html5);
19877
19878var _templateObject$1 = taggedTemplateLiteralLoose(['\n        Using the tech directly can be dangerous. I hope you know 
19878what you\'re doing.\n        See https://github.com/videojs/video.js/issues/2617 for more info.\n      '], ['\n        Using the tech directly can be dangerous. I hope you know what you\'re doing.\n        See https://github.com/videojs/video.js/issues/2617 for more info.\n      ']);
19879
19880/**
19881 * @file player.js
19882 */
19883// Subclasses Component
19884// The following imports are used only to ensure that the corresponding modules
19885// are always included in the video.js package. Importing the modules will
19886// execute them and they will register themselves with video.js.
19887// Import Html5 tech, at least for disposing the original video tag.
19888// The following tech events are simply re-triggered
19889// on the player when they happen
19890var TECH_EVENTS_RETRIGGER = [
19891/**
19892 * Fired while the user agent is downloading media data.
19893 *
19894 * @event Player#progress
19895 * @type {EventTarget~Event}
19896 */
19897/**
19898 * Retrigger the `progress` event that was triggered by the {@link Tech}.
19899 *
19900 * @private
19901 * @method Player#handleTechProgress_
19902 * @fires Player#progress
19903 * @listens Tech#progress
19904 */
19905'progress',
19906
19907/**
19908 * Fires when the loading of an audio/video is aborted.
19909 *
19910 * @event Player#abort
19911 * @type {EventTarget~Event}
19912 */
19913/**
19914 * Retrigger the `abort` event that was triggered by the {@link Tech}.
19915 *
19916 * @private
19917 * @method Player#handleTechAbort_
19918 * @fires Player#abort
19919 * @listens Tech#abort
19920 */
19921'abort',
19922
19923/**
19924 * Fires when the browser is intentionally not getting media data.
19925 *
19926 * @event Player#suspend
19927 * @type {EventTarget~Event}
19928 */
19929/**
19930 * Retrigger the `suspend` event that was triggered by the {@link Tech}.
19931 *
19932 * @private
19933 * @method Player#handleTechSuspend_
19934 * @fires Player#suspend
19935 * @listens Tech#suspend
19936 */
19937'suspend',
19938
19939/**
19940 * Fires when the current playlist is empty.
19941 *
19942 * @event Player#emptied
19943 * @type {EventTarget~Event}
19944 */
19945/**
19946 * Retrigger the `emptied` event that was triggered by the {@link Tech}.
19947 *
19948 * @private
19949 * @method Player#handleTechEmptied_
19950 * @fires Player#emptied
19951 * @listens Tech#emptied
19952 */
19953'emptied',
19954/**
19955 * Fires when the browser is trying to get media data, but data is not available.
19956 *
19957 * @event Player#stalled
19958 * @type {EventTarget~Event}
19959 */
19960/**
19961 * Retrigger the `stalled` event that was triggered by the {@link Tech}.
19962 *
19963 * @private
19964 * @method Player#handleTechStalled_
19965 * @fires Player#stalled
19966 * @listens Tech#stalled
19967 */
19968'stalled',
19969
19970/**
19971 * Fires when the browser has loaded meta data for the audio/video.
19972 *
19973 * @event Player#loadedmetadata
19974 * @type {EventTarget~Event}
19975 */
19976/**
19977 * Retrigger the `stalled` event that was triggered by the {@link Tech}.
19978 *
19979 * @private
19980 * @method Player#handleTechLoadedmetadata_
19981 * @fires Player#loadedmetadata
19982 * @listens Tech#loadedmetadata
19983 */
19984'loadedmetadata',
19985
19986/**
19987 * Fires when the browser has loaded the current frame of the audio/video.
19988 *
19989 * @event Player#loadeddata
19990 * @type {event}
19991 */
19992/**
19993 * Retrigger the `loadeddata` event that was triggered by the {@link Tech}.
19994 *
19995 * @private
19996 * @method Player#handleTechLoaddeddata_
19997 * @fires Player#loadeddata
19998 * @listens Tech#loadeddata
19999 */
20000'loadeddata',
20001
20002/**
20003 * Fires when the current playback position has changed.
20004 *
20005 * @event Player#timeupdate
20006 * @type {event}
20007 */
20008/**
20009 * Retrigger the `timeupdate` event that was triggered by the {@link Tech}.
20010 *
20011 * @private
20012 * @method Player#handleTechTimeUpdate_
20013 * @fires Player#timeupdate
20014 * @listens Tech#timeupdate
20015 */
20016'timeupdate',
20017
20018/**
20019 * Fires when the playing speed of the audio/video is changed
20020 *
20021 * @event Player#ratechange
20022 * @type {event}
20023 */
20024/**
20025 * Retrigger the `ratechange` event that was triggered by the {@link Tech}.
20026 *
20027 * @private
20028 * @method Player#handleTechRatechange_
20029 * @fires Player#ratechange
20030 * @listens Tech#ratechange
20031 */
20032'ratechange',
20033
20034/**
20035 * Fires when the video's intrinsic dimensions change
20036 *
20037 * @event Player#resize
20038 * @type {event}
20039 */
20040/**
20041 * Retrigger the `resize` event that was triggered by the {@link Tech}.
20042 *
20043 * @private
20044 * @method Player#handleTechResize_
20045 * @fires Player#resize
20046 * @listens Tech#resize
20047 */
20048'resize',
20049
20050/**
20051 * Fires when the volume has been changed
20052 *
20053 * @event Player#volumechange
20054 * @type {event}
20055 */
20056/**
20057 * Retrigger the `volumechange` event that was triggered by the {@link Tech}.
20058 *
20059 * @private
20060 * @method Player#handleTechVolumechange_
20061 * @fires Player#volumechange
20062 * @listens Tech#volumechange
20063 */
20064'volumechange',
20065
20066/**
20067 * Fires when the text track has been changed
20068 *
20069 * @event Player#texttrackchange
20070 * @type {event}
20071 */
20072/**
20073 * Retrigger the `texttrackchange` event that was triggered by the {@link Tech}.
20074 *
20075 * @private
20076 * @method Player#handleTechTexttrackchange_
20077 * @fires Player#texttrackchange
20078 * @listens Tech#texttrackchange
20079 */
20080'texttrackchange'];
20081
20082/**
20083 * An instance of the `Player` class is created when any of the Video.js setup methods
20084 * are used to initialize a video.
20085 *
20086 * After an instance has been created it can be accessed globally in two ways:
20087 * 1. By calling `videojs('example_video_1');`
20088 * 2. By using it directly via  `videojs.players.example_video_1;`
20089 *
20090 * @extends Component
20091 */
20092
20093var Player = function (_Component) {
20094  inherits(Player, _Component);
20095
20096  /**
20097   * Create an instance of this class.
20098   *
20099   * @param {Element} tag
vendor: 22,814 bytes, lines 20100-20820
20100   *        The original video DOM element used for configuring options.
20101   *
20102   * @param {Object} [options]
20103   *        Object of option names and values.
20104   *
20105   * @param {Component~ReadyCallback} [ready]
20106   *        Ready callback function.
20107   */
20108  function Player(tag, options, ready) {
20109    classCallCheck(this, Player);
20110
20111    // Make sure tag ID exists
20112    tag.id = tag.id || 'vjs_video_' + newGUID();
20113
20114    // Set Options
20115    // The options argument overrides options set in the video tag
20116    // which overrides globally set options.
20117    // This latter part coincides with the load order
20118    // (tag must exist before Player)
20119    options = assign(Player.getTagSettings(tag), options);
20120
20121    // Delay the initialization of children because we need to set up
20122    // player properties first, and can't use `this` before `super()`
20123    options.initChildren = false;
20124
20125    // Same with creating the element
20126    options.createEl = false;
20127
20128    // we don't want the player to report touch activity on itself
20129    // see enableTouchActivity in Component
20130    options.reportTouchActivity = false;
20131
20132    // If language is not set, get the closest lang attribute
20133    if (!options.language) {
20134      if (typeof tag.closest === 'function') {
20135        var closest = tag.closest('[lang]');
20136
20137        if (closest) {
20138          options.language = closest.getAttribute('lang');
20139        }
20140      } else {
20141        var element = tag;
20142
20143        while (element && element.nodeType === 1) {
20144          if (getAttributes(element).hasOwnProperty('lang')) {
20145            options.language = element.getAttribute('lang');
20146            break;
20147          }
20148          element = element.parentNode;
20149        }
20150      }
20151    }
20152
20153    // Run base component initializing with new options
20154
20155    // Turn off API access because we're loading a new tech that might load asynchronously
20156    var _this = possibleConstructorReturn(this, _Component.call(this, null, options, ready));
20157
20158    _this.isReady_ = false;
20159
20160    // if the global option object was accidentally blown away by
20161    // someone, bail early with an informative error
20162    if (!_this.options_ || !_this.options_.techOrder || !_this.options_.techOrder.length) {
20163      throw new Error('No techOrder specified. Did you overwrite ' + 'videojs.options instead of just changing the ' + 'properties you want to override?');
20164    }
20165
20166    // Store the original tag used to set options
20167    _this.tag = tag;
20168
20169    // Store the tag attributes used to restore html5 element
20170    _this.tagAttributes = tag && getAttributes(tag);
20171
20172    // Update current language
20173    _this.language(_this.options_.language);
20174
20175    // Update Supported Languages
20176    if (options.languages) {
20177      // Normalise player option languages to lowercase
20178      var languagesToLower = {};
20179
20180      Object.getOwnPropertyNames(options.languages).forEach(function (name$$1) {
20181        languagesToLower[name$$1.toLowerCase()] = options.languages[name$$1];
20182      });
20183      _this.languages_ = languagesToLower;
20184    } else {
20185      _this.languages_ = Player.prototype.options_.languages;
20186    }
20187
20188    // Cache for video property values.
20189    _this.cache_ = {};
20190
20191    // Set poster
20192    _this.poster_ = options.poster || '';
20193
20194    // Set controls
20195    _this.controls_ = !!options.controls;
20196
20197    // Set default values for lastVolume
20198    _this.cache_.lastVolume = 1;
20199
20200    // Original tag settings stored in options
20201    // now remove immediately so native controls don't flash.
20202    // May be turned back on by HTML5 tech if nativeControlsForTouch is true
20203    tag.controls = false;
20204
20205    /*
20206     * Store the internal state of scrubbing
20207     *
20208     * @private
20209     * @return {Boolean} True if the user is scrubbing
20210     */
20211    _this.scrubbing_ = false;
20212
20213    _this.el_ = _this.createEl();
20214
20215    // Make this an evented object and use `el_` as its event bus.
20216    evented(_this, { eventBusKey: 'el_' });
20217
20218    // We also want to pass the original player options to each component and plugin
20219    // as well so they don't need to reach back into the player for options later.
20220    // We also need to do another copy of this.options_ so we don't end up with
20221    // an infinite loop.
20222    var playerOptionsCopy = mergeOptions(_this.options_);
20223
20224    // Load plugins
20225    if (options.plugins) {
20226      var plugins = options.plugins;
20227
20228      Object.keys(plugins).forEach(function (name$$1) {
20229        if (typeof this[name$$1] === 'function') {
20230          this[name$$1](plugins[name$$1]);
20231        } else {
20232          throw new Error('plugin "' + name$$1 + '" does not exist');
20233        }
20234      }, _this);
20235    }
20236
20237    _this.options_.playerOptions = playerOptionsCopy;
20238
20239    _this.middleware_ = [];
20240
20241    _this.initChildren();
20242
20243    // Set isAudio based on whether or not an audio tag was used
20244    _this.isAudio(tag.nodeName.toLowerCase() === 'audio');
20245
20246    // Update controls className. Can't do this when the controls are initially
20247    // set because the element doesn't exist yet.
20248    if (_this.controls()) {
20249      _this.addClass('vjs-controls-enabled');
20250    } else {
20251      _this.addClass('vjs-controls-disabled');
20252    }
20253
20254    // Set ARIA label and region role depending on player type
20255    _this.el_.setAttribute('role', 'region');
20256    if (_this.isAudio()) {
20257      _this.el_.setAttribute('aria-label', _this.localize('Audio Player'));
20258    } else {
20259      _this.el_.setAttribute('aria-label', _this.localize('Video Player'));
20260    }
20261
20262    if (_this.isAudio()) {
20263      _this.addClass('vjs-audio');
20264    }
20265
20266    if (_this.flexNotSupported_()) {
20267      _this.addClass('vjs-no-flex');
20268    }
20269
20270    // TODO: Make this smarter. Toggle user state between touching/mousing
20271    // using events, since devices can have both touch and mouse events.
20272    // if (browser.TOUCH_ENABLED) {
20273    //   this.addClass('vjs-touch-enabled');
20274    // }
20275
20276    // iOS Safari has broken hover handling
20277    if (!IS_IOS) {
20278      _this.addClass('vjs-workinghover');
20279    }
20280
20281    // Make player easily findable by ID
20282    Player.players[_this.id_] = _this;
20283
20284    // Add a major version class to aid css in plugins
20285    var majorVersion = version.split('.')[0];
20286
20287    _this.addClass('vjs-v' + majorVersion);
20288
20289    // When the player is first initialized, trigger activity so components
20290    // like the control bar show themselves if needed
20291    _this.userActive(true);
20292    _this.reportUserActivity();
20293    _this.listenForUserActivity_();
20294
20295    _this.on('fullscreenchange', _this.handleFullscreenChange_);
20296    _this.on('stageclick', _this.handleStageClick_);
20297
20298    _this.changingSrc_ = false;
20299    return _this;
20300  }
20301
20302  /**
20303   * Destroys the video player and does any necessary cleanup.
20304   *
20305   * This is especially helpful if you are dynamically adding and removing videos
20306   * to/from the DOM.
20307   *
20308   * @fires Player#dispose
20309   */
20310
20311
20312  Player.prototype.dispose = function dispose() {
20313    /**
20314     * Called when the player is being disposed of.
20315     *
20316     * @event Player#dispose
20317     * @type {EventTarget~Event}
20318     */
20319    this.trigger('dispose');
20320    // prevent dispose from being called twice
20321    this.off('dispose');
20322
20323    if (this.styleEl_ && this.styleEl_.parentNode) {
20324      this.styleEl_.parentNode.removeChild(this.styleEl_);
20325    }
20326
20327    // Kill reference to this player
20328    Player.players[this.id_] = null;
20329
20330    if (this.tag && this.tag.player) {
20331      this.tag.player = null;
20332    }
20333
20334    if (this.el_ && this.el_.player) {
20335      this.el_.player = null;
20336    }
20337
20338    if (this.tech_) {
20339      this.tech_.dispose();
20340    }
20341
20342    _Component.prototype.dispose.call(this);
20343  };
20344
20345  /**
20346   * Create the `Player`'s DOM element.
20347   *
20348   * @return {Element}
20349   *         The DOM element that gets created.
20350   */
20351
20352
20353  Player.prototype.createEl = function createEl$$1() {
20354    var tag = this.tag;
20355    var el = void 0;
20356    var playerElIngest = this.playerElIngest_ = tag.parentNode && tag.parentNode.hasAttribute && tag.parentNode.hasAttribute('data-vjs-player');
20357
20358    if (playerElIngest) {
20359      el = this.el_ = tag.parentNode;
20360    } else {
20361      el = this.el_ = _Component.prototype.createEl.call(this, 'div');
20362    }
20363
20364    // set tabindex to -1 so we could focus on the player element
20365    tag.setAttribute('tabindex', '-1');
20366
20367    // Remove width/height attrs from tag so CSS can make it 100% width/height
20368    tag.removeAttribute('width');
20369    tag.removeAttribute('height');
20370
20371    // Copy over all the attributes from the tag, including ID and class
20372    // ID will now reference player box, not the video tag
20373    var attrs = getAttributes(tag);
20374
20375    Object.getOwnPropertyNames(attrs).forEach(function (attr) {
20376      // workaround so we don't totally break IE7
20377      // http://stackoverflow.com/questions/3653444/css-styles-not-applied-on-dynamic-elements-in-internet-explorer-7
20378      if (attr === 'class') {
20379        el.className += ' ' + attrs[attr];
20380      } else {
20381        el.setAttribute(attr, attrs[attr]);
20382      }
20383    });
20384
20385    // Update tag id/class for use as HTML5 playback tech
20386    // Might think we should do this after embedding in container so .vjs-tech class
20387    // doesn't flash 100% width/height, but class only applies with .video-js parent
20388    tag.playerId = tag.id;
20389    tag.id += '_html5_api';
20390    tag.className = 'vjs-tech';
20391
20392    // Make player findable on elements
20393    tag.player = el.player = this;
20394    // Default state of video is paused
20395    this.addClass('vjs-paused');
20396
20397    // Add a style element in the player that we'll use to set the width/height
20398    // of the player in a way that's still overrideable by CSS, just like the
20399    // video element
20400    if (window_1.VIDEOJS_NO_DYNAMIC_STYLE !== true) {
20401      this.styleEl_ = createStyleElement('vjs-styles-dimensions');
20402      var defaultsStyleEl = $('.vjs-styles-defaults');
20403      var head = $('head');
20404
20405      head.insertBefore(this.styleEl_, defaultsStyleEl ? defaultsStyleEl.nextSibling : head.firstChild);
20406    }
20407
20408    // Pass in the width/height/aspectRatio options which will update the style el
20409    this.width(this.options_.width);
20410    this.height(this.options_.height);
20411    this.fluid(this.options_.fluid);
20412    this.aspectRatio(this.options_.aspectRatio);
20413
20414    // Hide any links within the video/audio tag, because IE doesn't hide them completely.
20415    var links = tag.getElementsByTagName('a');
20416
20417    for (var i = 0; i < links.length; i++) {
20418      var linkEl = links.item(i);
20419
20420      addClass(linkEl, 'vjs-hidden');
20421      linkEl.setAttribute('hidden', 'hidden');
20422    }
20423
20424    // insertElFirst seems to cause the networkState to flicker from 3 to 2, so
20425    // keep track of the original for later so we can know if the source originally failed
20426    tag.initNetworkState_ = tag.networkState;
20427
20428    // Wrap video tag in div (el/box) container
20429    if (tag.parentNode && !playerElIngest) {
20430      tag.parentNode.insertBefore(el, tag);
20431    }
20432
20433    // insert the tag as the first child of the player element
20434    // then manually add it to the children array so that this.addChild
20435    // will work properly for other components
20436    //
20437    // Breaks iPhone, fixed in HTML5 setup.
20438    prependTo(tag, el);
20439    this.children_.unshift(tag);
20440
20441    // Set lang attr on player to ensure CSS :lang() in consistent with player
20442    // if it's been set to something different to the doc
20443    this.el_.setAttribute('lang', this.language_);
20444
20445    this.el_ = el;
20446
20447    return el;
20448  };
20449
20450  /**
20451   * A getter/setter for the `Player`'s width. Returns the player's configured value.
20452   * To get the current width use `currentWidth()`.
20453   *
20454   * @param {number} [value]
20455   *        The value to set the `Player`'s width to.
20456   *
20457   * @return {number}
20458   *         The current width of the `Player` when getting.
20459   */
20460
20461
20462  Player.prototype.width = function width(value) {
20463    return this.dimension('width', value);
20464  };
20465
20466  /**
20467   * A getter/setter for the `Player`'s height. Returns the player's configured value.
20468   * To get the current height use `currentheight()`.
20469   *
20470   * @param {number} [value]
20471   *        The value to set the `Player`'s heigth to.
20472   *
20473   * @return {number}
20474   *         The current height of the `Player` when getting.
20475   */
20476
20477
20478  Player.prototype.height = function height(value) {
20479    return this.dimension('height', value);
20480  };
20481
20482  /**
20483   * A getter/setter for the `Player`'s width & height.
20484   *
20485   * @param {string} dimension
20486   *        This string can be:
20487   *        - 'width'
20488   *        - 'height'
20489   *
20490   * @param {number} [value]
20491   *        Value for dimension specified in the first argument.
20492   *
20493   * @return {number}
20494   *         The dimension arguments value when getting (width/height).
20495   */
20496
20497
20498  Player.prototype.dimension = function dimension(_dimension, value) {
20499    var privDimension = _dimension + '_';
20500
20501    if (value === undefined) {
20502      return this[privDimension] || 0;
20503    }
20504
20505    if (value === '') {
20506      // If an empty string is given, reset the dimension to be automatic
20507      this[privDimension] = undefined;
20508    } else {
20509      var parsedVal = parseFloat(value);
20510
20511      if (isNaN(parsedVal)) {
20512        log$1.error('Improper value "' + value + '" supplied for for ' + _dimension);
20513        return;
20514      }
20515
20516      this[privDimension] = parsedVal;
20517    }
20518
20519    this.updateStyleEl_();
20520  };
20521
20522  /**
20523   * A getter/setter/toggler for the vjs-fluid `className` on the `Player`.
20524   *
20525   * @param {boolean} [bool]
20526   *        - A value of true adds the class.
20527   *        - A value of false removes the class.
20528   *        - No value will toggle the fluid class.
20529   *
20530   * @return {boolean|undefined}
20531   *         - The value of fluid when getting.
20532   *         - `undefined` when setting.
20533   */
20534
20535
20536  Player.prototype.fluid = function fluid(bool) {
20537    if (bool === undefined) {
20538      return !!this.fluid_;
20539    }
20540
20541    this.fluid_ = !!bool;
20542
20543    if (bool) {
20544      this.addClass('vjs-fluid');
20545    } else {
20546      this.removeClass('vjs-fluid');
20547    }
20548
20549    this.updateStyleEl_();
20550  };
20551
20552  /**
20553   * Get/Set the aspect ratio
20554   *
20555   * @param {string} [ratio]
20556   *        Aspect ratio for player
20557   *
20558   * @return {string|undefined}
20559   *         returns the current aspect ratio when getting
20560   */
20561
20562  /**
20563   * A getter/setter for the `Player`'s aspect ratio.
20564   *
20565   * @param {string} [ratio]
20566   *        The value to set the `Player's aspect ratio to.
20567   *
20568   * @return {string|undefined}
20569   *         - The current aspect ratio of the `Player` when getting.
20570   *         - undefined when setting
20571   */
20572
20573
20574  Player.prototype.aspectRatio = function aspectRatio(ratio) {
20575    if (ratio === undefined) {
20576      return this.aspectRatio_;
20577    }
20578
20579    // Check for width:height format
20580    if (!/^\d+\:\d+$/.test(ratio)) {
20581      throw new Error('Improper value supplied for aspect ratio. The format should be width:height, for example 16:9.');
20582    }
20583    this.aspectRatio_ = ratio;
20584
20585    // We're assuming if you set an aspect ratio you want fluid mode,
20586    // because in fixed mode you could calculate width and height yourself.
20587    this.fluid(true);
20588
20589    this.updateStyleEl_();
20590  };
20591
20592  /**
20593   * Update styles of the `Player` element (height, width and aspect ratio).
20594   *
20595   * @private
20596   * @listens Tech#loadedmetadata
20597   */
20598
20599
20600  Player.prototype.updateStyleEl_ = function updateStyleEl_() {
20601    if (window_1.VIDEOJS_NO_DYNAMIC_STYLE === true) {
20602      var _width = typeof this.width_ === 'number' ? this.width_ : this.options_.width;
20603      var _height = typeof this.height_ === 'number' ? this.height_ : this.options_.height;
20604      var techEl = this.tech_ && this.tech_.el();
20605
20606      if (techEl) {
20607        if (_width >= 0) {
20608          techEl.width = _width;
20609        }
20610        if (_height >= 0) {
20611          techEl.height = _height;
20612        }
20613      }
20614
20615      return;
20616    }
20617
20618    var width = void 0;
20619    var height = void 0;
20620    var aspectRatio = void 0;
20621    var idClass = void 0;
20622
20623    // The aspect ratio is either used directly or to calculate width and height.
20624    if (this.aspectRatio_ !== undefined && this.aspectRatio_ !== 'auto') {
20625      // Use any aspectRatio that's been specifically set
20626      aspectRatio = this.aspectRatio_;
20627    } else if (this.videoWidth() > 0) {
20628      // Otherwise try to get the aspect ratio from the video metadata
20629      aspectRatio = this.videoWidth() + ':' + this.videoHeight();
20630    } else {
20631      // Or use a default. The video element's is 2:1, but 16:9 is more common.
20632      aspectRatio = '16:9';
20633    }
20634
20635    // Get the ratio as a decimal we can use to calculate dimensions
20636    var ratioParts = aspectRatio.split(':');
20637    var ratioMultiplier = ratioParts[1] / ratioParts[0];
20638
20639    if (this.width_ !== undefined) {
20640      // Use any width that's been specifically set
20641      width = this.width_;
20642    } else if (this.height_ !== undefined) {
20643      // Or calulate the width from the aspect ratio if a height has been set
20644      width = this.height_ / ratioMultiplier;
20645    } else {
20646      // Or use the video's metadata, or use the video el's default of 300
20647      width = this.videoWidth() || 300;
20648    }
20649
20650    if (this.height_ !== undefined) {
20651      // Use any height that's been specifically set
20652      height = this.height_;
20653    } else {
20654      // Otherwise calculate the height from the ratio and the width
20655      height = width * ratioMultiplier;
20656    }
20657
20658    // Ensure the CSS class is valid by starting with an alpha character
20659    if (/^[^a-zA-Z]/.test(this.id())) {
20660      idClass = 'dimensions-' + this.id();
20661    } else {
20662      idClass = this.id() + '-dimensions';
20663    }
20664
20665    // Ensure the right class is still on the player for the style element
20666    this.addClass(idClass);
20667
20668    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    ');
20669  };
20670
20671  /**
20672   * Load/Create an instance of playback {@link Tech} including element
20673   * and API methods. Then append the `Tech` element in `Player` as a child.
20674   *
20675   * @param {string} techName
20676   *        name of the playback technology
20677   *
20678   * @param {string} source
20679   *        video source
20680   *
20681   * @private
20682   */
20683
20684
20685  Player.prototype.loadTech_ = function loadTech_(techName, source) {
20686    var _this2 = this;
20687
20688    // Pause and remove current playback technology
20689    if (this.tech_) {
20690      this.unloadTech_();
20691    }
20692
20693    var titleTechName = toTitleCase(techName);
20694    var camelTechName = techName.charAt(0).toLowerCase() + techName.slice(1);
20695
20696    // get rid of the HTML5 video tag as soon as we are using another tech
20697    if (titleTechName !== 'Html5' && this.tag) {
20698      Tech.getTech('Html5').disposeMediaElement(this.tag);
20699      this.tag.player = null;
20700      this.tag = null;
20701    }
20702
20703    this.techName_ = titleTechName;
20704
20705    // Turn off API access because we're loading a new tech that might load asynchronously
20706    this.isReady_ = false;
20707
20708    // Grab tech-specific options from player options and add source and parent element to use.
20709    var techOptions = {
20710      source: source,
20711      'nativeControlsForTouch': this.options_.nativeControlsForTouch,
20712      'playerId': this.id(),
20713      'techId': this.id() + '_' + titleTechName + '_api',
20714      'autoplay': this.options_.autoplay,
20715      'playsinline': this.options_.playsinline,
20716      'preload': this.options_.preload,
20717      'loop': this.options_.loop,
20718      'muted': this.options_.muted,
20719      'poster': this.poster(),
20720      'language': this.language(),
20721      'playerElIngest': this.playerElIngest_ || false,
20722      'vtt.js': this.options_['vtt.js']
20723    };
20724
20725    ALL.names.forEach(function (name$$1) {
20726      var props = ALL[name$$1];
20727
20728      techOptions[props.getterName] = _this2[props.privateName];
20729    });
20730
20731    assign(techOptions, this.options_[titleTechName]);
20732    assign(techOptions, this.options_[camelTechName]);
20733    assign(techOptions, this.options_[techName.toLowerCase()]);
20734
20735    if (this.tag) {
20736      techOptions.tag = this.tag;
20737    }
20738
20739    if (source && source.src === this.cache_.src && this.cache_.currentTime > 0) {
20740      techOptions.startTime = this.cache_.currentTime;
20741    }
20742
20743    // Initialize tech instance
20744    var TechClass = Tech.getTech(techName);
20745
20746    if (!TechClass) {
20747      throw new Error('No Tech named \'' + titleTechName + '\' exists! \'' + titleTechName + '\' should be registered using videojs.registerTech()\'');
20748    }
20749
20750    this.tech_ = new TechClass(techOptions);
20751
20752    // player.triggerReady is always async, so don't need this to be async
20753    this.tech_.ready(bind(this, this.handleTechReady_), true);
20754
20755    textTrackConverter.jsonToTextTracks(this.textTracksJson_ || [], this.tech_);
20756
20757    // Listen to all HTML5-defined events and trigger them on the player
20758    TECH_EVENTS_RETRIGGER.forEach(function (event) {
20759      _this2.on(_this2.tech_, event, _this2['handleTech' + toTitleCase(event) + '_']);
20760    });
20761    this.on(this.tech_, 'loadstart', this.handleTechLoadStart_);
20762    this.on(this.tech_, 'waiting', this.handleTechWaiting_);
20763    this.on(this.tech_, 'canplay', this.handleTechCanPlay_);
20764    this.on(this.tech_, 'canplaythrough', this.handleTechCanPlayThrough_);
20765    this.on(this.tech_, 'playing', this.handleTechPlaying_);
20766    this.on(this.tech_, 'ended', this.handleTechEnded_);
20767    this.on(this.tech_, 'seeking', this.handleTechSeeking_);
20768    this.on(this.tech_, 'seeked', this.handleTechSeeked_);
20769    this.on(this.tech_, 'play', this.handleTechPlay_);
20770    this.on(this.tech_, 'firstplay', this.handleTechFirstPlay_);
20771    this.on(this.tech_, 'pause', this.handleTechPause_);
20772    this.on(this.tech_, 'durationchange', this.handleTechDurationChange_);
20773    this.on(this.tech_, 'fullscreenchange', this.handleTechFullscreenChange_);
20774    this.on(this.tech_, 'error', this.handleTechError_);
20775    this.on(this.tech_, 'loadedmetadata', this.updateStyleEl_);
20776    this.on(this.tech_, 'posterchange', this.handleTechPosterChange_);
20777    this.on(this.tech_, 'textdata', this.handleTechTextData_);
20778
20779    this.usingNativeControls(this.techGet_('controls'));
20780
20781    if (this.controls() && !this.usingNativeControls()) {
20782      this.addTechControlsListeners_();
20783    }
20784
20785    // Add the tech element in the DOM if it was not already there
20786    // Make sure to not insert the original video element if using Html5
20787    if (this.tech_.el().parentNode !== this.el() && (titleTechName !== 'Html5' || !this.tag)) {
20788      prependTo(this.tech_.el(), this.el());
20789    }
20790
20791    // Get rid of the original video tag reference after the first tech is loaded
20792    if (this.tag) {
20793      this.tag.player = null;
20794      this.tag = null;
20795    }
20796  };
20797
20798  /**
20799   * Unload and dispose of the current playback {@link Tech}.
20800   *
20801   * @private
20802   */
20803
20804
20805  Player.prototype.unloadTech_ = function unloadTech_() {
20806    var _this3 = this;
20807
20808    // Save the current text tracks so that we can reuse the same text tracks with the next tech
20809    ALL.names.forEach(function (name$$1) {
20810      var props = ALL[name$$1];
20811
20812      _this3[props.privateName] = _this3[props.getterName]();
20813    });
20814    this.textTracksJson_ = textTrackConverter.textTracksToJson(this.tech_);
20815
20816    this.isReady_ = false;
20817
20818    this.tech_.dispose();
20819
20820    this.tech_ = false;
20821  };
20822
20823  /**
20824   * Return a reference to the current {@link Tech}.
20825   * It will print a warning by default about the danger of using the tech directly
20826   * but any argument that is passed in will silence the warning.
20827   *
20828   * @param {*} [safety]
20829   *        Anything passed in to silence the warning
20830   *
20831   * @return {Tech}
20832   *         The Tech
20833   */
20834
20835
20836  Player.prototype.tech = function tech(safety) {
20837    if (safety === undefined) {
20838      log$1.warn(tsml(_templateObject$1));
20839    }
20840
20841    return this.tech_;
20842  };
20843
20844  /**
20845   * Set up click and touch listeners for the playback element
20846   *
20847   * - On desktops: a click on the video itself will toggle playback
20848   * - On mobile devices: a click on the video toggles controls
20849   *   which is done by toggling the user state between active and
20850   *   inactive
20851   * - A tap can signal that a user has become active or has become inactive
20852   *   e.g. a quick tap on an iPhone movie should reveal the controls. Another
20853   *   quick tap should hide them again (signaling the user is in an inactive
20854   *   viewing state)
20855   * - In addition to this, we still want the user to be considered inactive after
20856   *   a few seconds of inactivity.
20857   *
20858   * > Note: the only part of iOS interaction we can't mimic with this setup
20859   * is a touch and hold on the video element counting as activity in order to
20860   * keep the controls showing, but that shouldn't be an issue. A touch and hold
20861   * on any controls will still keep the user active
20862   *
20863   * @private
20864   */
20865
20866
20867  Player.prototype.addTechControlsListeners_ = function addTechControlsListeners_() {
20868    // Make sure to remove all the previous listeners in case we are called multiple times.
20869    this.removeTechControlsListeners_();
20870
20871    // Some browsers (Chrome & IE) don't trigger a click on a flash swf, but do
20872    // trigger mousedown/up.
20873    // http://stackoverflow.com/questions/1444562/javascript-onclick-event-over-flash-object
20874    // Any touch events are set to block the mousedown event from happening
20875    this.on(this.tech_, 'mousedown', this.handleTechClick_);
20876
20877    // If the controls were hidden we don't want that to change without a tap event
20878    // so we'll check if the controls were already showing before reporting user
20879    // activity
20880    this.on(this.tech_, 'touchstart', this.handleTechTouchStart_);
20881    this.on(this.tech_, 'touchmove', this.handleTechTouchMove_);
20882    this.on(this.tech_, 'touchend', this.handleTechTouchEnd_);
20883
20884    // The tap listener needs to come after the touchend listener because the tap
20885    // listener cancels out any reportedUserActivity when setting userActive(false)
20886    this.on(this.tech_, 'tap', this.handleTechTap_);
20887  };
20888
20889  /**
20890   * Remove the listeners used for click and tap controls. This is needed for
20891   * toggling to controls disabled, where a tap/touch should do nothing.
20892   *
20893   * @private
20894   */
20895
20896
20897  Player.prototype.removeTechControlsListeners_ = function removeTechControlsListeners_() {
20898    // We don't want to just use `this.off()` because there might be other needed
20899    // listeners added by techs that extend this.
20900    this.off(this.tech_, 'tap', this.handleTechTap_);
20901    this.off(this.tech_, 'touchstart', this.handleTechTouchStart_);
20902    this.off(this.tech_, 'touchmove', this.handleTechTouchMove_);
20903    this.off(this.tech_, 'touchend', this.handleTechTouchEnd_);
20904    this.off(this.tech_, 'mousedown', this.handleTechClick_);
20905  };
20906
20907  /**
20908   * Player waits for the tech to be ready
20909   *
20910   * @private
20911   */
20912
20913
20914  Player.prototype.handleTechReady_ = function handleTechReady_() {
20915    this.triggerReady();
20916
20917    // Keep the same volume as before
20918    if (this.cache_.volume) {
20919      this.techCall_('setVolume', this.cache_.volume);
20920    }
20921
20922    // Look if the tech found a higher resolution poster while loading
20923    this.handleTechPosterChange_();
20924
20925    // Update the duration if available
20926    this.handleTechDurationChange_();
20927
20928    // Chrome and Safari both have issues with autoplay.
20929    // In Safari (5.1.1), when we move the video element into the container div, autoplay doesn't work.
20930    // In Chrome (15), if you have autoplay + a poster + no controls, the video gets hidden (but audio plays)
20931    // This fixes both issues. Need to wait for API, so it updates displays correctly
20932    if ((this.src() || this.currentSrc()) && this.tag && this.options_.autoplay && this.paused()) {
vendor: 13,063 bytes, lines 20933-21473
20933      try {
20934        // Chrome Fix. Fixed in Chrome v16.
20935        delete this.tag.poster;
20936      } catch (e) {
20937        log$1('deleting tag.poster throws in some browsers', e);
20938      }
20939    }
20940  };
20941
20942  /**
20943   * Retrigger the `loadstart` event that was triggered by the {@link Tech}. This
20944   * function will also trigger {@link Player#firstplay} if it is the first loadstart
20945   * for a video.
20946   *
20947   * @fires Player#loadstart
20948   * @fires Player#firstplay
20949   * @listens Tech#loadstart
20950   * @private
20951   */
20952
20953
20954  Player.prototype.handleTechLoadStart_ = function handleTechLoadStart_() {
20955    // TODO: Update to use `emptied` event instead. See #1277.
20956
20957    this.removeClass('vjs-ended');
20958    this.removeClass('vjs-seeking');
20959
20960    // reset the error state
20961    this.error(null);
20962
20963    // If it's already playing we want to trigger a firstplay event now.
20964    // The firstplay event relies on both the play and loadstart events
20965    // which can happen in any order for a new source
20966    if (!this.paused()) {
20967      /**
20968       * Fired when the user agent begins looking for media data
20969       *
20970       * @event Player#loadstart
20971       * @type {EventTarget~Event}
20972       */
20973      this.trigger('loadstart');
20974      this.trigger('firstplay');
20975    } else {
20976      // reset the hasStarted state
20977      this.hasStarted(false);
20978      this.trigger('loadstart');
20979    }
20980  };
20981
20982  /**
20983   * Add/remove the vjs-has-started class
20984   *
20985   * @fires Player#firstplay
20986   *
20987   * @param {boolean} hasStarted
20988   *        - true: adds the class
20989   *        - false: remove the class
20990   *
20991   * @return {boolean}
20992   *         the boolean value of hasStarted
20993   */
20994
20995
20996  Player.prototype.hasStarted = function hasStarted(_hasStarted) {
20997    if (_hasStarted !== undefined) {
20998      // only update if this is a new value
20999      if (this.hasStarted_ !== _hasStarted) {
21000        this.hasStarted_ = _hasStarted;
21001        if (_hasStarted) {
21002          this.addClass('vjs-has-started');
21003          // trigger the firstplay event if this newly has played
21004          this.trigger('firstplay');
21005        } else {
21006          this.removeClass('vjs-has-started');
21007        }
21008      }
21009      return;
21010    }
21011    return !!this.hasStarted_;
21012  };
21013
21014  /**
21015   * Fired whenever the media begins or resumes playback
21016   *
21017   * @see [Spec]{@link https://html.spec.whatwg.org/multipage/embedded-content.html#dom-media-play}
21018   * @fires Player#play
21019   * @listens Tech#play
21020   * @private
21021   */
21022
21023
21024  Player.prototype.handleTechPlay_ = function handleTechPlay_() {
21025    this.removeClass('vjs-ended');
21026    this.removeClass('vjs-paused');
21027    this.addClass('vjs-playing');
21028
21029    // hide the poster when the user hits play
21030    this.hasStarted(true);
21031    /**
21032     * Triggered whenever an {@link Tech#play} event happens. Indicates that
21033     * playback has started or resumed.
21034     *
21035     * @event Player#play
21036     * @type {EventTarget~Event}
21037     */
21038    this.trigger('play');
21039  };
21040
21041  /**
21042   * Retrigger the `waiting` event that was triggered by the {@link Tech}.
21043   *
21044   * @fires Player#waiting
21045   * @listens Tech#waiting
21046   * @private
21047   */
21048
21049
21050  Player.prototype.handleTechWaiting_ = function handleTechWaiting_() {
21051    var _this4 = this;
21052
21053    this.addClass('vjs-waiting');
21054    /**
21055     * A readyState change on the DOM element has caused playback to stop.
21056     *
21057     * @event Player#waiting
21058     * @type {EventTarget~Event}
21059     */
21060    this.trigger('waiting');
21061    this.one('timeupdate', function () {
21062      return _this4.removeClass('vjs-waiting');
21063    });
21064  };
21065
21066  /**
21067   * Retrigger the `canplay` event that was triggered by the {@link Tech}.
21068   * > Note: This is not consistent between browsers. See #1351
21069   *
21070   * @fires Player#canplay
21071   * @listens Tech#canplay
21072   * @private
21073   */
21074
21075
21076  Player.prototype.handleTechCanPlay_ = function handleTechCanPlay_() {
21077    this.removeClass('vjs-waiting');
21078    /**
21079     * The media has a readyState of HAVE_FUTURE_DATA or greater.
21080     *
21081     * @event Player#canplay
21082     * @type {EventTarget~Event}
21083     */
21084    this.trigger('canplay');
21085  };
21086
21087  /**
21088   * Retrigger the `canplaythrough` event that was triggered by the {@link Tech}.
21089   *
21090   * @fires Player#canplaythrough
21091   * @listens Tech#canplaythrough
21092   * @private
21093   */
21094
21095
21096  Player.prototype.handleTechCanPlayThrough_ = function handleTechCanPlayThrough_() {
21097    this.removeClass('vjs-waiting');
21098    /**
21099     * The media has a readyState of HAVE_ENOUGH_DATA or greater. This means that the
21100     * entire media file can be played without buffering.
21101     *
21102     * @event Player#canplaythrough
21103     * @type {EventTarget~Event}
21104     */
21105    this.trigger('canplaythrough');
21106  };
21107
21108  /**
21109   * Retrigger the `playing` event that was triggered by the {@link Tech}.
21110   *
21111   * @fires Player#playing
21112   * @listens Tech#playing
21113   * @private
21114   */
21115
21116
21117  Player.prototype.handleTechPlaying_ = function handleTechPlaying_() {
21118    this.removeClass('vjs-waiting');
21119    /**
21120     * The media is no longer blocked from playback, and has started playing.
21121     *
21122     * @event Player#playing
21123     * @type {EventTarget~Event}
21124     */
21125    this.trigger('playing');
21126  };
21127
21128  /**
21129   * Retrigger the `seeking` event that was triggered by the {@link Tech}.
21130   *
21131   * @fires Player#seeking
21132   * @listens Tech#seeking
21133   * @private
21134   */
21135
21136
21137  Player.prototype.handleTechSeeking_ = function handleTechSeeking_() {
21138    this.addClass('vjs-seeking');
21139    /**
21140     * Fired whenever the player is jumping to a new time
21141     *
21142     * @event Player#seeking
21143     * @type {EventTarget~Event}
21144     */
21145    this.trigger('seeking');
21146  };
21147
21148  /**
21149   * Retrigger the `seeked` event that was triggered by the {@link Tech}.
21150   *
21151   * @fires Player#seeked
21152   * @listens Tech#seeked
21153   * @private
21154   */
21155
21156
21157  Player.prototype.handleTechSeeked_ = function handleTechSeeked_() {
21158    this.removeClass('vjs-seeking');
21159    /**
21160     * Fired when the player has finished jumping to a new time
21161     *
21162     * @event Player#seeked
21163     * @type {EventTarget~Event}
21164     */
21165    this.trigger('seeked');
21166  };
21167
21168  /**
21169   * Retrigger the `firstplay` event that was triggered by the {@link Tech}.
21170   *
21171   * @fires Player#firstplay
21172   * @listens Tech#firstplay
21173   * @deprecated As of 6.0 firstplay event is deprecated.
21174   * @deprecated As of 6.0 passing the `starttime` option to the player and the firstplay event are deprecated.
21175   * @private
21176   */
21177
21178
21179  Player.prototype.handleTechFirstPlay_ = function handleTechFirstPlay_() {
21180    // If the first starttime attribute is specified
21181    // then we will start at the given offset in seconds
21182    if (this.options_.starttime) {
21183      log$1.warn('Passing the `starttime` option to the player will be deprecated in 6.0');
21184      this.currentTime(this.options_.starttime);
21185    }
21186
21187    this.addClass('vjs-has-started');
21188    /**
21189     * Fired the first time a video is played. Not part of the HLS spec, and this is
21190     * probably not the best implementation yet, so use sparingly. If you don't have a
21191     * reason to prevent playback, use `myPlayer.one('play');` instead.
21192     *
21193     * @event Player#firstplay
21194     * @deprecated As of 6.0 firstplay event is deprecated.
21195     * @type {EventTarget~Event}
21196     */
21197    this.trigger('firstplay');
21198  };
21199
21200  /**
21201   * Retrigger the `pause` event that was triggered by the {@link Tech}.
21202   *
21203   * @fires Player#pause
21204   * @listens Tech#pause
21205   * @private
21206   */
21207
21208
21209  Player.prototype.handleTechPause_ = function handleTechPause_() {
21210    this.removeClass('vjs-playing');
21211    this.addClass('vjs-paused');
21212    /**
21213     * Fired whenever the media has been paused
21214     *
21215     * @event Player#pause
21216     * @type {EventTarget~Event}
21217     */
21218    this.trigger('pause');
21219  };
21220
21221  /**
21222   * Retrigger the `ended` event that was triggered by the {@link Tech}.
21223   *
21224   * @fires Player#ended
21225   * @listens Tech#ended
21226   * @private
21227   */
21228
21229
21230  Player.prototype.handleTechEnded_ = function handleTechEnded_() {
21231    this.addClass('vjs-ended');
21232    if (this.options_.loop) {
21233      this.currentTime(0);
21234      this.play();
21235    } else if (!this.paused()) {
21236      this.pause();
21237    }
21238
21239    /**
21240     * Fired when the end of the media resource is reached (currentTime == duration)
21241     *
21242     * @event Player#ended
21243     * @type {EventTarget~Event}
21244     */
21245    this.trigger('ended');
21246  };
21247
21248  /**
21249   * Fired when the duration of the media resource is first known or changed
21250   *
21251   * @listens Tech#durationchange
21252   * @private
21253   */
21254
21255
21256  Player.prototype.handleTechDurationChange_ = function handleTechDurationChange_() {
21257    this.duration(this.techGet_('duration'));
21258  };
21259
21260  /**
21261   * Handle a click on the media element to play/pause
21262   *
21263   * @param {EventTarget~Event} event
21264   *        the event that caused this function to trigger
21265   *
21266   * @listens Tech#mousedown
21267   * @private
21268   */
21269
21270
21271  Player.prototype.handleTechClick_ = function handleTechClick_(event) {
21272    // We're using mousedown to detect clicks thanks to Flash, but mousedown
21273    // will also be triggered with right-clicks, so we need to prevent that
21274    if (event.button !== 0) {
21275      return;
21276    }
21277
21278    // When controls are disabled a click should not toggle playback because
21279    // the click is considered a control
21280    if (this.controls()) {
21281      if (this.paused()) {
21282        this.play();
21283      } else {
21284        this.pause();
21285      }
21286    }
21287  };
21288
21289  /**
21290   * Handle a tap on the media element. It will toggle the user
21291   * activity state, which hides and shows the controls.
21292   *
21293   * @listens Tech#tap
21294   * @private
21295   */
21296
21297
21298  Player.prototype.handleTechTap_ = function handleTechTap_() {
21299    this.userActive(!this.userActive());
21300  };
21301
21302  /**
21303   * Handle touch to start
21304   *
21305   * @listens Tech#touchstart
21306   * @private
21307   */
21308
21309
21310  Player.prototype.handleTechTouchStart_ = function handleTechTouchStart_() {
21311    this.userWasActive = this.userActive();
21312  };
21313
21314  /**
21315   * Handle touch to move
21316   *
21317   * @listens Tech#touchmove
21318   * @private
21319   */
21320
21321
21322  Player.prototype.handleTechTouchMove_ = function handleTechTouchMove_() {
21323    if (this.userWasActive) {
21324      this.reportUserActivity();
21325    }
21326  };
21327
21328  /**
21329   * Handle touch to end
21330   *
21331   * @param {EventTarget~Event} event
21332   *        the touchend event that triggered
21333   *        this function
21334   *
21335   * @listens Tech#touchend
21336   * @private
21337   */
21338
21339
21340  Player.prototype.handleTechTouchEnd_ = function handleTechTouchEnd_(event) {
21341    // Stop the mouse events from also happening
21342    event.preventDefault();
21343  };
21344
21345  /**
21346   * Fired when the player switches in or out of fullscreen mode
21347   *
21348   * @private
21349   * @listens Player#fullscreenchange
21350   */
21351
21352
21353  Player.prototype.handleFullscreenChange_ = function handleFullscreenChange_() {
21354    if (this.isFullscreen()) {
21355      this.addClass('vjs-fullscreen');
21356    } else {
21357      this.removeClass('vjs-fullscreen');
21358    }
21359  };
21360
21361  /**
21362   * native click events on the SWF aren't triggered on IE11, Win8.1RT
21363   * use stageclick events triggered from inside the SWF instead
21364   *
21365   * @private
21366   * @listens stageclick
21367   */
21368
21369
21370  Player.prototype.handleStageClick_ = function handleStageClick_() {
21371    this.reportUserActivity();
21372  };
21373
21374  /**
21375   * Handle Tech Fullscreen Change
21376   *
21377   * @param {EventTarget~Event} event
21378   *        the fullscreenchange event that triggered this function
21379   *
21380   * @param {Object} data
21381   *        the data that was sent with the event
21382   *
21383   * @private
21384   * @listens Tech#fullscreenchange
21385   * @fires Player#fullscreenchange
21386   */
21387
21388
21389  Player.prototype.handleTechFullscreenChange_ = function handleTechFullscreenChange_(event, data) {
21390    if (data) {
21391      this.isFullscreen(data.isFullscreen);
21392    }
21393    /**
21394     * Fired when going in and out of fullscreen.
21395     *
21396     * @event Player#fullscreenchange
21397     * @type {EventTarget~Event}
21398     */
21399    this.trigger('fullscreenchange');
21400  };
21401
21402  /**
21403   * Fires when an error occurred during the loading of an audio/video.
21404   *
21405   * @private
21406   * @listens Tech#error
21407   */
21408
21409
21410  Player.prototype.handleTechError_ = function handleTechError_() {
21411    var error = this.tech_.error();
21412
21413    this.error(error);
21414  };
21415
21416  /**
21417   * Retrigger the `textdata` event that was triggered by the {@link Tech}.
21418   *
21419   * @fires Player#textdata
21420   * @listens Tech#textdata
21421   * @private
21422   */
21423
21424
21425  Player.prototype.handleTechTextData_ = function handleTechTextData_() {
21426    var data = null;
21427
21428    if (arguments.length > 1) {
21429      data = arguments[1];
21430    }
21431
21432    /**
21433     * Fires when we get a textdata event from tech
21434     *
21435     * @event Player#textdata
21436     * @type {EventTarget~Event}
21437     */
21438    this.trigger('textdata', data);
21439  };
21440
21441  /**
21442   * Get object for cached values.
21443   *
21444   * @return {Object}
21445   *         get the current object cache
21446   */
21447
21448
21449  Player.prototype.getCache = function getCache() {
21450    return this.cache_;
21451  };
21452
21453  /**
21454   * Pass values to the playback tech
21455   *
21456   * @param {string} [method]
21457   *        the method to call
21458   *
21459   * @param {Object} arg
21460   *        the argument to pass
21461   *
21462   * @private
21463   */
21464
21465
21466  Player.prototype.techCall_ = function techCall_(method, arg) {
21467    // If it's not ready yet, call method when it is
21468
21469    this.ready(function () {
21470      if (method in allowedSetters) {
21471        return set$1(this.middleware_, this.tech_, method, arg);
21472      }
21473
21474      try {
21475        if (this.tech_) {
21476          this.tech_[method](arg);
21477        }
21478      } catch (e) {
21479        log$1(e);
21480        throw e;
21481      }
21482    }, true);
21483  };
21484
21485  /**
21486   * Get calls can't wait for the tech, and sometimes don't need to.
21487   *
21488   * @param {string} method
21489   *        Tech method
21490   *
21491   * @return {Function|undefined}
21492   *         the method or undefined
21493   *
21494   * @private
21495   */
21496
21497
21498  Player.prototype.techGet_ = function techGet_(method) {
21499    if (this.tech_ && this.tech_.isReady_) {
21500
21501      if (method in allowedGetters) {
21502        return get$1(this.middleware_, this.tech_, method);
21503      }
21504
21505      // Flash likes to die and reload when you hide or reposition it.
21506      // In these cases the object methods go away and we get errors.
21507      // When that happens we'll catch the errors and inform tech that it's not ready any more.
21508      try {
21509        return this.tech_[method]();
21510      } catch (e) {
21511        // When building additional tech libs, an expected method may not be defined yet
21512        if (this.tech_[method] === undefined) {
21513          log$1('Video.js: ' + method + ' method not defined for ' + this.techName_ + ' playback technology.', e);
21514
21515          // When a method isn't available on the object it throws a TypeError
21516        } else if (e.name === 'TypeError') {
21517          log$1('Video.js: ' + method + ' unavailable on ' + this.techName_ + ' playback technology element.', e);
21518          this.tech_.isReady_ = false;
21519        } else {
21520          log$1(e);
21521        }
21522        throw e;
21523      }
21524    }
21525
21526    return;
21527  };
21528
21529  /**
21530   * start media playback
21531   *
21532   * @return {Promise|undefined}
21533   *         Returns a `Promise` if the browser returns one, for most browsers this will
21534   *         return undefined.
21535   */
21536
21537
21538  Player.prototype.play = function play() {
21539    if (this.changingSrc_) {
21540      this.ready(function () {
21541        var retval = this.techGet_('play');
21542
21543        // silence errors (unhandled promise from play)
21544        if (retval !== undefined && typeof retval.then === 'function') {
21545          retval.then(null, function (e) {});
21546        }
21547      });
21548
21549      // Only calls the tech's play if we already have a src loaded
21550    } else if (this.isReady_ && (this.src() || this.currentSrc())) {
21551      return this.techGet_('play');
21552    } else {
21553      this.ready(function () {
21554        this.tech_.one('loadstart', function () {
21555          var retval = this.play();
21556
21557          // silence errors (unhandled promise from play)
21558          if (retval !== undefined && typeof retval.then === 'function') {
21559            retval.then(null, function (e) {});
21560          }
21561        });
21562      });
21563    }
21564  };
21565
21566  /**
21567   * Pause the video playback
21568   *
21569   * @return {Player}
21570   *         A reference to the player object this function was called on
21571   */
21572
21573
21574  Player.prototype.pause = function pause() {
21575    this.techCall_('pause');
21576  };
21577
21578  /**
21579   * Check if the player is paused or has yet to play
21580   *
21581   * @return {boolean}
21582   *         - false: if the media is currently playing
21583   *         - true: if media is not currently playing
21584   */
21585
21586
21587  Player.prototype.paused = function paused() {
21588    // The initial state of paused should be true (in Safari it's actually false)
21589    return this.techGet_('paused') === false ? false : true;
21590  };
21591
21592  /**
21593   * Get a TimeRange object representing the current ranges of time that the user
21594   * has played.
21595   *
21596   * @return {TimeRange}
21597   *         A time range object that represents all the increments of time that have
21598   *         been played.
21599   */
21600
21601
21602  Player.prototype.played = function played() {
21603    return this.techGet_('played') || createTimeRanges(0, 0);
21604  };
21605
21606  /**
21607   * Returns whether or not the user is "scrubbing". Scrubbing is
21608   * when the user has clicked the progress bar handle and is
21609   * dragging it along the progress bar.
21610   *
21611   * @param {boolean} [isScrubbing]
21612   *        wether the user is or is not scrubbing
21613   *
21614   * @return {boolean}
21615   *         The value of scrubbing when getting
21616   */
21617
21618
21619  Player.prototype.scrubbing = function scrubbing(isScrubbing) {
21620    if (typeof isScrubbing === 'undefined') {
21621      return this.scrubbing_;
21622    }
21623    this.scrubbing_ = !!isScrubbing;
21624
21625    if (isScrubbing) {
21626      this.addClass('vjs-scrubbing');
21627    } else {
21628      this.removeClass('vjs-scrubbing');
21629    }
21630  };
21631
21632  /**
21633   * Get or set the current time (in seconds)
21634   *
21635   * @param {number|string} [seconds]
21636   *        The time to seek to in seconds
21637   *
21638   * @return {number}
21639   *         - the current time in seconds when getting
21640   */
21641
21642
21643  Player.prototype.currentTime = function currentTime(seconds) {
21644    if (typeof seconds !== 'undefined') {
21645      this.techCall_('setCurrentTime', seconds);
21646      return;
21647    }
21648
21649    // cache last currentTime and return. default to 0 seconds
21650    //
21651    // Caching the currentTime is meant to prevent a massive amount of reads on the tech's
vendor: 5,097 bytes, lines 21652-21840
21652    // currentTime when scrubbing, but may not provide much performance benefit afterall.
21653    // Should be tested. Also something has to read the actual current time or the cache will
21654    // never get updated.
21655    this.cache_.currentTime = this.techGet_('currentTime') || 0;
21656    return this.cache_.currentTime;
21657  };
21658
21659  /**
21660   * Normally gets the length in time of the video in seconds;
21661   * in all but the rarest use cases an argument will NOT be passed to the method
21662   *
21663   * > **NOTE**: The video must have started loading before the duration can be
21664   * known, and in the case of Flash, may not be known until the video starts
21665   * playing.
21666   *
21667   * @fires Player#durationchange
21668   *
21669   * @param {number} [seconds]
21670   *        The duration of the video to set in seconds
21671   *
21672   * @return {number}
21673   *         - The duration of the video in seconds when getting
21674   */
21675
21676
21677  Player.prototype.duration = function duration(seconds) {
21678    if (seconds === undefined) {
21679      // return NaN if the duration is not known
21680      return this.cache_.duration !== undefined ? this.cache_.duration : NaN;
21681    }
21682
21683    seconds = parseFloat(seconds);
21684
21685    // Standardize on Inifity for signaling video is live
21686    if (seconds < 0) {
21687      seconds = Infinity;
21688    }
21689
21690    if (seconds !== this.cache_.duration) {
21691      // Cache the last set value for optimized scrubbing (esp. Flash)
21692      this.cache_.duration = seconds;
21693
21694      if (seconds === Infinity) {
21695        this.addClass('vjs-live');
21696      } else {
21697        this.removeClass('vjs-live');
21698      }
21699      /**
21700       * @event Player#durationchange
21701       * @type {EventTarget~Event}
21702       */
21703      this.trigger('durationchange');
21704    }
21705  };
21706
21707  /**
21708   * Calculates how much time is left in the video. Not part
21709   * of the native video API.
21710   *
21711   * @return {number}
21712   *         The time remaining in seconds
21713   */
21714
21715
21716  Player.prototype.remainingTime = function remainingTime() {
21717    return this.duration() - this.currentTime();
21718  };
21719
21720  //
21721  // Kind of like an array of portions of the video that have been downloaded.
21722
21723  /**
21724   * Get a TimeRange object with an array of the times of the video
21725   * that have been downloaded. If you just want the percent of the
21726   * video that's been downloaded, use bufferedPercent.
21727   *
21728   * @see [Buffered Spec]{@link http://dev.w3.org/html5/spec/video.html#dom-media-buffered}
21729   *
21730   * @return {TimeRange}
21731   *         A mock TimeRange object (following HTML spec)
21732   */
21733
21734
21735  Player.prototype.buffered = function buffered() {
21736    var buffered = this.techGet_('buffered');
21737
21738    if (!buffered || !buffered.length) {
21739      buffered = createTimeRanges(0, 0);
21740    }
21741
21742    return buffered;
21743  };
21744
21745  /**
21746   * Get the percent (as a decimal) of the video that's been downloaded.
21747   * This method is not a part of the native HTML video API.
21748   *
21749   * @return {number}
21750   *         A decimal between 0 and 1 representing the percent
21751   *         that is bufferred 0 being 0% and 1 being 100%
21752   */
21753
21754
21755  Player.prototype.bufferedPercent = function bufferedPercent$$1() {
21756    return bufferedPercent(this.buffered(), this.duration());
21757  };
21758
21759  /**
21760   * Get the ending time of the last buffered time range
21761   * This is used in the progress bar to encapsulate all time ranges.
21762   *
21763   * @return {number}
21764   *         The end of the last buffered time range
21765   */
21766
21767
21768  Player.prototype.bufferedEnd = function bufferedEnd() {
21769    var buffered = this.buffered();
21770    var duration = this.duration();
21771    var end = buffered.end(buffered.length - 1);
21772
21773    if (end > duration) {
21774      end = duration;
21775    }
21776
21777    return end;
21778  };
21779
21780  /**
21781   * Get or set the current volume of the media
21782   *
21783   * @param  {number} [percentAsDecimal]
21784   *         The new volume as a decimal percent:
21785   *         - 0 is muted/0%/off
21786   *         - 1.0 is 100%/full
21787   *         - 0.5 is half volume or 50%
21788   *
21789   * @return {number}
21790   *         The current volume as a percent when getting
21791   */
21792
21793
21794  Player.prototype.volume = function volume(percentAsDecimal) {
21795    var vol = void 0;
21796
21797    if (percentAsDecimal !== undefined) {
21798      // Force value to between 0 and 1
21799      vol = Math.max(0, Math.min(1, parseFloat(percentAsDecimal)));
21800      this.cache_.volume = vol;
21801      this.techCall_('setVolume', vol);
21802
21803      if (vol > 0) {
21804        this.lastVolume_(vol);
21805      }
21806
21807      return;
21808    }
21809
21810    // Default to 1 when returning current volume.
21811    vol = parseFloat(this.techGet_('volume'));
21812    return isNaN(vol) ? 1 : vol;
21813  };
21814
21815  /**
21816   * Get the current muted state, or turn mute on or off
21817   *
21818   * @param {boolean} [muted]
21819   *        - true to mute
21820   *        - false to unmute
21821   *
21822   * @return {boolean}
21823   *         - true if mute is on and getting
21824   *         - false if mute is off and getting
21825   */
21826
21827
21828  Player.prototype.muted = function muted(_muted) {
21829    if (_muted !== undefined) {
21830      this.techCall_('setMuted', _muted);
21831      return;
21832    }
21833    return this.techGet_('muted') || false;
21834  };
21835
21836  /**
21837   * Get the current defaultMuted state, or turn defaultMuted on or off. defaultMuted
21838   * indicates the state of muted on intial playback.
21839   *
21840   * ```js
21841   *   var myPlayer = videojs('some-player-id');
21842   *
21843   *   myPlayer.src("http://www.example.com/path/to/video.mp4");
21844   *
21845   *   // get, should be false
21846   *   console.log(myPlayer.defaultMuted());
21847   *   // set to true
21848   *   myPlayer.defaultMuted(true);
21849   *   // get should be true
21850   *   console.log(myPlayer.defaultMuted());
21851   * ```
21852   *
21853   * @param {boolean} [defaultMuted]
21854   *        - true to mute
21855   *        - false to unmute
21856   *
21857   * @return {boolean|Player}
21858   *         - true if defaultMuted is on and getting
21859   *         - false if defaultMuted is off and getting
21860   *         - A reference to the current player when setting
21861   */
21862
21863
21864  Player.prototype.defaultMuted = function defaultMuted(_defaultMuted) {
21865    if (_defaultMuted !== undefined) {
21866      return this.techCall_('setDefaultMuted', _defaultMuted);
21867    }
21868    return this.techGet_('defaultMuted') || false;
21869  };
21870
21871  /**
21872   * Get the last volume, or set it
21873   *
21874   * @param  {number} [percentAsDecimal]
21875   *         The new last volume as a decimal percent:
21876   *         - 0 is muted/0%/off
21877   *         - 1.0 is 100%/full
21878   *         - 0.5 is half volume or 50%
21879   *
21880   * @return {number}
21881   *         the current value of lastVolume as a percent when getting
21882   *
21883   * @private
21884   */
21885
21886
21887  Player.prototype.lastVolume_ = function lastVolume_(percentAsDecimal) {
21888    if (percentAsDecimal !== undefined && percentAsDecimal !== 0) {
21889      this.cache_.lastVolume = percentAsDecimal;
21890      return;
21891    }
21892    return this.cache_.lastVolume;
21893  };
21894
21895  /**
21896   * Check if current tech can support native fullscreen
21897   * (e.g. with built in controls like iOS, so not our flash swf)
21898   *
21899   * @return {boolean}
21900   *         if native fullscreen is supported
21901   */
21902
21903
21904  Player.prototype.supportsFullScreen = function supportsFullScreen() {
21905    return this.techGet_('supportsFullScreen') || false;
21906  };
21907
21908  /**
21909   * Check if the player is in fullscreen mode or tell the player that it
21910   * is or is not in fullscreen mode.
21911   *
21912   * > NOTE: As of the latest HTML5 spec, isFullscreen is no longer an official
21913   * property and instead document.fullscreenElement is used. But isFullscreen is
21914   * still a valuable property for internal player workings.
21915   *
21916   * @param  {boolean} [isFS]
21917   *         Set the players current fullscreen state
21918   *
21919   * @return {boolean}
21920   *         - true if fullscreen is on and getting
21921   *         - false if fullscreen is off and getting
21922   */
21923
21924
21925  Player.prototype.isFullscreen = function isFullscreen(isFS) {
21926    if (isFS !== undefined) {
21927      this.isFullscreen_ = !!isFS;
21928      return;
21929    }
21930    return !!this.isFullscreen_;
21931  };
21932
21933  /**
21934   * Increase the size of the video to full screen
21935   * In some browsers, full screen is not supported natively, so it enters
21936   * "full window mode", where the video fills the browser window.
21937   * In browsers and devices that support native full screen, sometimes the
21938   * browser's default controls will be shown, and not the Video.js custom skin.
21939   * This includes most mobile devices (iOS, Android) and older versions of
21940   * Safari.
21941   *
21942   * @fires Player#fullscreenchange
21943   */
21944
21945
21946  Player.prototype.requestFullscreen = function requestFullscreen() {
21947    var fsApi = FullscreenApi;
21948
21949    this.isFullscreen(true);
21950
21951    if (fsApi.requestFullscreen) {
21952      // the browser supports going fullscreen at the element level so we can
21953      // take the controls fullscreen as well as the video
21954
21955      // Trigger fullscreenchange event after change
21956      // We have to specifically add this each time, and remove
21957      // when canceling fullscreen. Otherwise if there's multiple
21958      // players on a page, they would all be reacting to the same fullscreen
21959      // events
21960      on(document_1, fsApi.fullscreenchange, bind(this, function documentFullscreenChange(e) {
21961        this.isFullscreen(document_1[fsApi.fullscreenElement]);
21962
21963        // If cancelling fullscreen, remove event listener.
21964        if (this.isFullscreen() === false) {
21965          off(document_1, fsApi.fullscreenchange, documentFullscreenChange);
21966        }
21967        /**
21968         * @event Player#fullscreenchange
21969         * @type {EventTarget~Event}
21970         */
21971        this.trigger('fullscreenchange');
21972      }));
21973
21974      this.el_[fsApi.requestFullscreen]();
21975    } else if (this.tech_.supportsFullScreen()) {
21976      // we can't take the video.js controls fullscreen but we can go fullscreen
21977      // with native controls
21978      this.techCall_('enterFullScreen');
21979    } else {
21980      // fullscreen isn't supported so we'll just stretch the video element to
21981      // fill the viewport
21982      this.enterFullWindow();
21983      /**
21984       * @event Player#fullscreenchange
21985       * @type {EventTarget~Event}
21986       */
21987      this.trigger('fullscreenchange');
21988    }
21989  };
21990
21991  /**
21992   * Return the video to its normal size after having been in full screen mode
21993   *
21994   * @fires Player#fullscreenchange
21995   */
21996
21997
21998  Player.prototype.exitFullscreen = function exitFullscreen() {
21999    var fsApi = FullscreenApi;
22000
22001    this.isFullscreen(false);
22002
22003    // Check for browser element fullscreen support
22004    if (fsApi.requestFullscreen) {
22005      document_1[fsApi.exitFullscreen]();
22006    } else if (this.tech_.supportsFullScreen()) {
22007      this.techCall_('exitFullScreen');
22008    } else {
22009      this.exitFullWindow();
22010      /**
22011       * @event Player#fullscreenchange
22012       * @type {EventTarget~Event}
22013       */
22014      this.trigger('fullscreenchange');
22015    }
22016  };
22017
22018  /**
22019   * When fullscreen isn't supported we can stretch the
22020   * video container to as wide as the browser will let us.
22021   *
22022   * @fires Player#enterFullWindow
22023   */
22024
22025
22026  Player.prototype.enterFullWindow = function enterFullWindow() {
22027    this.isFullWindow = true;
22028
22029    // Storing original doc overflow value to return to when fullscreen is off
22030    this.docOrigOverflow = document_1.documentElement.style.overflow;
22031
22032    // Add listener for esc key to exit fullscreen
22033    on(document_1, 'keydown', bind(this, this.fullWindowOnEscKey));
22034
22035    // Hide any scroll bars
22036    document_1.documentElement.style.overflow = 'hidden';
22037
22038    // Apply fullscreen styles
22039    addClass(document_1.body, 'vjs-full-window');
22040
22041    /**
22042     * @event Player#enterFullWindow
22043     * @type {EventTarget~Event}
22044     */
22045    this.trigger('enterFullWindow');
22046  };
22047
22048  /**
22049   * Check for call to either exit full window or
22050   * full screen on ESC key
22051   *
22052   * @param {string} event
22053   *        Event to check for key press
22054   */
22055
22056
22057  Player.prototype.fullWindowOnEscKey = function fullWindowOnEscKey(event) {
22058    if (event.keyCode === 27) {
22059      if (this.isFullscreen() === true) {
22060        this.exitFullscreen();
22061      } else {
22062        this.exitFullWindow();
22063      }
22064    }
22065  };
22066
22067  /**
22068   * Exit full window
22069   *
22070   * @fires Player#exitFullWindow
22071   */
22072
22073
22074  Player.prototype.exitFullWindow = function exitFullWindow() {
22075    this.isFullWindow = false;
vendor: 4,466 bytes, lines 22076-22224
22076    off(document_1, 'keydown', this.fullWindowOnEscKey);
22077
22078    // Unhide scroll bars.
22079    document_1.documentElement.style.overflow = this.docOrigOverflow;
22080
22081    // Remove fullscreen styles
22082    removeClass(document_1.body, 'vjs-full-window');
22083
22084    // Resize the box, controller, and poster to original sizes
22085    // this.positionAll();
22086    /**
22087     * @event Player#exitFullWindow
22088     * @type {EventTarget~Event}
22089     */
22090    this.trigger('exitFullWindow');
22091  };
22092
22093  /**
22094   * Check whether the player can play a given mimetype
22095   *
22096   * @see https://www.w3.org/TR/2011/WD-html5-20110113/video.html#dom-navigator-canplaytype
22097   *
22098   * @param {string} type
22099   *        The mimetype to check
22100   *
22101   * @return {string}
22102   *         'probably', 'maybe', or '' (empty string)
22103   */
22104
22105
22106  Player.prototype.canPlayType = function canPlayType(type) {
22107    var can = void 0;
22108
22109    // Loop through each playback technology in the options order
22110    for (var i = 0, j = this.options_.techOrder; i < j.length; i++) {
22111      var techName = j[i];
22112      var tech = Tech.getTech(techName);
22113
22114      // Support old behavior of techs being registered as components.
22115      // Remove once that deprecated behavior is removed.
22116      if (!tech) {
22117        tech = Component.getComponent(techName);
22118      }
22119
22120      // Check if the current tech is defined before continuing
22121      if (!tech) {
22122        log$1.error('The "' + techName + '" tech is undefined. Skipped browser support check for that tech.');
22123        continue;
22124      }
22125
22126      // Check if the browser supports this technology
22127      if (tech.isSupported()) {
22128        can = tech.canPlayType(type);
22129
22130        if (can) {
22131          return can;
22132        }
22133      }
22134    }
22135
22136    return '';
22137  };
22138
22139  /**
22140   * Select source based on tech-order or source-order
22141   * Uses source-order selection if `options.sourceOrder` is truthy. Otherwise,
22142   * defaults to tech-order selection
22143   *
22144   * @param {Array} sources
22145   *        The sources for a media asset
22146   *
22147   * @return {Object|boolean}
22148   *         Object of source and tech order or false
22149   */
22150
22151
22152  Player.prototype.selectSource = function selectSource(sources) {
22153    var _this5 = this;
22154
22155    // Get only the techs specified in `techOrder` that exist and are supported by the
22156    // current platform
22157    var techs = this.options_.techOrder.map(function (techName) {
22158      return [techName, Tech.getTech(techName)];
22159    }).filter(function (_ref) {
22160      var techName = _ref[0],
22161          tech = _ref[1];
22162
22163      // Check if the current tech is defined before continuing
22164      if (tech) {
22165        // Check if the browser supports this technology
22166        return tech.isSupported();
22167      }
22168
22169      log$1.error('The "' + techName + '" tech is undefined. Skipped browser support check for that tech.');
22170      return false;
22171    });
22172
22173    // Iterate over each `innerArray` element once per `outerArray` element and execute
22174    // `tester` with both. If `tester` returns a non-falsy value, exit early and return
22175    // that value.
22176    var findFirstPassingTechSourcePair = function findFirstPassingTechSourcePair(outerArray, innerArray, tester) {
22177      var found = void 0;
22178
22179      outerArray.some(function (outerChoice) {
22180        return innerArray.some(function (innerChoice) {
22181          found = tester(outerChoice, innerChoice);
22182
22183          if (found) {
22184            return true;
22185          }
22186        });
22187      });
22188
22189      return found;
22190    };
22191
22192    var foundSourceAndTech = void 0;
22193    var flip = function flip(fn) {
22194      return function (a, b) {
22195        return fn(b, a);
22196      };
22197    };
22198    var finder = function finder(_ref2, source) {
22199      var techName = _ref2[0],
22200          tech = _ref2[1];
22201
22202      if (tech.canPlaySource(source, _this5.options_[techName.toLowerCase()])) {
22203        return { source: source, tech: techName };
22204      }
22205    };
22206
22207    // Depending on the truthiness of `options.sourceOrder`, we swap the order of techs and sources
22208    // to select from them based on their priority.
22209    if (this.options_.sourceOrder) {
22210      // Source-first ordering
22211      foundSourceAndTech = findFirstPassingTechSourcePair(sources, techs, flip(finder));
22212    } else {
22213      // Tech-first ordering
22214      foundSourceAndTech = findFirstPassingTechSourcePair(techs, sources, finder);
22215    }
22216
22217    return foundSourceAndTech || false;
22218  };
22219
22220  /**
22221   * Get or set the video source.
22222   *
22223   * @param {Tech~SourceObject|Tech~SourceObject[]|string} [source]
22224   *        A SourceObject, an array of SourceObjects, or a string referencing
22225   *        a URL to a media source. It is _highly recommended_ that an object
22226   *        or array of objects is used here, so that source selection
22227   *        algorithms can take the `type` into account.
22228   *
22229   *        If not provided, this method acts as a getter.
22230   *
22231   * @return {string|undefined}
22232   *         If the `source` argument is missing, returns the current source
22233   *         URL. Otherwise, returns nothing/undefined.
22234   */
22235
22236
22237  Player.prototype.src = function src(source) {
22238    var _this6 = this;
22239
22240    // getter usage
22241    if (typeof source === 'undefined') {
22242      return this.cache_.src;
22243    }
22244    // filter out invalid sources and turn our source into
22245    // an array of source objects
22246    var sources = filterSource(source);
22247
22248    // if a source was passed in then it is invalid because
22249    // it was filtered to a zero length Array. So we have to
22250    // show an error
22251    if (!sources.length) {
22252      this.setTimeout(function () {
22253        this.error({ code: 4, message: this.localize(this.options_.notSupportedMessage) });
22254      }, 0);
22255      return;
22256    }
22257
22258    // intial sources
22259    this.cache_.sources = sources;
22260    this.changingSrc_ = true;
22261
22262    // intial source
22263    this.cache_.source = sources[0];
22264
22265    // middlewareSource is the source after it has been changed by middleware
22266    setSource(this, sources[0], function (middlewareSource, mws) {
22267      _this6.middleware_ = mws;
22268
22269      var err = _this6.src_(middlewareSource);
22270
22271      if (err) {
22272        if (sources.length > 1) {
22273          return _this6.src(sources.slice(1));
22274        }
22275
22276        // We need to wrap this in a timeout to give folks a chance to add error event handlers
22277        _this6.setTimeout(function () {
22278          this.error({ code: 4, message: this.localize(this.options_.notSupportedMessage) });
22279        }, 0);
22280
22281        // we could not find an appropriate tech, but let's still notify the delegate that this is it
22282        // this needs a better comment about why this is needed
22283        _this6.triggerReady();
22284
22285        return;
22286      }
22287
22288      _this6.changingSrc_ = false;
22289      // video element listed source
22290      _this6.cache_.src = middlewareSource.src;
22291
22292      setTech(mws, _this6.tech_);
22293    });
22294  };
22295
22296  /**
22297   * Set the source object on the tech, returns a boolean that indicates wether
22298   * there is a tech that can play the source or not
22299   *
22300   * @param {Tech~SourceObject} source
22301   *        The source object to set on the Tech
22302   *
22303   * @return {Boolean}
22304   *         - True if there is no Tech to playback this source
22305   *         - False otherwise
22306   *
22307   * @private
22308   */
22309
22310
22311  Player.prototype.src_ = function src_(source) {
22312    var sourceTech = this.selectSource([source]);
22313
22314    if (!sourceTech) {
22315      return true;
22316    }
22317
22318    if (!titleCaseEquals(sourceTech.tech, this.techName_)) {
22319      this.changingSrc_ = true;
22320
22321      // load this technology with the chosen source
22322      this.loadTech_(sourceTech.tech, sourceTech.source);
22323      return false;
22324    }
22325
22326    // wait until the tech is ready to set the source
22327    this.ready(function () {
22328
22329      // The setSource tech method was added with source handlers
22330      // so older techs won't support it
22331      // We need to check the direct prototype for the case where subclasses
22332      // of the tech do not support source handlers
22333      if (this.tech_.constructor.prototype.hasOwnProperty('setSource')) {
22334        this.techCall_('setSource', source);
22335      } else {
22336        this.techCall_('src', source.src);
22337      }
22338
22339      if (this.options_.preload === 'auto') {
22340        this.load();
22341      }
22342
22343      // Set the source synchronously if possible (#2326)
22344    }, true);
22345
22346    return false;
22347  };
22348
22349  /**
22350   * Begin loading the src data.
22351   */
22352
22353
22354  Player.prototype.load = function load() {
22355    this.techCall_('load');
22356  };
22357
22358  /**
22359   * Reset the player. Loads the first tech in the techOrder,
22360   * and calls `reset` on the tech`.
22361   */
22362
22363
22364  Player.prototype.reset = function reset() {
22365    this.loadTech_(this.options_.techOrder[0], null);
22366    this.techCall_('reset');
22367  };
22368
22369  /**
22370   * Returns all of the current source objects.
22371   *
22372   * @return {Tech~SourceObject[]}
22373   *         The current source objects
22374   */
22375
22376
22377  Player.prototype.currentSources = function currentSources() {
22378    var source = this.currentSource();
22379    var sources = [];
22380
22381    // assume `{}` or `{ src }`
22382    if (Object.keys(source).length !== 0) {
22383      sources.push(source);
22384    }
22385
22386    return this.cache_.sources || sources;
22387  };
22388
22389  /**
22390   * Returns the current source object.
22391   *
22392   * @return {Tech~SourceObject}
22393   *         The current source object
22394   */
22395
22396
22397  Player.prototype.currentSource = function currentSource() {
22398    return this.cache_.source || {};
22399  };
22400
22401  /**
22402   * Returns the fully qualified URL of the current source value e.g. http://mysite.com/video.mp4
22403   * Can be used in conjuction with `currentType` to assist in rebuilding the current source object.
22404   *
22405   * @return {string}
22406   *         The current source
22407   */
22408
22409
22410  Player.prototype.currentSrc = function currentSrc() {
22411    return this.currentSource() && this.currentSource().src || '';
22412  };
22413
22414  /**
22415   * Get the current source type e.g. video/mp4
22416   * This can allow you rebuild the current source object so that you could load the same
22417   * source and tech later
22418   *
22419   * @return {string}
22420   *         The source MIME type
22421   */
22422
22423
22424  Player.prototype.currentType = function currentType() {
22425    return this.currentSource() && this.currentSource().type || '';
22426  };
22427
22428  /**
22429   * Get or set the preload attribute
22430   *
22431   * @param {boolean} [value]
22432   *        - true means that we should preload
22433   *        - false maens that we should not preload
22434   *
22435   * @return {string}
22436   *         The preload attribute value when getting
22437   */
22438
22439
22440  Player.prototype.preload = function preload(value) {
22441    if (value !== undefined) {
22442      this.techCall_('setPreload', value);
22443      this.options_.preload = value;
22444      return;
22445    }
22446    return this.techGet_('preload');
22447  };
22448
22449  /**
22450   * Get or set the autoplay attribute.
22451   *
22452   * @param {boolean} [value]
22453   *        - true means that we should autoplay
22454   *        - false means that we should not autoplay
22455   *
22456   * @return {string}
22457   *         The current value of autoplay when getting
22458   */
22459
22460
22461  Player.prototype.autoplay = function autoplay(value) {
22462    if (value !== undefined) {
22463      this.techCall_('setAutoplay', value);
22464      this.options_.autoplay = value;
22465      return;
22466    }
22467    return this.techGet_('autoplay', value);
22468  };
22469
22470  /**
22471   * Set or unset the playsinline attribute.
22472   * Playsinline tells the browser that non-fullscreen playback is preferred.
22473   *
22474   * @param {boolean} [value]
22475   *        - true means that we should try to play inline by default
22476   *        - false means that we should use the browser's default playback mode,
22477   *          which in most cases is inline. iOS Safari is a notable exception
22478   *          and plays fullscreen by default.
22479   *
22480   * @return {string|Player}
22481   *         - the current value of playsinline
22482   *         - the player when setting
22483   *
22484   * @see [Spec]{@link https://html.spec.whatwg.org/#attr-video-playsinline}
22485   */
22486
22487
22488  Player.prototype.playsinline = function playsinline(value) {
22489    if (value !== undefined) {
22490      this.techCall_('setPlaysinline', value);
22491      this.options_.playsinline = value;
22492      return this;
22493    }
22494    return this.techGet_('playsinline');
22495  };
22496
22497  /**
22498   * Get or set the loop attribute on the video element.
22499   *
22500   * @param {boolean} [value]
22501   *        - true means that we should loop the video
22502   *        - false means that we should not loop the video
22503   *
22504   * @return {string}
22505   *         The current value of loop when getting
22506   */
22507
22508
22509  Player.prototype.loop = function loop(value) {
22510    if (value !== undefined) {
22511      this.techCall_('setLoop', value);
22512      this.options_.loop = value;
22513      return;
22514    }
22515    return this.techGet_('loop');
22516  };
22517
22518  /**
22519   * Get or set the poster image source url
22520   *
22521   * @fires Player#posterchange
22522   *
22523   * @param {string} [src]
22524   *        Poster image source URL
22525   *
22526   * @return {string}
22527   *         The current value of poster when getting
22528   */
22529
22530
22531  Player.prototype.poster = function poster(src) {
22532    if (src === undefined) {
22533      return this.poster_;
22534    }
22535
22536    // The correct way to remove a poster is to set as an empty string
22537    // other falsey values will throw errors
22538    if (!src) {
22539      src = '';
22540    }
22541
22542    // update the internal poster variable
22543    this.poster_ = src;
22544
22545    // update the tech's poster
22546    this.techCall_('setPoster', src);
22547
22548    // alert components that the poster has been set
22549    /**
22550     * This event fires when the poster image is changed on the player.
22551     *
22552     * @event Player#posterchange
22553     * @type {EventTarget~Event}
22554     */
22555    this.trigger('posterchange');
22556  };
22557
22558  /**
22559   * Some techs (e.g. YouTube) can provide a poster source in an
22560   * asynchronous way. We want the poster component to use this
22561   * poster source so that it covers up the tech's controls.
22562   * (YouTube's play button). However we only want to use this
22563   * source if the player user hasn't set a poster through
22564   * the normal APIs.
22565   *
22566   * @fires Player#posterchange
22567   * @listens Tech#posterchange
22568   * @private
22569   */
22570
22571
22572  Player.prototype.handleTechPosterChange_ = function handleTechPosterChange_() {
22573    if (!this.poster_ && this.tech_ && this.tech_.poster) {
22574      this.poster_ = this.tech_.poster() || '';
22575
22576      // Let components know the poster has changed
22577      this.trigger('posterchange');
22578    }
22579  };
22580
22581  /**
22582   * Get or set whether or not the controls are showing.
22583   *
22584   * @fires Player#controlsenabled
22585   *
22586   * @param {boolean} [bool]
22587   *        - true to turn controls on
22588   *        - false to turn controls off
22589   *
22590   * @return {boolean}
22591   *         The current value of controls when getting
22592   */
22593
22594
22595  Player.prototype.controls = function controls(bool) {
22596    if (bool !== undefined) {
22597      bool = !!bool;
22598
22599      // Don't trigger a change event unless it actually changed
22600      if (this.controls_ !== bool) {
22601        this.controls_ = bool;
22602
22603        if (this.usingNativeControls()) {
22604          this.techCall_('setControls', bool);
22605        }
22606
22607        if (bool) {
22608          this.removeClass('vjs-controls-disabled');
22609          this.addClass('vjs-controls-enabled');
22610          /**
22611           * @event Player#controlsenabled
22612           * @type {EventTarget~Event}
22613           */
22614          this.trigger('controlsenabled');
22615
22616          if (!this.usingNativeControls()) {
22617            this.addTechControlsListeners_();
22618          }
22619        } else {
22620          this.removeClass('vjs-controls-enabled');
22621          this.addClass('vjs-controls-disabled');
22622          /**
22623           * @event Player#controlsdisabled
22624           * @type {EventTarget~Event}
22625           */
22626          this.trigger('controlsdisabled');
22627
22628          if (!this.usingNativeControls()) {
22629            this.removeTechControlsListeners_();
22630          }
22631        }
22632      }
22633      return;
22634    }
22635    return !!this.controls_;
22636  };
22637
22638  /**
22639   * Toggle native controls on/off. Native controls are the controls built into
22640   * devices (e.g. default iPhone controls), Flash, or other techs
22641   * (e.g. Vimeo Controls)
22642   * **This should only be set by the current tech, because only the tech knows
22643   * if it can support native controls**
22644   *
22645   * @fires Player#usingnativecontrols
22646   * @fires Player#usingcustomcontrols
22647   *
22648   * @param {boolean} [bool]
22649   *        - true to turn native controls on
22650   *        - false to turn native controls off
22651   *
22652   * @return {boolean}
22653   *         The current value of native controls when getting
22654   */
22655
22656
22657  Player.prototype.usingNativeControls = function usingNativeControls(bool) {
22658    if (bool !== undefined) {
22659      bool = !!bool;
22660
22661      // Don't trigger a change event unless it actually changed
22662      if (this.usingNativeControls_ !== bool) {
22663        this.usingNativeControls_ = bool;
22664        if (bool) {
22665          this.addClass('vjs-using-native-controls');
22666
22667          /**
22668           * player is using the native device controls
22669           *
22670           * @event Player#usingnativecontrols
22671           * @type {EventTarget~Event}
22672           */
22673          this.trigger('usingnativecontrols');
22674        } else {
22675          this.removeClass('vjs-using-native-controls');
22676
22677          /**
22678           * player is using the custom HTML controls
22679           *
22680           * @event Player#usingcustomcontrols
22681           * @type {EventTarget~Event}
22682           */
22683          this.trigger('usingcustomcontrols');
22684        }
22685      }
22686      return;
22687    }
22688    return !!this.usingNativeControls_;
22689  };
22690
22691  /**
22692   * Set or get the current MediaError
22693   *
22694   * @fires Player#error
22695   *
22696   * @param  {MediaError|string|number} [err]
22697   *         A MediaError or a string/number to be turned
22698   *         into a MediaError
22699   *
22700   * @return {MediaError|null}
22701   *         The current MediaError when getting (or null)
22702   */
22703
22704
22705  Player.prototype.error = function error(err) {
22706    if (err === undefined) {
22707      return this.error_ || null;
22708    }
22709
22710    // restoring to default
22711    if (err === null) {
22712      this.error_ = err;
22713      this.removeClass('vjs-error');
22714      if (this.errorDisplay) {
22715        this.errorDisplay.close();
22716      }
22717      return;
22718    }
22719
22720    this.error_ = new MediaError(err);
22721
22722    // add the vjs-error classname to the player
22723    this.addClass('vjs-error');
22724
22725    // log the name of the error type and any message
22726    // ie8 just logs "[object object]" if you just log the error object
22727    log$1.error('(CODE:' + this.error_.code + ' ' + MediaError.errorTypes[this.error_.code] + ')', this.error_.message, this.error_);
22728
22729    /**
22730     * @event Player#error
22731     * @type {EventTarget~Event}
22732     */
22733    this.trigger('error');
22734
22735    return;
22736  };
22737
22738  /**
22739   * Report user activity
22740   *
22741   * @param {Object} event
22742   *        Event object
22743   */
22744
22745
22746  Player.prototype.reportUserActivity = function reportUserActivity(event) {
22747    this.userActivity_ = true;
22748  };
22749
22750  /**
22751   * Get/set if user is active
22752   *
22753   * @fires Player#useractive
22754   * @fires Player#userinactive
22755   *
22756   * @param {boolean} [bool]
22757   *        - true if the user is active
22758   *        - false if the user is inactive
22759   *
22760   * @return {boolean}
22761   *         The current value of userActive when getting
22762   */
22763
22764
22765  Player.prototype.userActive = function userActive(bool) {
22766    if (bool !== undefined) {
22767      bool = !!bool;
22768      if (bool !== this.userActive_) {
22769        this.userActive_ = bool;
22770        if (bool) {
22771          // If the user was inactive and is now active we want to reset the
22772          // inactivity timer
22773          this.userActivity_ = true;
22774          this.removeClass('vjs-user-inactive');
22775          this.addClass('vjs-user-active');
22776          /**
22777           * @event Player#useractive
22778           * @type {EventTarget~Event}
22779           */
22780          this.trigger('useractive');
22781        } else {
22782          // We're switching the state to inactive manually, so erase any other
22783          // activity
22784          this.userActivity_ = false;
vendor: 12,911 bytes, lines 22785-23204
22785
22786          // Chrome/Safari/IE have bugs where when you change the cursor it can
22787          // trigger a mousemove event. This causes an issue when you're hiding
22788          // the cursor when the user is inactive, and a mousemove signals user
22789          // activity. Making it impossible to go into inactive mode. Specifically
22790          // this happens in fullscreen when we really need to hide the cursor.
22791          //
22792          // When this gets resolved in ALL browsers it can be removed
22793          // https://code.google.com/p/chromium/issues/detail?id=103041
22794          if (this.tech_) {
22795            this.tech_.one('mousemove', function (e) {
22796              e.stopPropagation();
22797              e.preventDefault();
22798            });
22799          }
22800
22801          this.removeClass('vjs-user-active');
22802          this.addClass('vjs-user-inactive');
22803          /**
22804           * @event Player#userinactive
22805           * @type {EventTarget~Event}
22806           */
22807          this.trigger('userinactive');
22808        }
22809      }
22810      return;
22811    }
22812    return this.userActive_;
22813  };
22814
22815  /**
22816   * Listen for user activity based on timeout value
22817   *
22818   * @private
22819   */
22820
22821
22822  Player.prototype.listenForUserActivity_ = function listenForUserActivity_() {
22823    var mouseInProgress = void 0;
22824    var lastMoveX = void 0;
22825    var lastMoveY = void 0;
22826    var handleActivity = bind(this, this.reportUserActivity);
22827
22828    var handleMouseMove = function handleMouseMove(e) {
22829      // #1068 - Prevent mousemove spamming
22830      // Chrome Bug: https://code.google.com/p/chromium/issues/detail?id=366970
22831      if (e.screenX !== lastMoveX || e.screenY !== lastMoveY) {
22832        lastMoveX = e.screenX;
22833        lastMoveY = e.screenY;
22834        handleActivity();
22835      }
22836    };
22837
22838    var handleMouseDown = function handleMouseDown() {
22839      handleActivity();
22840      // For as long as the they are touching the device or have their mouse down,
22841      // we consider them active even if they're not moving their finger or mouse.
22842      // So we want to continue to update that they are active
22843      this.clearInterval(mouseInProgress);
22844      // Setting userActivity=true now and setting the interval to the same time
22845      // as the activityCheck interval (250) should ensure we never miss the
22846      // next activityCheck
22847      mouseInProgress = this.setInterval(handleActivity, 250);
22848    };
22849
22850    var handleMouseUp = function handleMouseUp(event) {
22851      handleActivity();
22852      // Stop the interval that maintains activity if the mouse/touch is down
22853      this.clearInterval(mouseInProgress);
22854    };
22855
22856    // Any mouse movement will be considered user activity
22857    this.on('mousedown', handleMouseDown);
22858    this.on('mousemove', handleMouseMove);
22859    this.on('mouseup', handleMouseUp);
22860
22861    // Listen for keyboard navigation
22862    // Shouldn't need to use inProgress interval because of key repeat
22863    this.on('keydown', handleActivity);
22864    this.on('keyup', handleActivity);
22865
22866    // Run an interval every 250 milliseconds instead of stuffing everything into
22867    // the mousemove/touchmove function itself, to prevent performance degradation.
22868    // `this.reportUserActivity` simply sets this.userActivity_ to true, which
22869    // then gets picked up by this loop
22870    // http://ejohn.org/blog/learning-from-twitter/
22871    var inactivityTimeout = void 0;
22872
22873    this.setInterval(function () {
22874      // Check to see if mouse/touch activity has happened
22875      if (this.userActivity_) {
22876        // Reset the activity tracker
22877        this.userActivity_ = false;
22878
22879        // If the user state was inactive, set the state to active
22880        this.userActive(true);
22881
22882        // Clear any existing inactivity timeout to start the timer over
22883        this.clearTimeout(inactivityTimeout);
22884
22885        var timeout = this.options_.inactivityTimeout;
22886
22887        if (timeout > 0) {
22888          // In <timeout> milliseconds, if no more activity has occurred the
22889          // user will be considered inactive
22890          inactivityTimeout = this.setTimeout(function () {
22891            // Protect against the case where the inactivityTimeout can trigger just
22892            // before the next user activity is picked up by the activity check loop
22893            // causing a flicker
22894            if (!this.userActivity_) {
22895              this.userActive(false);
22896            }
22897          }, timeout);
22898        }
22899      }
22900    }, 250);
22901  };
22902
22903  /**
22904   * Gets or sets the current playback rate. A playback rate of
22905   * 1.0 represents normal speed and 0.5 would indicate half-speed
22906   * playback, for instance.
22907   *
22908   * @see https://html.spec.whatwg.org/multipage/embedded-content.html#dom-media-playbackrate
22909   *
22910   * @param {number} [rate]
22911   *       New playback rate to set.
22912   *
22913   * @return {number}
22914   *         The current playback rate when getting or 1.0
22915   */
22916
22917
22918  Player.prototype.playbackRate = function playbackRate(rate) {
22919    if (rate !== undefined) {
22920      this.techCall_('setPlaybackRate', rate);
22921      return;
22922    }
22923
22924    if (this.tech_ && this.tech_.featuresPlaybackRate) {
22925      return this.techGet_('playbackRate');
22926    }
22927    return 1.0;
22928  };
22929
22930  /**
22931   * Gets or sets the current default playback rate. A default playback rate of
22932   * 1.0 represents normal speed and 0.5 would indicate half-speed playback, for instance.
22933   * defaultPlaybackRate will only represent what the intial playbackRate of a video was, not
22934   * not the current playbackRate.
22935   *
22936   * @see https://html.spec.whatwg.org/multipage/embedded-content.html#dom-media-defaultplaybackrate
22937   *
22938   * @param {number} [rate]
22939   *       New default playback rate to set.
22940   *
22941   * @return {number|Player}
22942   *         - The default playback rate when getting or 1.0
22943   *         - the player when setting
22944   */
22945
22946
22947  Player.prototype.defaultPlaybackRate = function defaultPlaybackRate(rate) {
22948    if (rate !== undefined) {
22949      return this.techCall_('setDefaultPlaybackRate', rate);
22950    }
22951
22952    if (this.tech_ && this.tech_.featuresPlaybackRate) {
22953      return this.techGet_('defaultPlaybackRate');
22954    }
22955    return 1.0;
22956  };
22957
22958  /**
22959   * Gets or sets the audio flag
22960   *
22961   * @param {boolean} bool
22962   *        - true signals that this is an audio player
22963   *        - false signals that this is not an audio player
22964   *
22965   * @return {boolean}
22966   *         The current value of isAudio when getting
22967   */
22968
22969
22970  Player.prototype.isAudio = function isAudio(bool) {
22971    if (bool !== undefined) {
22972      this.isAudio_ = !!bool;
22973      return;
22974    }
22975
22976    return !!this.isAudio_;
22977  };
22978
22979  /**
22980   * A helper method for adding a {@link TextTrack} to our
22981   * {@link TextTrackList}.
22982   *
22983   * In addition to the W3C settings we allow adding additional info through options.
22984   *
22985   * @see http://www.w3.org/html/wg/drafts/html/master/embedded-content-0.html#dom-media-addtexttrack
22986   *
22987   * @param {string} [kind]
22988   *        the kind of TextTrack you are adding
22989   *
22990   * @param {string} [label]
22991   *        the label to give the TextTrack label
22992   *
22993   * @param {string} [language]
22994   *        the language to set on the TextTrack
22995   *
22996   * @return {TextTrack|undefined}
22997   *         the TextTrack that was added or undefined
22998   *         if there is no tech
22999   */
23000
23001
23002  Player.prototype.addTextTrack = function addTextTrack(kind, label, language) {
23003    if (this.tech_) {
23004      return this.tech_.addTextTrack(kind, label, language);
23005    }
23006  };
23007
23008  /**
23009   * Create a remote {@link TextTrack} and an {@link HTMLTrackElement}. It will
23010   * automatically removed from the video element whenever the source changes, unless
23011   * manualCleanup is set to false.
23012   *
23013   * @param {Object} options
23014   *        Options to pass to {@link HTMLTrackElement} during creation. See
23015   *        {@link HTMLTrackElement} for object properties that you should use.
23016   *
23017   * @param {boolean} [manualCleanup=true] if set to false, the TextTrack will be
23018   *
23019   * @return {HtmlTrackElement}
23020   *         the HTMLTrackElement that was created and added
23021   *         to the HtmlTrackElementList and the remote
23022   *         TextTrackList
23023   *
23024   * @deprecated The default value of the "manualCleanup" parameter will default
23025   *             to "false" in upcoming versions of Video.js
23026   */
23027
23028
23029  Player.prototype.addRemoteTextTrack = function addRemoteTextTrack(options, manualCleanup) {
23030    if (this.tech_) {
23031      return this.tech_.addRemoteTextTrack(options, manualCleanup);
23032    }
23033  };
23034
23035  /**
23036   * Remove a remote {@link TextTrack} from the respective
23037   * {@link TextTrackList} and {@link HtmlTrackElementList}.
23038   *
23039   * @param {Object} track
23040   *        Remote {@link TextTrack} to remove
23041   *
23042   * @return {undefined}
23043   *         does not return anything
23044   */
23045
23046
23047  Player.prototype.removeRemoteTextTrack = function removeRemoteTextTrack() {
23048    var _ref3 = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {},
23049        _ref3$track = _ref3.track,
23050        track = _ref3$track === undefined ? arguments[0] : _ref3$track;
23051
23052    // destructure the input into an object with a track argument, defaulting to arguments[0]
23053    // default the whole argument to an empty object if nothing was passed in
23054
23055    if (this.tech_) {
23056      return this.tech_.removeRemoteTextTrack(track);
23057    }
23058  };
23059
23060  /**
23061   * Gets available media playback quality metrics as specified by the W3C's Media
23062   * Playback Quality API.
23063   *
23064   * @see [Spec]{@link https://wicg.github.io/media-playback-quality}
23065   *
23066   * @return {Object|undefined}
23067   *         An object with supported media playback quality metrics or undefined if there
23068   *         is no tech or the tech does not support it.
23069   */
23070
23071
23072  Player.prototype.getVideoPlaybackQuality = function getVideoPlaybackQuality() {
23073    return this.techGet_('getVideoPlaybackQuality');
23074  };
23075
23076  /**
23077   * Get video width
23078   *
23079   * @return {number}
23080   *         current video width
23081   */
23082
23083
23084  Player.prototype.videoWidth = function videoWidth() {
23085    return this.tech_ && this.tech_.videoWidth && this.tech_.videoWidth() || 0;
23086  };
23087
23088  /**
23089   * Get video height
23090   *
23091   * @return {number}
23092   *         current video height
23093   */
23094
23095
23096  Player.prototype.videoHeight = function videoHeight() {
23097    return this.tech_ && this.tech_.videoHeight && this.tech_.videoHeight() || 0;
23098  };
23099
23100  /**
23101   * The player's language code
23102   * NOTE: The language should be set in the player options if you want the
23103   * the controls to be built with a specific language. Changing the lanugage
23104   * later will not update controls text.
23105   *
23106   * @param {string} [code]
23107   *        the language code to set the player to
23108   *
23109   * @return {string}
23110   *         The current language code when getting
23111   */
23112
23113
23114  Player.prototype.language = function language(code) {
23115    if (code === undefined) {
23116      return this.language_;
23117    }
23118
23119    this.language_ = String(code).toLowerCase();
23120  };
23121
23122  /**
23123   * Get the player's language dictionary
23124   * Merge every time, because a newly added plugin might call videojs.addLanguage() at any time
23125   * Languages specified directly in the player options have precedence
23126   *
23127   * @return {Array}
23128   *         An array of of supported languages
23129   */
23130
23131
23132  Player.prototype.languages = function languages() {
23133    return mergeOptions(Player.prototype.options_.languages, this.languages_);
23134  };
23135
23136  /**
23137   * returns a JavaScript object reperesenting the current track
23138   * information. **DOES not return it as JSON**
23139   *
23140   * @return {Object}
23141   *         Object representing the current of track info
23142   */
23143
23144
23145  Player.prototype.toJSON = function toJSON() {
23146    var options = mergeOptions(this.options_);
23147    var tracks = options.tracks;
23148
23149    options.tracks = [];
23150
23151    for (var i = 0; i < tracks.length; i++) {
23152      var track = tracks[i];
23153
23154      // deep merge tracks and null out player so no circular references
23155      track = mergeOptions(track);
23156      track.player = undefined;
23157      options.tracks[i] = track;
23158    }
23159
23160    return options;
23161  };
23162
23163  /**
23164   * Creates a simple modal dialog (an instance of the {@link ModalDialog}
23165   * component) that immediately overlays the player with arbitrary
23166   * content and removes itself when closed.
23167   *
23168   * @param {string|Function|Element|Array|null} content
23169   *        Same as {@link ModalDialog#content}'s param of the same name.
23170   *        The most straight-forward usage is to provide a string or DOM
23171   *        element.
23172   *
23173   * @param {Object} [options]
23174   *        Extra options which will be passed on to the {@link ModalDialog}.
23175   *
23176   * @return {ModalDialog}
23177   *         the {@link ModalDialog} that was created
23178   */
23179
23180
23181  Player.prototype.createModal = function createModal(content, options) {
23182    var _this7 = this;
23183
23184    options = options || {};
23185    options.content = content || '';
23186
23187    var modal = new ModalDialog(this, options);
23188
23189    this.addChild(modal);
23190    modal.on('dispose', function () {
23191      _this7.removeChild(modal);
23192    });
23193
23194    modal.open();
23195    return modal;
23196  };
23197
23198  /**
23199   * Gets tag settings
23200   *
23201   * @param {Element} tag
23202   *        The player tag
23203   *
23204   * @return {Object}
vendor: 4,271 bytes, lines 23205-23375
23205   *         An object containing all of the settings
23206   *         for a player tag
23207   */
23208
23209
23210  Player.getTagSettings = function getTagSettings(tag) {
23211    var baseOptions = {
23212      sources: [],
23213      tracks: []
23214    };
23215
23216    var tagOptions = getAttributes(tag);
23217    var dataSetup = tagOptions['data-setup'];
23218
23219    if (hasClass(tag, 'vjs-fluid')) {
23220      tagOptions.fluid = true;
23221    }
23222
23223    // Check if data-setup attr exists.
23224    if (dataSetup !== null) {
23225      // Parse options JSON
23226      // If empty string, make it a parsable json object.
23227      var _safeParseTuple = tuple(dataSetup || '{}'),
23228          err = _safeParseTuple[0],
23229          data = _safeParseTuple[1];
23230
23231      if (err) {
23232        log$1.error(err);
23233      }
23234      assign(tagOptions, data);
23235    }
23236
23237    assign(baseOptions, tagOptions);
23238
23239    // Get tag children settings
23240    if (tag.hasChildNodes()) {
23241      var children = tag.childNodes;
23242
23243      for (var i = 0, j = children.length; i < j; i++) {
23244        var child = children[i];
23245        // Change case needed: http://ejohn.org/blog/nodename-case-sensitivity/
23246        var childName = child.nodeName.toLowerCase();
23247
23248        if (childName === 'source') {
23249          baseOptions.sources.push(getAttributes(child));
23250        } else if (childName === 'track') {
23251          baseOptions.tracks.push(getAttributes(child));
23252        }
23253      }
23254    }
23255
23256    return baseOptions;
23257  };
23258
23259  /**
23260   * Determine wether or not flexbox is supported
23261   *
23262   * @return {boolean}
23263   *         - true if flexbox is supported
23264   *         - false if flexbox is not supported
23265   */
23266
23267
23268  Player.prototype.flexNotSupported_ = function flexNotSupported_() {
23269    var elem = document_1.createElement('i');
23270
23271    // Note: We don't actually use flexBasis (or flexOrder), but it's one of the more
23272    // common flex features that we can rely on when checking for flex support.
23273    return !('flexBasis' in elem.style || 'webkitFlexBasis' in elem.style || 'mozFlexBasis' in elem.style || 'msFlexBasis' in elem.style ||
23274    // IE10-specific (2012 flex spec)
23275    'msFlexOrder' in elem.style);
23276  };
23277
23278  return Player;
23279}(Component);
23280
23281/**
23282 * Get the {@link VideoTrackList}
23283 * @link https://html.spec.whatwg.org/multipage/embedded-content.html#videotracklist
23284 *
23285 * @return {VideoTrackList}
23286 *         the current video track list
23287 *
23288 * @method Player.prototype.videoTracks
23289 */
23290
23291/**
23292 * Get the {@link AudioTrackList}
23293 * @link https://html.spec.whatwg.org/multipage/embedded-content.html#audiotracklist
23294 *
23295 * @return {AudioTrackList}
23296 *         the current audio track list
23297 *
23298 * @method Player.prototype.audioTracks
23299 */
23300
23301/**
23302 * Get the {@link TextTrackList}
23303 *
23304 * @link http://www.w3.org/html/wg/drafts/html/master/embedded-content-0.html#dom-media-texttracks
23305 *
23306 * @return {TextTrackList}
23307 *         the current text track list
23308 *
23309 * @method Player.prototype.textTracks
23310 */
23311
23312/**
23313 * Get the remote {@link TextTrackList}
23314 *
23315 * @return {TextTrackList}
23316 *         The current remote text track list
23317 *
23318 * @method Player.prototype.remoteTextTracks
23319 */
23320
23321/**
23322 * Get the remote {@link HtmlTrackElementList} tracks.
23323 *
23324 * @return {HtmlTrackElementList}
23325 *         The current remote text track element list
23326 *
23327 * @method Player.prototype.remoteTextTrackEls
23328 */
23329
23330ALL.names.forEach(function (name$$1) {
23331  var props = ALL[name$$1];
23332
23333  Player.prototype[props.getterName] = function () {
23334    if (this.tech_) {
23335      return this.tech_[props.getterName]();
23336    }
23337
23338    // if we have not yet loadTech_, we create {video,audio,text}Tracks_
23339    // these will be passed to the tech during loading
23340    this[props.privateName] = this[props.privateName] || new props.ListClass();
23341    return this[props.privateName];
23342  };
23343});
23344
23345/**
23346 * Global player list
23347 *
23348 * @type {Object}
23349 */
23350Player.players = {};
23351
23352var navigator$1 = window_1.navigator;
23353
23354/*
23355 * Player instance options, surfaced using options
23356 * options = Player.prototype.options_
23357 * Make changes in options, not here.
23358 *
23359 * @type {Object}
23360 * @private
23361 */
23362Player.prototype.options_ = {
23363  // Default order of fallback technology
23364  techOrder: Tech.defaultTechOrder_,
23365
23366  html5: {},
23367  flash: {},
23368
23369  // default inactivity timeout
23370  inactivityTimeout: 2000,
23371
23372  // default playback rates
23373  playbackRates: [],
23374  // Add playback rate selection by adding rates
23375  // 'playbackRates': [0.5, 1, 1.5, 2],
vendor: 10,750 bytes, lines 23376-23753
23376
23377  // Included control sets
23378  children: ['mediaLoader', 'posterImage', 'textTrackDisplay', 'loadingSpinner', 'bigPlayButton', 'controlBar', 'errorDisplay', 'textTrackSettings'],
23379
23380  language: navigator$1 && (navigator$1.languages && navigator$1.languages[0] || navigator$1.userLanguage || navigator$1.language) || 'en',
23381
23382  // locales and their language translations
23383  languages: {},
23384
23385  // Default message to show when a video cannot be played.
23386  notSupportedMessage: 'No compatible source was found for this media.'
23387};
23388
23389[
23390/**
23391 * Returns whether or not the player is in the "ended" state.
23392 *
23393 * @return {Boolean} True if the player is in the ended state, false if not.
23394 * @method Player#ended
23395 */
23396'ended',
23397/**
23398 * Returns whether or not the player is in the "seeking" state.
23399 *
23400 * @return {Boolean} True if the player is in the seeking state, false if not.
23401 * @method Player#seeking
23402 */
23403'seeking',
23404/**
23405 * Returns the TimeRanges of the media that are currently available
23406 * for seeking to.
23407 *
23408 * @return {TimeRanges} the seekable intervals of the media timeline
23409 * @method Player#seekable
23410 */
23411'seekable',
23412/**
23413 * Returns the current state of network activity for the element, from
23414 * the codes in the list below.
23415 * - NETWORK_EMPTY (numeric value 0)
23416 *   The element has not yet been initialised. All attributes are in
23417 *   their initial states.
23418 * - NETWORK_IDLE (numeric value 1)
23419 *   The element's resource selection algorithm is active and has
23420 *   selected a resource, but it is not actually using the network at
23421 *   this time.
23422 * - NETWORK_LOADING (numeric value 2)
23423 *   The user agent is actively trying to download data.
23424 * - NETWORK_NO_SOURCE (numeric value 3)
23425 *   The element's resource selection algorithm is active, but it has
23426 *   not yet found a resource to use.
23427 *
23428 * @see https://html.spec.whatwg.org/multipage/embedded-content.html#network-states
23429 * @return {number} the current network activity state
23430 * @method Player#networkState
23431 */
23432'networkState',
23433/**
23434 * Returns a value that expresses the current state of the element
23435 * with respect to rendering the current playback position, from the
23436 * codes in the list below.
23437 * - HAVE_NOTHING (numeric value 0)
23438 *   No information regarding the media resource is available.
23439 * - HAVE_METADATA (numeric value 1)
23440 *   Enough of the resource has been obtained that the duration of the
23441 *   resource is available.
23442 * - HAVE_CURRENT_DATA (numeric value 2)
23443 *   Data for the immediate current playback position is available.
23444 * - HAVE_FUTURE_DATA (numeric value 3)
23445 *   Data for the immediate current playback position is available, as
23446 *   well as enough data for the user agent to advance the current
23447 *   playback position in the direction of playback.
23448 * - HAVE_ENOUGH_DATA (numeric value 4)
23449 *   The user agent estimates that enough data is available for
23450 *   playback to proceed uninterrupted.
23451 *
23452 * @see https://html.spec.whatwg.org/multipage/embedded-content.html#dom-media-readystate
23453 * @return {number} the current playback rendering state
23454 * @method Player#readyState
23455 */
23456'readyState'].forEach(function (fn) {
23457  Player.prototype[fn] = function () {
23458    return this.techGet_(fn);
23459  };
23460});
23461
23462TECH_EVENTS_RETRIGGER.forEach(function (event) {
23463  Player.prototype['handleTech' + toTitleCase(event) + '_'] = function () {
23464    return this.trigger(event);
23465  };
23466});
23467
23468/**
23469 * Fired when the player has initial duration and dimension information
23470 *
23471 * @event Player#loadedmetadata
23472 * @type {EventTarget~Event}
23473 */
23474
23475/**
23476 * Fired when the player has downloaded data at the current playback position
23477 *
23478 * @event Player#loadeddata
23479 * @type {EventTarget~Event}
23480 */
23481
23482/**
23483 * Fired when the current playback position has changed *
23484 * During playback this is fired every 15-250 milliseconds, depending on the
23485 * playback technology in use.
23486 *
23487 * @event Player#timeupdate
23488 * @type {EventTarget~Event}
23489 */
23490
23491/**
23492 * Fired when the volume changes
23493 *
23494 * @event Player#volumechange
23495 * @type {EventTarget~Event}
23496 */
23497
23498/**
23499 * Reports whether or not a player has a plugin available.
23500 *
23501 * This does not report whether or not the plugin has ever been initialized
23502 * on this player. For that, [usingPlugin]{@link Player#usingPlugin}.
23503 *
23504 * @method Player#hasPlugin
23505 * @param  {string}  name
23506 *         The name of a plugin.
23507 *
23508 * @return {boolean}
23509 *         Whether or not this player has the requested plugin available.
23510 */
23511
23512/**
23513 * Reports whether or not a player is using a plugin by name.
23514 *
23515 * For basic plugins, this only reports whether the plugin has _ever_ been
23516 * initialized on this player.
23517 *
23518 * @method Player#usingPlugin
23519 * @param  {string} name
23520 *         The name of a plugin.
23521 *
23522 * @return {boolean}
23523 *         Whether or not this player is using the requested plugin.
23524 */
23525
23526Component.registerComponent('Player', Player);
23527
23528/**
23529 * @file plugin.js
23530 */
23531/**
23532 * The base plugin name.
23533 *
23534 * @private
23535 * @constant
23536 * @type {string}
23537 */
23538var BASE_PLUGIN_NAME = 'plugin';
23539
23540/**
23541 * The key on which a player's active plugins cache is stored.
23542 *
23543 * @private
23544 * @constant
23545 * @type     {string}
23546 */
23547var PLUGIN_CACHE_KEY = 'activePlugins_';
23548
23549/**
23550 * Stores registered plugins in a private space.
23551 *
23552 * @private
23553 * @type    {Object}
23554 */
23555var pluginStorage = {};
23556
23557/**
23558 * Reports whether or not a plugin has been registered.
23559 *
23560 * @private
23561 * @param   {string} name
23562 *          The name of a plugin.
23563 *
23564 * @returns {boolean}
23565 *          Whether or not the plugin has been registered.
23566 */
23567var pluginExists = function pluginExists(name) {
23568  return pluginStorage.hasOwnProperty(name);
23569};
23570
23571/**
23572 * Get a single registered plugin by name.
23573 *
23574 * @private
23575 * @param   {string} name
23576 *          The name of a plugin.
23577 *
23578 * @returns {Function|undefined}
23579 *          The plugin (or undefined).
23580 */
23581var getPlugin = function getPlugin(name) {
23582  return pluginExists(name) ? pluginStorage[name] : undefined;
23583};
23584
23585/**
23586 * Marks a plugin as "active" on a player.
23587 *
23588 * Also, ensures that the player has an object for tracking active plugins.
23589 *
23590 * @private
23591 * @param   {Player} player
23592 *          A Video.js player instance.
23593 *
23594 * @param   {string} name
23595 *          The name of a plugin.
23596 */
23597var markPluginAsActive = function markPluginAsActive(player, name) {
23598  player[PLUGIN_CACHE_KEY] = player[PLUGIN_CACHE_KEY] || {};
23599  player[PLUGIN_CACHE_KEY][name] = true;
23600};
23601
23602/**
23603 * Triggers a pair of plugin setup events.
23604 *
23605 * @private
23606 * @param  {Player} player
23607 *         A Video.js player instance.
23608 *
23609 * @param  {Plugin~PluginEventHash} hash
23610 *         A plugin event hash.
23611 *
23612 * @param  {Boolean} [before]
23613 *         If true, prefixes the event name with "before". In other words,
23614 *         use this to trigger "beforepluginsetup" instead of "pluginsetup".
23615 */
23616var triggerSetupEvent = function triggerSetupEvent(player, hash, before) {
23617  var eventName = (before ? 'before' : '') + 'pluginsetup';
23618
23619  player.trigger(eventName, hash);
23620  player.trigger(eventName + ':' + hash.name, hash);
23621};
23622
23623/**
23624 * Takes a basic plugin function and returns a wrapper function which marks
23625 * on the player that the plugin has been activated.
23626 *
23627 * @private
23628 * @param   {string} name
23629 *          The name of the plugin.
23630 *
23631 * @param   {Function} plugin
23632 *          The basic plugin.
23633 *
23634 * @returns {Function}
23635 *          A wrapper function for the given plugin.
23636 */
23637var createBasicPlugin = function createBasicPlugin(name, plugin) {
23638  var basicPluginWrapper = function basicPluginWrapper() {
23639
23640    // We trigger the "beforepluginsetup" and "pluginsetup" events on the player
23641    // regardless, but we want the hash to be consistent with the hash provided
23642    // for advanced plugins.
23643    //
23644    // The only potentially counter-intuitive thing here is the `instance` in
23645    // the "pluginsetup" event is the value returned by the `plugin` function.
23646    triggerSetupEvent(this, { name: name, plugin: plugin, instance: null }, true);
23647
23648    var instance = plugin.apply(this, arguments);
23649
23650    markPluginAsActive(this, name);
23651    triggerSetupEvent(this, { name: name, plugin: plugin, instance: instance });
23652
23653    return instance;
23654  };
23655
23656  Object.keys(plugin).forEach(function (prop) {
23657    basicPluginWrapper[prop] = plugin[prop];
23658  });
23659
23660  return basicPluginWrapper;
23661};
23662
23663/**
23664 * Takes a plugin sub-class and returns a factory function for generating
23665 * instances of it.
23666 *
23667 * This factory function will replace itself with an instance of the requested
23668 * sub-class of Plugin.
23669 *
23670 * @private
23671 * @param   {string} name
23672 *          The name of the plugin.
23673 *
23674 * @param   {Plugin} PluginSubClass
23675 *          The advanced plugin.
23676 *
23677 * @returns {Function}
23678 */
23679var createPluginFactory = function createPluginFactory(name, PluginSubClass) {
23680
23681  // Add a `name` property to the plugin prototype so that each plugin can
23682  // refer to itself by name.
23683  PluginSubClass.prototype.name = name;
23684
23685  return function () {
23686    triggerSetupEvent(this, { name: name, plugin: PluginSubClass, instance: null }, true);
23687
23688    for (var _len = arguments.length, args = Array(_len), _key = 0; _key < _len; _key++) {
23689      args[_key] = arguments[_key];
23690    }
23691
23692    var instance = new (Function.prototype.bind.apply(PluginSubClass, [null].concat([this].concat(args))))();
23693
23694    // The plugin is replaced by a function that returns the current instance.
23695    this[name] = function () {
23696      return instance;
23697    };
23698
23699    triggerSetupEvent(this, instance.getEventHash());
23700
23701    return instance;
23702  };
23703};
23704
23705/**
23706 * Parent class for all advanced plugins.
23707 *
23708 * @mixes   module:evented~EventedMixin
23709 * @mixes   module:stateful~StatefulMixin
23710 * @fires   Player#beforepluginsetup
23711 * @fires   Player#beforepluginsetup:$name
23712 * @fires   Player#pluginsetup
23713 * @fires   Player#pluginsetup:$name
23714 * @listens Player#dispose
23715 * @throws  {Error}
23716 *          If attempting to instantiate the base {@link Plugin} class
23717 *          directly instead of via a sub-class.
23718 */
23719
23720var Plugin = function () {
23721
23722  /**
23723   * Creates an instance of this class.
23724   *
23725   * Sub-classes should call `super` to ensure plugins are properly initialized.
23726   *
23727   * @param {Player} player
23728   *        A Video.js player instance.
23729   */
23730  function Plugin(player) {
23731    classCallCheck(this, Plugin);
23732
23733    if (this.constructor === Plugin) {
23734      throw new Error('Plugin must be sub-classed; not directly instantiated.');
23735    }
23736
23737    this.player = player;
23738
23739    // Make this object evented, but remove the added `trigger` method so we
23740    // use the prototype version instead.
23741    evented(this);
23742    delete this.trigger;
23743
23744    stateful(this, this.constructor.defaultState);
23745    markPluginAsActive(player, this.name);
23746
23747    // Auto-bind the dispose method so we can use it as a listener and unbind
23748    // it later easily.
23749    this.dispose = bind(this, this.dispose);
23750
23751    // If the player is disposed, dispose the plugin.
23752    player.on('dispose', this.dispose);
23753  }
vendor: 7,496 bytes, lines 23753-24029
23753
23754
23755  /**
23756   * Each event triggered by plugins includes a hash of additional data with
23757   * conventional properties.
23758   *
23759   * This returns that object or mutates an existing hash.
23760   *
23761   * @param   {Object} [hash={}]
23762   *          An object to be used as event an event hash.
23763   *
23764   * @returns {Plugin~PluginEventHash}
23765   *          An event hash object with provided properties mixed-in.
23766   */
23767
23768
23769  Plugin.prototype.getEventHash = function getEventHash() {
23770    var hash = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
23771
23772    hash.name = this.name;
23773    hash.plugin = this.constructor;
23774    hash.instance = this;
23775    return hash;
23776  };
23777
23778  /**
23779   * Triggers an event on the plugin object and overrides
23780   * {@link module:evented~EventedMixin.trigger|EventedMixin.trigger}.
23781   *
23782   * @param   {string|Object} event
23783   *          An event type or an object with a type property.
23784   *
23785   * @param   {Object} [hash={}]
23786   *          Additional data hash to merge with a
23787   *          {@link Plugin~PluginEventHash|PluginEventHash}.
23788   *
23789   * @returns {boolean}
23790   *          Whether or not default was prevented.
23791   */
23792
23793
23794  Plugin.prototype.trigger = function trigger$$1(event) {
23795    var hash = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
23796
23797    return trigger(this.eventBusEl_, event, this.getEventHash(hash));
23798  };
23799
23800  /**
23801   * Handles "statechanged" events on the plugin. No-op by default, override by
23802   * subclassing.
23803   *
23804   * @abstract
23805   * @param    {Event} e
23806   *           An event object provided by a "statechanged" event.
23807   *
23808   * @param    {Object} e.changes
23809   *           An object describing changes that occurred with the "statechanged"
23810   *           event.
23811   */
23812
23813
23814  Plugin.prototype.handleStateChanged = function handleStateChanged(e) {};
23815
23816  /**
23817   * Disposes a plugin.
23818   *
23819   * Subclasses can override this if they want, but for the sake of safety,
23820   * it's probably best to subscribe the "dispose" event.
23821   *
23822   * @fires Plugin#dispose
23823   */
23824
23825
23826  Plugin.prototype.dispose = function dispose() {
23827    var name = this.name,
23828        player = this.player;
23829
23830    /**
23831     * Signals that a advanced plugin is about to be disposed.
23832     *
23833     * @event Plugin#dispose
23834     * @type  {EventTarget~Event}
23835     */
23836
23837    this.trigger('dispose');
23838    this.off();
23839    player.off('dispose', this.dispose);
23840
23841    // Eliminate any possible sources of leaking memory by clearing up
23842    // references between the player and the plugin instance and nulling out
23843    // the plugin's state and replacing methods with a function that throws.
23844    player[PLUGIN_CACHE_KEY][name] = false;
23845    this.player = this.state = null;
23846
23847    // Finally, replace the plugin name on the player with a new factory
23848    // function, so that the plugin is ready to be set up again.
23849    player[name] = createPluginFactory(name, pluginStorage[name]);
23850  };
23851
23852  /**
23853   * Determines if a plugin is a basic plugin (i.e. not a sub-class of `Plugin`).
23854   *
23855   * @param   {string|Function} plugin
23856   *          If a string, matches the name of a plugin. If a function, will be
23857   *          tested directly.
23858   *
23859   * @returns {boolean}
23860   *          Whether or not a plugin is a basic plugin.
23861   */
23862
23863
23864  Plugin.isBasic = function isBasic(plugin) {
23865    var p = typeof plugin === 'string' ? getPlugin(plugin) : plugin;
23866
23867    return typeof p === 'function' && !Plugin.prototype.isPrototypeOf(p.prototype);
23868  };
23869
23870  /**
23871   * Register a Video.js plugin.
23872   *
23873   * @param   {string} name
23874   *          The name of the plugin to be registered. Must be a string and
23875   *          must not match an existing plugin or a method on the `Player`
23876   *          prototype.
23877   *
23878   * @param   {Function} plugin
23879   *          A sub-class of `Plugin` or a function for basic plugins.
23880   *
23881   * @returns {Function}
23882   *          For advanced plugins, a factory function for that plugin. For
23883   *          basic plugins, a wrapper function that initializes the plugin.
23884   */
23885
23886
23887  Plugin.registerPlugin = function registerPlugin(name, plugin) {
23888    if (typeof name !== 'string') {
23889      throw new Error('Illegal plugin name, "' + name + '", must be a string, was ' + (typeof name === 'undefined' ? 'undefined' : _typeof(name)) + '.');
23890    }
23891
23892    if (pluginExists(name)) {
23893      log$1.warn('A plugin named "' + name + '" already exists. You may want to avoid re-registering plugins!');
23894    } else if (Player.prototype.hasOwnProperty(name)) {
23895      throw new Error('Illegal plugin name, "' + name + '", cannot share a name with an existing player method!');
23896    }
23897
23898    if (typeof plugin !== 'function') {
23899      throw new Error('Illegal plugin for "' + name + '", must be a function, was ' + (typeof plugin === 'undefined' ? 'undefined' : _typeof(plugin)) + '.');
23900    }
23901
23902    pluginStorage[name] = plugin;
23903
23904    // Add a player prototype method for all sub-classed plugins (but not for
23905    // the base Plugin class).
23906    if (name !== BASE_PLUGIN_NAME) {
23907      if (Plugin.isBasic(plugin)) {
23908        Player.prototype[name] = createBasicPlugin(name, plugin);
23909      } else {
23910        Player.prototype[name] = createPluginFactory(name, plugin);
23911      }
23912    }
23913
23914    return plugin;
23915  };
23916
23917  /**
23918   * De-register a Video.js plugin.
23919   *
23920   * @param {string} name
23921   *        The name of the plugin to be deregistered.
23922   */
23923
23924
23925  Plugin.deregisterPlugin = function deregisterPlugin(name) {
23926    if (name === BASE_PLUGIN_NAME) {
23927      throw new Error('Cannot de-register base plugin.');
23928    }
23929    if (pluginExists(name)) {
23930      delete pluginStorage[name];
23931      delete Player.prototype[name];
23932    }
23933  };
23934
23935  /**
23936   * Gets an object containing multiple Video.js plugins.
23937   *
23938   * @param   {Array} [names]
23939   *          If provided, should be an array of plugin names. Defaults to _all_
23940   *          plugin names.
23941   *
23942   * @returns {Object|undefined}
23943   *          An object containing plugin(s) associated with their name(s) or
23944   *          `undefined` if no matching plugins exist).
23945   */
23946
23947
23948  Plugin.getPlugins = function getPlugins() {
23949    var names = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : Object.keys(pluginStorage);
23950
23951    var result = void 0;
23952
23953    names.forEach(function (name) {
23954      var plugin = getPlugin(name);
23955
23956      if (plugin) {
23957        result = result || {};
23958        result[name] = plugin;
23959      }
23960    });
23961
23962    return result;
23963  };
23964
23965  /**
23966   * Gets a plugin's version, if available
23967   *
23968   * @param   {string} name
23969   *          The name of a plugin.
23970   *
23971   * @returns {string}
23972   *          The plugin's version or an empty string.
23973   */
23974
23975
23976  Plugin.getPluginVersion = function getPluginVersion(name) {
23977    var plugin = getPlugin(name);
23978
23979    return plugin && plugin.VERSION || '';
23980  };
23981
23982  return Plugin;
23983}();
23984
23985/**
23986 * Gets a plugin by name if it exists.
23987 *
23988 * @static
23989 * @method   getPlugin
23990 * @memberOf Plugin
23991 * @param    {string} name
23992 *           The name of a plugin.
23993 *
23994 * @returns  {Function|undefined}
23995 *           The plugin (or `undefined`).
23996 */
23997
23998
23999Plugin.getPlugin = getPlugin;
24000
24001/**
24002 * The name of the base plugin class as it is registered.
24003 *
24004 * @type {string}
24005 */
24006Plugin.BASE_PLUGIN_NAME = BASE_PLUGIN_NAME;
24007
24008Plugin.registerPlugin(BASE_PLUGIN_NAME, Plugin);
24009
24010/**
24011 * Documented in player.js
24012 *
24013 * @ignore
24014 */
24015Player.prototype.usingPlugin = function (name) {
24016  return !!this[PLUGIN_CACHE_KEY] && this[PLUGIN_CACHE_KEY][name] === true;
24017};
24018
24019/**
24020 * Documented in player.js
24021 *
24022 * @ignore
24023 */
24024Player.prototype.hasPlugin = function (name) {
24025  return !!pluginExists(name);
24026};
24027
24028/**
24029 * 
vendor: 3,442 bytes, lines 24029-24162
24029Signals that a plugin is about to be set up on a player.
24030 *
24031 * @event    Player#beforepluginsetup
24032 * @type     {Plugin~PluginEventHash}
24033 */
24034
24035/**
24036 * Signals that a plugin is about to be set up on a player - by name. The name
24037 * is the name of the plugin.
24038 *
24039 * @event    Player#beforepluginsetup:$name
24040 * @type     {Plugin~PluginEventHash}
24041 */
24042
24043/**
24044 * Signals that a plugin has just been set up on a player.
24045 *
24046 * @event    Player#pluginsetup
24047 * @type     {Plugin~PluginEventHash}
24048 */
24049
24050/**
24051 * Signals that a plugin has just been set up on a player - by name. The name
24052 * is the name of the plugin.
24053 *
24054 * @event    Player#pluginsetup:$name
24055 * @type     {Plugin~PluginEventHash}
24056 */
24057
24058/**
24059 * @typedef  {Object} Plugin~PluginEventHash
24060 *
24061 * @property {string} instance
24062 *           For basic plugins, the return value of the plugin function. For
24063 *           advanced plugins, the plugin instance on which the event is fired.
24064 *
24065 * @property {string} name
24066 *           The name of the plugin.
24067 *
24068 * @property {string} plugin
24069 *           For basic plugins, the plugin function. For advanced plugins, the
24070 *           plugin class/constructor.
24071 */
24072
24073/**
24074 * @file extend.js
24075 * @module extend
24076 */
24077
24078/**
24079 * A combination of node inherits and babel's inherits (after transpile).
24080 * Both work the same but node adds `super_` to the subClass
24081 * and Bable adds the superClass as __proto__. Both seem useful.
24082 *
24083 * @param {Object} subClass
24084 *        The class to inherit to
24085 *
24086 * @param {Object} superClass
24087 *        The class to inherit from
24088 *
24089 * @private
24090 */
24091var _inherits = function _inherits(subClass, superClass) {
24092  if (typeof superClass !== 'function' && superClass !== null) {
24093    throw new TypeError('Super expression must either be null or a function, not ' + (typeof superClass === 'undefined' ? 'undefined' : _typeof(superClass)));
24094  }
24095
24096  subClass.prototype = Object.create(superClass && superClass.prototype, {
24097    constructor: {
24098      value: subClass,
24099      enumerable: false,
24100      writable: true,
24101      configurable: true
24102    }
24103  });
24104
24105  if (superClass) {
24106    // node
24107    subClass.super_ = superClass;
24108  }
24109};
24110
24111/**
24112 * Function for subclassing using the same inheritance that
24113 * videojs uses internally
24114 *
24115 * @static
24116 * @const
24117 *
24118 * @param {Object} superClass
24119 *        The class to inherit from
24120 *
24121 * @param {Object} [subClassMethods={}]
24122 *        The class to inherit to
24123 *
24124 * @return {Object}
24125 *         The new object with subClassMethods that inherited superClass.
24126 */
24127var extendFn = function extendFn(superClass) {
24128  var subClassMethods = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
24129
24130  var subClass = function subClass() {
24131    superClass.apply(this, arguments);
24132  };
24133
24134  var methods = {};
24135
24136  if ((typeof subClassMethods === 'undefined' ? 'undefined' : _typeof(subClassMethods)) === 'object') {
24137    if (subClassMethods.constructor !== Object.prototype.constructor) {
24138      subClass = subClassMethods.constructor;
24139    }
24140    methods = subClassMethods;
24141  } else if (typeof subClassMethods === 'function') {
24142    subClass = subClassMethods;
24143  }
24144
24145  _inherits(subClass, superClass);
24146
24147  // Extend subObj's prototype with functions and other properties from props
24148  for (var name in methods) {
24149    if (methods.hasOwnProperty(name)) {
24150      subClass.prototype[name] = methods[name];
24151    }
24152  }
24153
24154  return subClass;
24155};
24156
24157/**
24158 * @file video.js
24159 * @module videojs
24160 */
24161// Include the built-in techs
24162// HTML5 Element Shim for IE8
24163if (typeof HTMLVideoElement === 'undefined' && isReal()) {
24164  document_1.createElement('video');
24165  document_1.createElement('audio');
24166  document_1.createElement('track');
24167}
24168
24169/**
24170 * Doubles as the main function for users to create a player instance and also
24171 * the main library object.
24172 * The `videojs` function can be used to initialize or retrieve a player.
24173  *
24174 * @param {string|Element} id
24175 *        Video element or video element ID
24176 *
24177 * @param {Object} [options]
24178 *        Optional options object for config/settings
24179 *
24180 * @param {Component~ReadyCallback} [ready]
24181 *        Optional ready callback
24182 *
24183 * @return {Player}
24184 *         A player instance
24185 */
24186function videojs(id, options, ready) {
24187  var tag = void 0;
24188
24189  // Allow for element or ID to be passed in
24190  // String ID
24191  if (typeof id === 'string') {
24192    var players = videojs.getPlayers();
24193
24194    // Adjust for jQuery ID syntax
24195    if (id.indexOf('#') === 0) {
24196      id = id.slice(1);
24197    }
24198
24199    // If a player instance has already been created for this ID return it.
24200    if (players[id]) {
24201
24202      // If options or ready function are passed, warn
24203      if (options) {
24204        log$1.warn('Player "' + id + '" is already initialised. Options will not be applied.');
24205      }
24206
24207      if (ready) {
24208        players[id].ready(ready);
24209      }
24210
24211      return players[id];
24212    }
24213
24214    // Otherwise get element for ID
24215    tag = $('#' + id);
24216
24217    // ID is a media element
24218  } else {
24219    tag = id;
24220  }
24221
24222  // Check for a useable element
24223  // re: nodeName, could be a box div also
24224  if (!tag || !tag.nodeName) {
24225    throw new TypeError('The element or ID supplied is not valid. (videojs)');
24226  }
24227
24228  // Element may have a player attr referring to an already created player instance.
24229  // If so return that otherwise set up a new player below
24230  if (tag.player || Player.players[tag.playerId]) {
24231    return tag.player || Player.players[tag.playerId];
24232  }
24233
24234  options = options || {};
24235
24236  videojs.hooks('beforesetup').forEach(function (hookFunction) {
24237    var opts = hookFunction(tag, mergeOptions(options));
24238
24239    if (!isObject(opts) || Array.isArray(opts)) {
24240      log$1.error('please return an object in beforesetup hooks');
24241      return;
24242    }
24243
24244    options = mergeOptions(options, opts);
24245  });
24246
24247  var PlayerComponent = Component.getComponent('Player');
24248  // If not, set up a new player
24249  var player = new PlayerComponent(tag, options, ready);
24250
24251  videojs.hooks('setup').forEach(function (hookFunction) {
24252    return hookFunction(player);
24253  });
24254
24255  return player;
24256}
24257
24258/**
24259 * An Object that contains lifecycle hooks as keys which point to an array
24260 * of functions that are run when a lifecycle is triggered
24261 */
24262videojs.hooks_ = {};
24263
24264/**
24265 * Get a list of hooks for a specific lifecycle
24266 * @function videojs.hooks
24267 *
24268 * @param {string} type
24269 *        the lifecyle to get hooks from
24270 *
24271 * @param {Function} [fn]
24272 *        Optionally add a hook to the lifecycle that your are getting.
24273 *
24274 * @return {Array}
24275 *         an array of hooks, or an empty array if there are none.
24276 */
24277videojs.hooks = function (type, fn) {
24278  videojs.hooks_[type] = videojs.hooks_[type] || [];
24279  if (fn) {
24280    videojs.hooks_[type] = videojs.hooks_[type].concat(fn);
24281  }
24282  return videojs.hooks_[type];
24283};
24284
24285/**
24286 * Add a function hook to a specific videojs lifecycle.
24287 *
24288 * @param {string} type
24289 *        the lifecycle to hook the function to.
24290 *
24291 * @param {Function|Function[]}
24292 *        The function or array of functions to attach.
24293 */
24294videojs.hook = function (type, fn) {
24295  videojs.hooks(type, fn);
24296};
24297
24298/**
24299 * Remove a hook from a specific videojs lifecycle.
24300 *
24301 * @param {string} type
24302 *        the lifecycle that the function hooked to
24303 *
24304 * @param {Function} fn
24305 *        The hooked function to remove
24306 *
24307 * @return {boolean}
24308 *         The function that was removed or undef
24309 */
24310videojs.removeHook = function (type, fn) {
24311  var index = videojs.hooks(type).indexOf(fn);
24312
24313  if (index <= -1) {
24314    return false;
24315  }
24316
24317  videojs.hooks_[type] = videojs.hooks_[type].slice();
24318  videojs.hooks_[type].splice(index, 1);
24319
24320  return true;
24321};
24322
24323// Add default styles
vendor: 4,925 bytes, lines 24324-24508
24324if (window_1.VIDEOJS_NO_DYNAMIC_STYLE !== true && isReal()) {
24325  var style = $('.vjs-styles-defaults');
24326
24327  if (!style) {
24328    style = createStyleElement('vjs-styles-defaults');
24329    var head = $('head');
24330
24331    if (head) {
24332      head.insertBefore(style, head.firstChild);
24333    }
24334    setTextContent(style, '\n      .video-js {\n        width: 300px;\n        height: 150px;\n      }\n\n      .vjs-fluid {\n        padding-top: 56.25%\n      }\n    ');
24335  }
24336}
24337
24338// Run Auto-load players
24339// You have to wait at least once in case this script is loaded after your
24340// video in the DOM (weird behavior only with minified version)
24341autoSetupTimeout(1, videojs);
24342
24343/**
24344 * Current software version. Follows semver.
24345 *
24346 * @type {string}
24347 */
24348videojs.VERSION = version;
24349
24350/**
24351 * The global options object. These are the settings that take effect
24352 * if no overrides are specified when the player is created.
24353 *
24354 * @type {Object}
24355 */
24356videojs.options = Player.prototype.options_;
24357
24358/**
24359 * Get an object with the currently created players, keyed by player ID
24360 *
24361 * @return {Object}
24362 *         The created players
24363 */
24364videojs.getPlayers = function () {
24365  return Player.players;
24366};
24367
24368/**
24369 * Expose players object.
24370 *
24371 * @memberOf videojs
24372 * @property {Object} players
24373 */
24374videojs.players = Player.players;
24375
24376/**
24377 * Get a component class object by name
24378 *
24379 * @borrows Component.getComponent as videojs.getComponent
24380 */
24381videojs.getComponent = Component.getComponent;
24382
24383/**
24384 * Register a component so it can referred to by name. Used when adding to other
24385 * components, either through addChild `component.addChild('myComponent')` or through
24386 * default children options  `{ children: ['myComponent'] }`.
24387 *
24388 * > NOTE: You could also just initialize the component before adding.
24389 * `component.addChild(new MyComponent());`
24390 *
24391 * @param {string} name
24392 *        The class name of the component
24393 *
24394 * @param {Component} comp
24395 *        The component class
24396 *
24397 * @return {Component}
24398 *         The newly registered component
24399 */
24400videojs.registerComponent = function (name$$1, comp) {
24401  if (Tech.isTech(comp)) {
24402    log$1.warn('The ' + name$$1 + ' tech was registered as a component. It should instead be registered using videojs.registerTech(name, tech)');
24403  }
24404
24405  Component.registerComponent.call(Component, name$$1, comp);
24406};
24407
24408/**
24409 * Get a Tech class object by name
24410 *
24411 * @borrows Tech.getTech as videojs.getTech
24412 */
24413videojs.getTech = Tech.getTech;
24414
24415/**
24416 * Register a Tech so it can referred to by name.
24417 * This is used in the tech order for the player.
24418 *
24419 * @borrows Tech.registerTech as videojs.registerTech
24420 */
24421videojs.registerTech = Tech.registerTech;
24422
24423videojs.use = use;
24424
24425/**
24426 * A suite of browser and device tests from {@link browser}.
24427 *
24428 * @type {Object}
24429 * @private
24430 */
24431videojs.browser = browser;
24432
24433/**
24434 * Whether or not the browser supports touch events. Included for backward
24435 * compatibility with 4.x, but deprecated. Use `videojs.browser.TOUCH_ENABLED`
24436 * instead going forward.
24437 *
24438 * @deprecated since version 5.0
24439 * @type {boolean}
24440 */
24441videojs.TOUCH_ENABLED = TOUCH_ENABLED;
24442
24443/**
24444 * Subclass an existing class
24445 * Mimics ES6 subclassing with the `extend` keyword
24446 *
24447 * @borrows extend:extendFn as videojs.extend
24448 */
24449videojs.extend = extendFn;
24450
24451/**
24452 * Merge two options objects recursively
24453 * Performs a deep merge like lodash.merge but **only merges plain objects**
24454 * (not arrays, elements, anything else)
24455 * Other values will be copied directly from the second object.
24456 *
24457 * @borrows merge-options:mergeOptions as videojs.mergeOptions
24458 */
24459videojs.mergeOptions = mergeOptions;
24460
24461/**
24462 * Change the context (this) of a function
24463 *
24464 * > NOTE: as of v5.0 we require an ES5 shim, so you should use the native
24465 * `function() {}.bind(newContext);` instead of this.
24466 *
24467 * @borrows fn:bind as videojs.bind
24468 */
24469videojs.bind = bind;
24470
24471/**
24472 * Register a Video.js plugin.
24473 *
24474 * @borrows plugin:registerPlugin as videojs.registerPlugin
24475 * @method registerPlugin
24476 *
24477 * @param  {string} name
24478 *         The name of the plugin to be registered. Must be a string and
24479 *         must not match an existing plugin or a method on the `Player`
24480 *         prototype.
24481 *
24482 * @param  {Function} plugin
24483 *         A sub-class of `Plugin` or a function for basic plugins.
24484 *
24485 * @return {Function}
24486 *         For advanced plugins, a factory function for that plugin. For
24487 *         basic plugins, a wrapper function that initializes the plugin.
24488 */
24489videojs.registerPlugin = Plugin.registerPlugin;
24490
24491/**
24492 * Deprecated method to register a plugin with Video.js
24493 *
24494 * @deprecated
24495 *        videojs.plugin() is deprecated; use videojs.registerPlugin() instead
24496 *
24497 * @param {string} name
24498 *        The plugin name
24499 *
24500 * @param {Plugin|Function} plugin
24501 *         The plugin sub-class or function
24502 */
24503videojs.plugin = function (name$$1, plugin) {
24504  log$1.warn('videojs.plugin() is deprecated; use videojs.registerPlugin() instead');
24505  return Plugin.registerPlugin(name$$1, plugin);
24506};
24507
24508/**
24509 * Gets an object containing multiple Video.js plugins.
24510 *
24511 * @param  {Array} [names]
24512 *         If provided, should be an array of plugin names. Defaults to _all_
24513 *         plugin names.
24514 *
24515 * @return {Object|undefined}
24516 *         An object containing plugin(s) associated with their name(s) or
24517 *         `undefined` if no matching plugins exist).
24518 */
24519videojs.getPlugins = Plugin.getPlugins;
24520
24521/**
24522 * Gets a plugin by name if it exists.
24523 *
24524 * @param  {string} name
24525 *         The name of a plugin.
24526 *
24527 * @return {Function|undefined}
24528 *         The plugin (or `undefined`).
24529 */
24530videojs.getPlugin = Plugin.getPlugin;
24531
24532/**
24533 * Gets a plugin's version, if available
24534 *
24535 * @param  {string} name
24536 *         The name of a plugin.
24537 *
24538 * @return {string}
24539 *         The plugin's version or an empty string.
24540 */
24541videojs.getPluginVersion = Plugin.getPluginVersion;
24542
24543/**
24544 * Adding languages so that they're available to all players.
24545 * Example: `videojs.addLanguage('es', { 'Hello': 'Hola' });`
24546 *
24547 * @param {string} code
24548 *        The language code or dictionary property
24549 *
24550 * @param {Object} data
24551 *        The data values to be translated
24552 *
24553 * @return {Object}
24554 *         The resulting language dictionary object
24555 */
24556videojs.addLanguage = function (code, data) {
24557  var _mergeOptions;
24558
24559  code = ('' + code).toLowerCase();
24560
24561  videojs.options.languages = mergeOptions(videojs.options.languages, (_mergeOptions = {}, _mergeOptions[code] = data, _mergeOptions));
24562
24563  return videojs.options.languages[code];
24564};
24565
24566/**
24567 * Log messages
24568 *
24569 * @borrows log:log as videojs.log
24570 */
24571videojs.log = log$1;
24572
24573/**
24574 * Creates an emulated TimeRange object.
24575 *
24576 * @borrows time-ranges:createTimeRanges as videojs.createTimeRange
24577 */
24578/**
24579 * @borrows time-ranges:createTimeRanges as videojs.createTimeRanges
24580 */
24581videojs.createTimeRange = videojs.createTimeRanges = createTimeRanges;
24582
24583/**
24584 * Format seconds as a time string, H:MM:SS or M:SS
24585 * Supplying a guide (in seconds) will force a number of leading zeros
24586 * to cover the length of the guide
24587 *
24588 * @borrows format-time:formatTime as videojs.formatTime
24589 */
24590videojs.formatTime = formatTime;
24591
24592/**
24593 * Resolve and parse the elements of a URL
24594 *
24595 * @borrows url:parseUrl as videojs.parseUrl
24596 */
24597videojs.parseUrl = parseUrl;
24598
24599/**
24600 * Returns whether the url passed is a cross domain request or not.
24601 *
24602 * @borrows url:isCrossOrigin as videojs.isCrossOrigin
24603 */
24604videojs.isCrossOrigin = isCrossOrigin;
24605
24606/**
24607 * Event target class.
24608 *
24609 * @borrows EventTarget as videojs.EventTarget
24610 */
24611videojs.EventTarget = EventTarget;
24612
24613/**
24614 * Add an event listener to element
24615 * It stores the handler function in a separate cache object
24616 * and adds a generic handler to the element's event,
24617 * along with a unique id (guid) to the element.
24618 *
24619 * @borrows events:on as videojs.on
24620 */
24621videojs.on = on;
24622
24623/**
24624 * Trigger a listener only once for an event
24625 *
24626 * @borrows events:one as videojs.one
24627 */
24628videojs.one = one;
24629
24630/**
24631 * Removes event listeners from an element
24632 *
24633 * @borrows events:off as videojs.off
24634 */
24635videojs.off = off;
24636
24637/**
24638 * Trigger an event for an element
24639 *
24640 * @borrows events:trigger as videojs.trigger
24641 */
24642videojs.trigger = trigger;
24643
24644/**
24645 * A cross-browser XMLHttpRequest wrapper. Here's a simple example:
24646 *
24647 * @param {Object} options
24648 *        settings for the request.
24649 *
24650 * @return {XMLHttpRequest|XDomainRequest}
24651 *         The request object.
24652 *
24653 * @see https://github.com/Raynos/xhr
24654 */
24655videojs.xhr = xhr;
24656
24657/**
24658 * TextTrack class
24659 *
24660 * @borrows TextTrack as videojs.TextTrack
24661 */
24662videojs.TextTrack = TextTrack;
24663
24664/**
24665 * export the AudioTrack class so that source handlers can create
24666 * AudioTracks and then add them to the players AudioTrackList
24667 *
24668 * @borrows AudioTrack as videojs.AudioTrack
24669 */
24670videojs.AudioTrack = AudioTrack;
24671
24672/**
24673 * export the VideoTrack class so that source handlers can create
24674 * VideoTracks and then add them to the players VideoTrackList
24675 *
24676 * @borrows VideoTrack as videojs.VideoTrack
24677 */
24678videojs.VideoTrack = VideoTrack;
24679
24680/**
24681 * Determines, via duck typing, whether or not a value is a DOM element.
24682 *
24683 * @borrows dom:isEl as videojs.isEl
24684 * @deprecated Use videojs.dom.isEl() instead
24685 */
24686
24687/**
24688 * Determines, via duck typing, whether or not a value is a text node.
24689 *
24690 * @borrows dom:isTextNode as videojs.isTextNode
24691 * @deprecated Use videojs.dom.isTextNode() instead
24692 */
24693
24694/**
24695 * Creates an element and applies properties.
24696 *
24697 * @borrows dom:createEl as videojs.createEl
24698 * @deprecated Use videojs.dom.createEl() instead
24699 */
24700
24701/**
24702 * Check if an element has a CSS class
24703 *
24704 * @borrows dom:hasElClass as videojs.hasClass
24705 * @deprecated Use videojs.dom.hasClass() instead
24706 */
24707
24708/**
24709 * Add a CSS class name to an element
24710 *
24711 * @borrows dom:addElClass as videojs.addClass
24712 * @deprecated Use videojs.dom.addClass() instead
24713 */
24714
24715/**
24716 * Remove a CSS class name from an element
24717 *
24718 * @borrows dom:removeElClass as videojs.removeClass
24719 * @deprecated Use videojs.dom.removeClass() instead
24720 */
24721
24722/**
24723 * Adds or removes a CSS class name on an element depending on an optional
24724 * condition or the presence/absence of the class name.
24725 *
24726 * @borrows dom:toggleElClass as videojs.toggleClass
24727 * @deprecated Use videojs.dom.toggleClass() instead
24728 */
24729
24730/**
24731 * Apply attributes to an HTML element.
24732 *
24733 * @borrows dom:setElAttributes as videojs.setAttribute
24734 * @deprecated Use videojs.dom.setAttributes() instead
24735 */
24736
24737/**
24738 * Get an element's attribute values, as defined on the HTML tag
24739 * Attributes are not the same as properties. They're defined on the tag
24740 * or with setAttribute (which shouldn't be used with HTML)
24741 * This will return true or false for boolean attributes.
24742 *
24743 * @borrows dom:getElAttributes as videojs.getAttributes
24744 * @deprecated Use videojs.dom.getAttributes() instead
24745 */
24746
24747/**
24748 * Empties the contents of an element.
24749 *
24750 * @borrows dom:emptyEl as videojs.emptyEl
24751 * @deprecated Use videojs.dom.emptyEl() instead
24752 */
24753
24754/**
24755 * Normalizes and appends content to an element.
24756 *
24757 * The content for an element can be passed in multiple types and
24758 * combinations, whose behavior is as follows:
24759 *
24760 * - String
24761 *   Normalized into a text node.
24762 *
24763 * - Element, TextNode
24764 *   Passed through.
24765 *
24766 * - Array
24767 *   A one-dimensional array of strings, elements, nodes, or functions (which
24768 *   return single strings, elements, or nodes).
24769 *
24770 * - Function
24771 *   If the sole argument, is expected to produce a string, element,
24772 *   node, or array.
24773 *
24774 * @borrows dom:appendContents as videojs.appendContet
24775 * @deprecated Use videojs.dom.appendContent() instead
24776 */
24777
24778/**
24779 * Normalizes and inserts content into an element; this is identical to
24780 * `appendContent()`, except it empties the element first.
24781 *
24782 * The content for an element can be passed in multiple types and
24783 * combinations, whose behavior is as follows:
24784 *
24785 * - String
24786 *   Normalized into a text node.
24787 *
24788 * - Element, TextNode
24789 *   Passed through.
24790 *
24791 * - Array
24792 *   A one-dimensional array of strings, elements, nodes, or functions (which
24793 *   return single strings, elements, or nodes).
24794 *
24795 * - Function
24796 *   If the sole argument, is expected to produce a string, element,
24797 *   node, or array.
24798 *
24799 * @borrows dom:insertContent as videojs.insertContent
24800 * @deprecated Use videojs.dom.insertContent() instead
24801 */
24802['isEl', 'isTextNode', 'createEl', 'hasClass', 'addClass', 'removeClass', 'toggleClass', 'setAttributes', 'getAttributes', 'emptyEl', 'appendContent', 'insertContent'].forEach(function (k) {
24803  videojs[k] = function () {
24804    log$1.warn('videojs.' + k + '() is deprecated; use videojs.dom.' + k + '() instead');
24805    return Dom[k].apply(null, arguments);
24806  };
24807});
24808
24809/**
24810 * A safe getComputedStyle with an IE8 fallback.
24811 *
24812 * This is because in Firefox, if the player is loaded in an iframe with `display:none`,
24813 * then `getComputedStyle` returns `null`, so, we do a null-check to make sure
24814 * that the player doesn't break in these cases.
24815 * See https://bugzilla.mozilla.org/show_bug.cgi?id=548397 for more details.
24816 *
24817 * @borrows computed-style:computedStyle as videojs.computedStyle
24818 */
24819videojs.computedStyle = computedStyle;
24820
24821/**
24822 * Export the Dom utilities for use in external plugins
24823 * and Tech's
24824 */
24825videojs.dom = Dom;
24826
24827/**
24828 * Export the Url utilities for use in external plugins
24829 * and Tech's
24830 */
24831videojs.url = Url;
24832
24833return videojs;
24834
24835})));

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.