PageSourceSearch

https://www.paloaltonetworks.it/etc/clientlibs/clean/dependencies/video-js/video-js-yt.js

js paloaltonetworks.it collected 2026-10-02 20:28:59 UTC 777,532 bytes, 27,980 lines download raw bytes

vendor: 15,927 bytes, lines 1-623
1/**
2 * @license
3 * Video.js 6.12.0 <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.12.0";
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) || /CriOS/i.test(USER_AGENT));
133var CHROME_VERSION = function () {
134  var match = USER_AGENT.match(/(Chrome|CriOS)\/(\d+)/);
135
136  if (match && match[2]) {
137    return parseFloat(match[2]);
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) && !IS_CHROME;
156
157var TOUCH_ENABLED = isReal() && ('ontouchstart' in window_1 || window_1.navigator.maxTouchPoints || 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 = 'info';
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  if (!window_1.console) {
455    return;
456  }
457
458  // Was setting these once outside of this function, but containing them
459  // in the function makes it easier to test cases where console doesn't exist
460  // when the module is executed.
461  var fn = window_1.console[type];
462
463  if (!fn && type === 'debug') {
464    // Certain browsers don't have support for console.debug. For those, we
465    // should default to the closest comparable log.
466    fn = window_1.console.info || window_1.console.log;
467  }
468
469  // Bail out if there's no console or if this type is not allowed by the
470  // current logging level.
471  if (!fn || !lvl || !lvlRegExp.test(type)) {
472    return;
473  }
474
475  // IEs previous to 11 log objects uselessly as "[object Object]"; so, JSONify
476  // objects and arrays for those less-capable browsers.
477  if (stringify) {
478    args = args.map(function (a) {
479      if (isObject(a) || Array.isArray(a)) {
480        try {
481          return JSON.stringify(a);
482        } catch (x) {
483          return String(a);
484        }
485      }
486
487      // Cast to string before joining, so we get null and undefined explicitly
488      // included in output (as we would in a modern console).
489      return String(a);
490    }).join(' ');
491  }
492
493  // Old IE versions do not allow .apply() for console methods (they are
494  // reported as objects rather than functions).
495  if (!fn.apply) {
496    fn(args);
497  } else {
498    fn[Array.isArray(args) ? 'apply' : 'call'](window_1.console, args);
499  }
500};
501
502/**
503 * Logs plain debug messages. Similar to `console.log`.
504 *
505 * @class
506 * @param    {Mixed[]} args
507 *           One or more messages or objects that should be logged.
508 */
509log = function log() {
510  for (var _len = arguments.length, args = Array(_len), _key = 0; _key < _len; _key++) {
511    args[_key] = arguments[_key];
512  }
513
514  logByType('log', args);
515};
516
517/**
518 * Enumeration of available logging levels, where the keys are the level names
519 * and the values are `|`-separated strings containing logging methods allowed
520 * in that logging level. These strings are used to create a regular expression
521 * matching the function name being called.
522 *
523 * Levels provided by video.js are:
524 *
525 * - `off`: Matches no calls. Any value that can be cast to `false` will have
526 *   this effect. The most restrictive.
527 * - `all`: Matches only Video.js-provided functions (`debug`, `log`,
528 *   `log.warn`, and `log.error`).
529 * - `debug`: Matches `log.debug`, `log`, `log.warn`, and `log.error` calls.
530 * - `info` (default): Matches `log`, `log.warn`, and `log.error` calls.
531 * - `warn`: Matches `log.warn` and `log.error` calls.
532 * - `error`: Matches only `log.error` calls.
533 *
534 * @type {Object}
535 */
536log.levels = {
537  all: 'debug|log|warn|error',
538  off: '',
539  debug: 'debug|log|warn|error',
540  info: 'log|warn|error',
541  warn: 'warn|error',
542  error: 'error',
543  DEFAULT: level
544};
545
546/**
547 * Get or set the current logging level. If a string matching a key from
548 * {@link log.levels} is provided, acts as a setter. Regardless of argument,
549 * returns the current logging level.
550 *
551 * @param  {string} [lvl]
552 *         Pass to set a new logging level.
553 *
554 * @return {string}
555 *         The current logging level.
556 */
557log.level = function (lvl) {
558  if (typeof lvl === 'string') {
559    if (!log.levels.hasOwnProperty(lvl)) {
560      throw new Error('"' + lvl + '" in not a valid log level');
561    }
562    level = lvl;
563  }
564  return level;
565};
566
567/**
568 * Returns an array containing everything that has been logged to the history.
569 *
570 * This array is a shallow clone of the internal history record. However, its
571 * contents are _not_ cloned; so, mutating objects inside this array will
572 * mutate them in history.
573 *
574 * @return {Array}
575 */
576log.history = function () {
577  return history ? [].concat(history) : [];
578};
579
580/**
581 * Clears the internal history tracking, but does not prevent further history
582 * tracking.
583 */
584log.history.clear = function () {
585  if (history) {
586    history.length = 0;
587  }
588};
589
590/**
591 * Disable history tracking if it is currently enabled.
592 */
593log.history.disable = function () {
594  if (history !== null) {
595    history.length = 0;
596    history = null;
597  }
598};
599
600/**
601 * Enable history tracking if it is currently disabled.
602 */
603log.history.enable = function () {
604  if (history === null) {
605    history = [];
606  }
607};
608
609/**
610 * Logs error messages. Similar to `console.error`.
611 *
612 * @param {Mixed[]} args
613 *        One or more messages or objects that should be logged as an error
614 */
615log.error = function () {
616  for (var _len2 = arguments.length, args = Array(_len2), _key2 = 0; _key2 < _len2; _key2++) {
617    args[_key2] = arguments[_key2];
618  }
619
620  return logByType('error', args);
621};
622
623/**
624 * Logs warning messages. Similar to `console.warn`.
625 *
626 * @param {Mixed[]} args
627 *        One or more messages or objects that should be logged as a warning.
628 */
629log.warn = function () {
630  for (var _len3 = arguments.length, args = Array(_len3), _key3 = 0; _key3 < _len3; _key3++) {
631    args[_key3] = arguments[_key3];
632  }
633
634  return logByType('warn', args);
635};
636
637/**
638 * Logs debug messages. Similar to `console.debug`, but may also act as a comparable
639 * log if `console.debug` is not available
640 *
641 * @param {Mixed[]} args
642 *        One or more messages or objects that should be logged as debug.
643 */
644log.debug = function () {
645  for (var _len4 = arguments.length, args = Array(_len4), _key4 = 0; _key4 < _len4; _key4++) {
646    args[_key4] = arguments[_key4];
647  }
648
649  return logByType('debug', args);
650};
651
652var log$1 = log;
653
654function clean (s) {
655  return s.replace(/\n\r?\s*/g, '')
656}
657
658
659var tsml = function tsml (sa) {
660  var s = ''
661    , i = 0;
662
663  for (; i < arguments.length; i++)
664    s += clean(sa[i]) + (arguments[i + 1] || '');
665
666  return s
667};
668
669/**
670 * @file computed-style.js
671 * @module computed-style
672 */
673/**
674 * A safe getComputedStyle with an IE8 fallback.
675 *
676 * This is needed because in Firefox, if the player is loaded in an iframe with
677 * `display:none`, then `getComputedStyle` returns `null`, so, we do a null-check to
678 * make sure  that the player doesn't break in these cases.
679 *
680 * @param {Element} el
681 *        The element you want the computed style of
682 *
683 * @param {string} prop
684 *        The property name you want
685 *
686 * @see https://bugzilla.mozilla.org/show_bug.cgi?id=548397
687 *
688 * @static
689 * @const
690 */
691function computedStyle(el, prop) {
692  if (!el || !prop) {
693    return '';
694  }
695
696  if (typeof window_1.getComputedStyle === 'function') {
697    var cs = window_1.getComputedStyle(el);
698
699    return cs ? cs[prop] : '';
700  }
701
702  return el.currentStyle[prop] || '';
703}
704
705var _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 ', '.']);
706
707/**
708 * @file dom.js
709 * @module dom
710 */
711/**
712 * Detect if a value is a string with any non-whitespace characters.
713 *
714 * @param {string} str
715 *        The string to check
716 *
717 * @return {boolean}
718 *         - True if the string is non-blank
719 *         - False otherwise
720 *
721 */
722function isNonBlankString(str) {
723  return typeof str === 'string' && /\S/.test(str);
724}
725
726/**
727 * Throws an error if the passed string has whitespace. This is used by
728 * class methods to be relatively consistent with the classList API.
729 *
730 * @param {string} str
731 *         The string to check for whitespace.
732 *
733 * @throws {Error}
734 *         Throws an error if there is whitespace in the string.
735 *
736 */
737function throwIfWhitespace(str) {
738  if (/\s/.test(str)) {
739    throw new Error('class has illegal whitespace characters');
740  }
741}
742
743/**
744 * Produce a regular expression for matching a className within an elements className.
745 *
746 * @param {string} className
747 *         The className to generate the RegExp for.
748 *
749 * @return {RegExp}
750 *         The RegExp that will check for a specific `className` in an elements
751 *         className.
752 */
753function classRegExp(className) {
754  return new RegExp('(^|\\s)' + className + '($|\\s)');
755}
756
757/**
758 * Whether the current DOM interface appears to be real.
759 *
760 * @return {Boolean}
761 */
762function isReal() {
763  return (
764
765    // Both document and window will never be undefined thanks to `global`.
766    document_1 === window_1.document &&
767
768    // In IE < 9, DOM methods return "object" as their type, so all we can
769    // confidently check is that it exists.
770    typeof document_1.createElement !== 'undefined'
771  );
772}
773
774/**
775 * Determines, via duck typing, whether or not a value is a DOM element.
776 *
777 * @param {Mixed} value
778 *        The thing to check
779 *
780 * @return {boolean}
781 *         - True if it is a DOM element
782 *         - False otherwise
783 */
784function isEl(value) {
785  return isObject(value) && value.nodeType === 1;
786}
787
788/**
789 * Determines if the current DOM is embedded in an iframe.
790 *
791 * @return {boolean}
792 *
793 */
794function isInFrame() {
795
796  // We need a try/catch here because Safari will throw errors when attempting
797  // to get either `parent` or `self`
798  try {
799    return window_1.parent !== window_1.self;
800  } catch (x) {
801    return true;
802  }
803}
804
805/**
806 * Creates functions to query the DOM using a given method.
807 *
808 * @param {string} method
809 *         The method to create the query with.
810 *
811 * @return {Function}
812 *         The query method
813 */
814function createQuerier(method) {
815  return function (selector, context) {
816    if (!isNonBlankString(selector)) {
817      return document_1[method](null);
818    }
819    if (isNonBlankString(context)) {
820      context = document_1.querySelector(context);
821    }
822
823    var ctx = isEl(context) ? context : document_1;
824
825    return ctx[method] && ctx[method](selector);
826  };
827}
828
829/**
830 * Creates an element and applies properties.
831 *
832 * @param {string} [tagName='div']
833 *         Name of tag to be created.
834 *
835 * @param {Object} [properties={}]
836 *         Element properties to be applied.
837 *
838 * @param {Object} [attributes={}]
839 *         Element attributes to be applied.
840 *
841 * @param {String|Element|TextNode|Array|Function} [content]
842 *         Contents for the element (see: {@link dom:normalizeContent})
843 *
844 * @return {Element}
845 *         The element that was created.
846 */
847function createEl() {
848  var tagName = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 'div';
849  var properties = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
850  var attributes = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
851  var content = arguments[3];
852
853  var el = document_1.createElement(tagName);
854
vendor: 4,609 bytes, lines 855-1031
855  Object.getOwnPropertyNames(properties).forEach(function (propName) {
856    var val = properties[propName];
857
858    // See #2176
859    // We originally were accepting both properties and attributes in the
860    // same object, but that doesn't work so well.
861    if (propName.indexOf('aria-') !== -1 || propName === 'role' || propName === 'type') {
862      log$1.warn(tsml(_templateObject, propName, val));
863      el.setAttribute(propName, val);
864
865      // Handle textContent since it's not supported everywhere and we have a
866      // method for it.
867    } else if (propName === 'textContent') {
868      textContent(el, val);
869    } else {
870      el[propName] = val;
871    }
872  });
873
874  Object.getOwnPropertyNames(attributes).forEach(function (attrName) {
875    el.setAttribute(attrName, attributes[attrName]);
876  });
877
878  if (content) {
879    appendContent(el, content);
880  }
881
882  return el;
883}
884
885/**
886 * Injects text into an element, replacing any existing contents entirely.
887 *
888 * @param {Element} el
889 *        The element to add text content into
890 *
891 * @param {string} text
892 *        The text content to add.
893 *
894 * @return {Element}
895 *         The element with added text content.
896 */
897function textContent(el, text) {
898  if (typeof el.textContent === 'undefined') {
899    el.innerText = text;
900  } else {
901    el.textContent = text;
902  }
903  return el;
904}
905
906/**
907 * Insert an element as the first child node of another
908 *
909 * @param {Element} child
910 *        Element to insert
911 *
912 * @param {Element} parent
913 *        Element to insert child into
914 */
915function prependTo(child, parent) {
916  if (parent.firstChild) {
917    parent.insertBefore(child, parent.firstChild);
918  } else {
919    parent.appendChild(child);
920  }
921}
922
923/**
924 * Check if an element has a CSS class
925 *
926 * @param {Element} element
927 *        Element to check
928 *
929 * @param {string} classToCheck
930 *        Class name to check for
931 *
932 * @return {boolean}
933 *         - True if the element had the class
934 *         - False otherwise.
935 *
936 * @throws {Error}
937 *         Throws an error if `classToCheck` has white space.
938 */
939function hasClass(element, classToCheck) {
940  throwIfWhitespace(classToCheck);
941  if (element.classList) {
942    return element.classList.contains(classToCheck);
943  }
944  return classRegExp(classToCheck).test(element.className);
945}
946
947/**
948 * Add a CSS class name to an element
949 *
950 * @param {Element} element
951 *        Element to add class name to.
952 *
953 * @param {string} classToAdd
954 *        Class name to add.
955 *
956 * @return {Element}
957 *         The dom element with the added class name.
958 */
959function addClass(element, classToAdd) {
960  if (element.classList) {
961    element.classList.add(classToAdd);
962
963    // Don't need to `throwIfWhitespace` here because `hasElClass` will do it
964    // in the case of classList not being supported.
965  } else if (!hasClass(element, classToAdd)) {
966    element.className = (element.className + ' ' + classToAdd).trim();
967  }
968
969  return element;
970}
971
972/**
973 * Remove a CSS class name from an element
974 *
975 * @param {Element} element
976 *        Element to remove a class name from.
977 *
978 * @param {string} classToRemove
979 *        Class name to remove
980 *
981 * @return {Element}
982 *         The dom element with class name removed.
983 */
984function removeClass(element, classToRemove) {
985  if (element.classList) {
986    element.classList.remove(classToRemove);
987  } else {
988    throwIfWhitespace(classToRemove);
989    element.className = element.className.split(/\s+/).filter(function (c) {
990      return c !== classToRemove;
991    }).join(' ');
992  }
993
994  return element;
995}
996
997/**
998 * The callback definition for toggleElClass.
999 *
1000 * @callback Dom~PredicateCallback
1001 * @param {Element} element
1002 *        The DOM element of the Component.
1003 *
1004 * @param {string} classToToggle
1005 *        The `className` that wants to be toggled
1006 *
1007 * @return {boolean|undefined}
1008 *         - If true the `classToToggle` will get added to `element`.
1009 *         - If false the `classToToggle` will get removed from `element`.
1010 *         - If undefined this callback will be ignored
1011 */
1012
1013/**
1014 * Adds or removes a CSS class name on an element depending on an optional
1015 * condition or the presence/absence of the class name.
1016 *
1017 * @param {Element} element
1018 *        The element to toggle a class name on.
1019 *
1020 * @param {string} classToToggle
1021 *        The class that should be toggled
1022 *
1023 * @param {boolean|PredicateCallback} [predicate]
1024 *        See the return value for {@link Dom~PredicateCallback}
1025 *
1026 * @return {Element}
1027 *         The element with a class that has been toggled.
1028 */
1029function toggleClass(element, classToToggle, predicate) {
1030
1031  // This CANNOT use `classList` internally because IE does not support the
vendor: 5,424 bytes, lines 1032-1240
1032  // second parameter to the `classList.toggle()` method! Which is fine because
1033  // `classList` will be used by the add/remove functions.
1034  var has = hasClass(element, classToToggle);
1035
1036  if (typeof predicate === 'function') {
1037    predicate = predicate(element, classToToggle);
1038  }
1039
1040  if (typeof predicate !== 'boolean') {
1041    predicate = !has;
1042  }
1043
1044  // If the necessary class operation matches the current state of the
1045  // element, no action is required.
1046  if (predicate === has) {
1047    return;
1048  }
1049
1050  if (predicate) {
1051    addClass(element, classToToggle);
1052  } else {
1053    removeClass(element, classToToggle);
1054  }
1055
1056  return element;
1057}
1058
1059/**
1060 * Apply attributes to an HTML element.
1061 *
1062 * @param {Element} el
1063 *        Element to add attributes to.
1064 *
1065 * @param {Object} [attributes]
1066 *        Attributes to be applied.
1067 */
1068function setAttributes(el, attributes) {
1069  Object.getOwnPropertyNames(attributes).forEach(function (attrName) {
1070    var attrValue = attributes[attrName];
1071
1072    if (attrValue === null || typeof attrValue === 'undefined' || attrValue === false) {
1073      el.removeAttribute(attrName);
1074    } else {
1075      el.setAttribute(attrName, attrValue === true ? '' : attrValue);
1076    }
1077  });
1078}
1079
1080/**
1081 * Get an element's attribute values, as defined on the HTML tag
1082 * Attributes are not the same as properties. They're defined on the tag
1083 * or with setAttribute (which shouldn't be used with HTML)
1084 * This will return true or false for boolean attributes.
1085 *
1086 * @param {Element} tag
1087 *        Element from which to get tag attributes.
1088 *
1089 * @return {Object}
1090 *         All attributes of the element.
1091 */
1092function getAttributes(tag) {
1093  var obj = {};
1094
1095  // known boolean attributes
1096  // we can check for matching boolean properties, but older browsers
1097  // won't know about HTML5 boolean attributes that we still read from
1098  var knownBooleans = ',' + 'autoplay,controls,playsinline,loop,muted,default,defaultMuted' + ',';
1099
1100  if (tag && tag.attributes && tag.attributes.length > 0) {
1101    var attrs = tag.attributes;
1102
1103    for (var i = attrs.length - 1; i >= 0; i--) {
1104      var attrName = attrs[i].name;
1105      var attrVal = attrs[i].value;
1106
1107      // check for known booleans
1108      // the matching element property will return a value for typeof
1109      if (typeof tag[attrName] === 'boolean' || knownBooleans.indexOf(',' + attrName + ',') !== -1) {
1110        // the value of an included boolean attribute is typically an empty
1111        // string ('') which would equal false if we just check for a false value.
1112        // we also don't want support bad code like autoplay='false'
1113        attrVal = attrVal !== null ? true : false;
1114      }
1115
1116      obj[attrName] = attrVal;
1117    }
1118  }
1119
1120  return obj;
1121}
1122
1123/**
1124 * Get the value of an element's attribute
1125 *
1126 * @param {Element} el
1127 *        A DOM element
1128 *
1129 * @param {string} attribute
1130 *        Attribute to get the value of
1131 *
1132 * @return {string}
1133 *         value of the attribute
1134 */
1135function getAttribute(el, attribute) {
1136  return el.getAttribute(attribute);
1137}
1138
1139/**
1140 * Set the value of an element's attribute
1141 *
1142 * @param {Element} el
1143 *        A DOM element
1144 *
1145 * @param {string} attribute
1146 *        Attribute to set
1147 *
1148 * @param {string} value
1149 *        Value to set the attribute to
1150 */
1151function setAttribute(el, attribute, value) {
1152  el.setAttribute(attribute, value);
1153}
1154
1155/**
1156 * Remove an element's attribute
1157 *
1158 * @param {Element} el
1159 *        A DOM element
1160 *
1161 * @param {string} attribute
1162 *        Attribute to remove
1163 */
1164function removeAttribute(el, attribute) {
1165  el.removeAttribute(attribute);
1166}
1167
1168/**
1169 * Attempt to block the ability to select text while dragging controls
1170 */
1171function blockTextSelection() {
1172  document_1.body.focus();
1173  document_1.onselectstart = function () {
1174    return false;
1175  };
1176}
1177
1178/**
1179 * Turn off text selection blocking
1180 */
1181function unblockTextSelection() {
1182  document_1.onselectstart = function () {
1183    return true;
1184  };
1185}
1186
1187/**
1188 * Identical to the native `getBoundingClientRect` function, but ensures that
1189 * the method is supported at all (it is in all browsers we claim to support)
1190 * and that the element is in the DOM before continuing.
1191 *
1192 * This wrapper function also shims properties which are not provided by some
1193 * older browsers (namely, IE8).
1194 *
1195 * Additionally, some browsers do not support adding properties to a
1196 * `ClientRect`/`DOMRect` object; so, we shallow-copy it with the standard
1197 * properties (except `x` and `y` which are not widely supported). This helps
1198 * avoid implementations where keys are non-enumerable.
1199 *
1200 * @param  {Element} el
1201 *         Element whose `ClientRect` we want to calculate.
1202 *
1203 * @return {Object|undefined}
1204 *         Always returns a plain
1205 */
1206function getBoundingClientRect(el) {
1207  if (el && el.getBoundingClientRect && el.parentNode) {
1208    var rect = el.getBoundingClientRect();
1209    var result = {};
1210
1211    ['bottom', 'height', 'left', 'right', 'top', 'width'].forEach(function (k) {
1212      if (rect[k] !== undefined) {
1213        result[k] = rect[k];
1214      }
1215    });
1216
1217    if (!result.height) {
1218      result.height = parseFloat(computedStyle(el, 'height'));
1219    }
1220
1221    if (!result.width) {
1222      result.width = parseFloat(computedStyle(el, 'width'));
1223    }
1224
1225    return result;
1226  }
1227}
1228
1229/**
1230 * The postion of a DOM element on the page.
1231 *
1232 * @typedef {Object} module:dom~Position
1233 *
1234 * @property {number} left
1235 *           Pixels to the left
1236 *
1237 * @property {number} top
1238 *           Pixels on top
1239 */
1240
vendor: 4,060 bytes, lines 1241-1395
1241/**
1242 * Offset Left.
1243 * getBoundingClientRect technique from
1244 * John Resig
1245 *
1246 * @see http://ejohn.org/blog/getboundingclientrect-is-awesome/
1247 *
1248 * @param {Element} el
1249 *        Element from which to get offset
1250 *
1251 * @return {module:dom~Position}
1252 *         The position of the element that was passed in.
1253 */
1254function findPosition(el) {
1255  var box = void 0;
1256
1257  if (el.getBoundingClientRect && el.parentNode) {
1258    box = el.getBoundingClientRect();
1259  }
1260
1261  if (!box) {
1262    return {
1263      left: 0,
1264      top: 0
1265    };
1266  }
1267
1268  var docEl = document_1.documentElement;
1269  var body = document_1.body;
1270
1271  var clientLeft = docEl.clientLeft || body.clientLeft || 0;
1272  var scrollLeft = window_1.pageXOffset || body.scrollLeft;
1273  var left = box.left + scrollLeft - clientLeft;
1274
1275  var clientTop = docEl.clientTop || body.clientTop || 0;
1276  var scrollTop = window_1.pageYOffset || body.scrollTop;
1277  var top = box.top + scrollTop - clientTop;
1278
1279  // Android sometimes returns slightly off decimal values, so need to round
1280  return {
1281    left: Math.round(left),
1282    top: Math.round(top)
1283  };
1284}
1285
1286/**
1287 * x and y coordinates for a dom element or mouse pointer
1288 *
1289 * @typedef {Object} Dom~Coordinates
1290 *
1291 * @property {number} x
1292 *           x coordinate in pixels
1293 *
1294 * @property {number} y
1295 *           y coordinate in pixels
1296 */
1297
1298/**
1299 * Get pointer position in element
1300 * Returns an object with x and y coordinates.
1301 * The base on the coordinates are the bottom left of the element.
1302 *
1303 * @param {Element} el
1304 *        Element on which to get the pointer position on
1305 *
1306 * @param {EventTarget~Event} event
1307 *        Event object
1308 *
1309 * @return {Dom~Coordinates}
1310 *         A Coordinates object corresponding to the mouse position.
1311 *
1312 */
1313function getPointerPosition(el, event) {
1314  var position = {};
1315  var box = findPosition(el);
1316  var boxW = el.offsetWidth;
1317  var boxH = el.offsetHeight;
1318
1319  var boxY = box.top;
1320  var boxX = box.left;
1321  var pageY = event.pageY;
1322  var pageX = event.pageX;
1323
1324  if (event.changedTouches) {
1325    pageX = event.changedTouches[0].pageX;
1326    pageY = event.changedTouches[0].pageY;
1327  }
1328
1329  position.y = Math.max(0, Math.min(1, (boxY - pageY + boxH) / boxH));
1330  position.x = Math.max(0, Math.min(1, (pageX - boxX) / boxW));
1331
1332  return position;
1333}
1334
1335/**
1336 * Determines, via duck typing, whether or not a value is a text node.
1337 *
1338 * @param {Mixed} value
1339 *        Check if this value is a text node.
1340 *
1341 * @return {boolean}
1342 *         - True if it is a text node
1343 *         - False otherwise
1344 */
1345function isTextNode(value) {
1346  return isObject(value) && value.nodeType === 3;
1347}
1348
1349/**
1350 * Empties the contents of an element.
1351 *
1352 * @param {Element} el
1353 *        The element to empty children from
1354 *
1355 * @return {Element}
1356 *         The element with no children
1357 */
1358function emptyEl(el) {
1359  while (el.firstChild) {
1360    el.removeChild(el.firstChild);
1361  }
1362  return el;
1363}
1364
1365/**
1366 * Normalizes content for eventual insertion into the DOM.
1367 *
1368 * This allows a wide range of content definition methods, but protects
1369 * from falling into the trap of simply writing to `innerHTML`, which is
1370 * an XSS concern.
1371 *
1372 * The content for an element can be passed in multiple types and
1373 * combinations, whose behavior is as follows:
1374 *
1375 * @param {String|Element|TextNode|Array|Function} content
1376 *        - String: Normalized into a text node.
1377 *        - Element/TextNode: Passed through.
1378 *        - Array: A one-dimensional array of strings, elements, nodes, or functions
1379 *          (which return single strings, elements, or nodes).
1380 *        - Function: If the sole argument, is expected to produce a string, element,
1381 *          node, or array as defined above.
1382 *
1383 * @return {Array}
1384 *         All of the content that was passed in normalized.
1385 */
1386function normalizeContent(content) {
1387
1388  // First, invoke content if it is a function. If it produces an array,
1389  // that needs to happen before normalization.
1390  if (typeof content === 'function') {
1391    content = content();
1392  }
1393
1394  // Next up, normalize to an array, so one or many items can be normalized,
1395  // filtered, and returned.
vendor: 4,108 bytes, lines 1396-1536
1396  return (Array.isArray(content) ? content : [content]).map(function (value) {
1397
1398    // First, invoke value if it is a function to produce a new value,
1399    // which will be subsequently normalized to a Node of some kind.
1400    if (typeof value === 'function') {
1401      value = value();
1402    }
1403
1404    if (isEl(value) || isTextNode(value)) {
1405      return value;
1406    }
1407
1408    if (typeof value === 'string' && /\S/.test(value)) {
1409      return document_1.createTextNode(value);
1410    }
1411  }).filter(function (value) {
1412    return value;
1413  });
1414}
1415
1416/**
1417 * Normalizes and appends content to an element.
1418 *
1419 * @param {Element} el
1420 *        Element to append normalized content to.
1421 *
1422 *
1423 * @param {String|Element|TextNode|Array|Function} content
1424 *        See the `content` argument of {@link dom:normalizeContent}
1425 *
1426 * @return {Element}
1427 *         The element with appended normalized content.
1428 */
1429function appendContent(el, content) {
1430  normalizeContent(content).forEach(function (node) {
1431    return el.appendChild(node);
1432  });
1433  return el;
1434}
1435
1436/**
1437 * Normalizes and inserts content into an element; this is identical to
1438 * `appendContent()`, except it empties the element first.
1439 *
1440 * @param {Element} el
1441 *        Element to insert normalized content into.
1442 *
1443 * @param {String|Element|TextNode|Array|Function} content
1444 *        See the `content` argument of {@link dom:normalizeContent}
1445 *
1446 * @return {Element}
1447 *         The element with inserted normalized content.
1448 *
1449 */
1450function insertContent(el, content) {
1451  return appendContent(emptyEl(el), content);
1452}
1453
1454/**
1455 * Check if event was a single left click
1456 *
1457 * @param {EventTarget~Event} event
1458 *        Event object
1459 *
1460 * @return {boolean}
1461 *         - True if a left click
1462 *         - False if not a left click
1463 */
1464function isSingleLeftClick(event) {
1465  // Note: if you create something draggable, be sure to
1466  // call it on both `mousedown` and `mousemove` event,
1467  // otherwise `mousedown` should be enough for a button
1468
1469  if (event.button === undefined && event.buttons === undefined) {
1470    // Why do we need `buttons` ?
1471    // Because, middle mouse sometimes have this:
1472    // e.button === 0 and e.buttons === 4
1473    // Furthermore, we want to prevent combination click, something like
1474    // HOLD middlemouse then left click, that would be
1475    // e.button === 0, e.buttons === 5
1476    // just `button` is not gonna work
1477
1478    // Alright, then what this block does ?
1479    // this is for chrome `simulate mobile devices`
1480    // I want to support this as well
1481
1482    return true;
1483  }
1484
1485  if (event.button === 0 && event.buttons === undefined) {
1486    // Touch screen, sometimes on some specific device, `buttons`
1487    // doesn't have anything (safari on ios, blackberry...)
1488
1489    return true;
1490  }
1491
1492  if (IE_VERSION === 9) {
1493    // Ignore IE9
1494
1495    return true;
1496  }
1497
1498  if (event.button !== 0 || event.buttons !== 1) {
1499    // This is the reason we have those if else block above
1500    // if any special case we can catch and let it slide
1501    // we do it above, when get to here, this definitely
1502    // is-not-left-click
1503
1504    return false;
1505  }
1506
1507  return true;
1508}
1509
1510/**
1511 * Finds a single DOM element matching `selector` within the optional
1512 * `context` of another DOM element (defaulting to `document`).
1513 *
1514 * @param {string} selector
1515 *        A valid CSS selector, which will be passed to `querySelector`.
1516 *
1517 * @param {Element|String} [context=document]
1518 *        A DOM element within which to query. Can also be a selector
1519 *        string in which case the first matching element will be used
1520 *        as context. If missing (or no element matches selector), falls
1521 *        back to `document`.
1522 *
1523 * @return {Element|null}
1524 *         The element that was found or null.
1525 */
1526var $ = createQuerier('querySelector');
1527
1528/**
1529 * Finds a all DOM elements matching `selector` within the optional
1530 * `context` of another DOM element (defaulting to `document`).
1531 *
1532 * @param {string} selector
1533 *           A valid CSS selector, which will be passed to `querySelectorAll`.
1534 *
1535 * @param {Element|String} [context=document]
1536 *           A DOM element within which to query. Can also be a selector
vendor: 5,002 bytes, lines 1537-1751
1537 *           string in which case the first matching element will be used
1538 *           as context. If missing (or no element matches selector), falls
1539 *           back to `document`.
1540 *
1541 * @return {NodeList}
1542 *         A element list of elements that were found. Will be empty if none were found.
1543 *
1544 */
1545var $$ = createQuerier('querySelectorAll');
1546
1547
1548
1549var Dom = (Object.freeze || Object)({
1550	isReal: isReal,
1551	isEl: isEl,
1552	isInFrame: isInFrame,
1553	createEl: createEl,
1554	textContent: textContent,
1555	prependTo: prependTo,
1556	hasClass: hasClass,
1557	addClass: addClass,
1558	removeClass: removeClass,
1559	toggleClass: toggleClass,
1560	setAttributes: setAttributes,
1561	getAttributes: getAttributes,
1562	getAttribute: getAttribute,
1563	setAttribute: setAttribute,
1564	removeAttribute: removeAttribute,
1565	blockTextSelection: blockTextSelection,
1566	unblockTextSelection: unblockTextSelection,
1567	getBoundingClientRect: getBoundingClientRect,
1568	findPosition: findPosition,
1569	getPointerPosition: getPointerPosition,
1570	isTextNode: isTextNode,
1571	emptyEl: emptyEl,
1572	normalizeContent: normalizeContent,
1573	appendContent: appendContent,
1574	insertContent: insertContent,
1575	isSingleLeftClick: isSingleLeftClick,
1576	$: $,
1577	$$: $$
1578});
1579
1580/**
1581 * @file guid.js
1582 * @module guid
1583 */
1584
1585/**
1586 * Unique ID for an element or function
1587 * @type {Number}
1588 */
1589var _guid = 1;
1590
1591/**
1592 * Get a unique auto-incrementing ID by number that has not been returned before.
1593 *
1594 * @return {number}
1595 *         A new unique ID.
1596 */
1597function newGUID() {
1598  return _guid++;
1599}
1600
1601/**
1602 * @file dom-data.js
1603 * @module dom-data
1604 */
1605/**
1606 * Element Data Store.
1607 *
1608 * Allows for binding data to an element without putting it directly on the
1609 * element. Ex. Event listeners are stored here.
1610 * (also from jsninja.com, slightly modified and updated for closure compiler)
1611 *
1612 * @type {Object}
1613 * @private
1614 */
1615var elData = {};
1616
1617/*
1618 * Unique attribute name to store an element's guid in
1619 *
1620 * @type {String}
1621 * @constant
1622 * @private
1623 */
1624var elIdAttr = 'vdata' + new Date().getTime();
1625
1626/**
1627 * Returns the cache object where data for an element is stored
1628 *
1629 * @param {Element} el
1630 *        Element to store data for.
1631 *
1632 * @return {Object}
1633 *         The cache object for that el that was passed in.
1634 */
1635function getData(el) {
1636  var id = el[elIdAttr];
1637
1638  if (!id) {
1639    id = el[elIdAttr] = newGUID();
1640  }
1641
1642  if (!elData[id]) {
1643    elData[id] = {};
1644  }
1645
1646  return elData[id];
1647}
1648
1649/**
1650 * Returns whether or not an element has cached data
1651 *
1652 * @param {Element} el
1653 *        Check if this element has cached data.
1654 *
1655 * @return {boolean}
1656 *         - True if the DOM element has cached data.
1657 *         - False otherwise.
1658 */
1659function hasData(el) {
1660  var id = el[elIdAttr];
1661
1662  if (!id) {
1663    return false;
1664  }
1665
1666  return !!Object.getOwnPropertyNames(elData[id]).length;
1667}
1668
1669/**
1670 * Delete data for the element from the cache and the guid attr from getElementById
1671 *
1672 * @param {Element} el
1673 *        Remove cached data for this element.
1674 */
1675function removeData(el) {
1676  var id = el[elIdAttr];
1677
1678  if (!id) {
1679    return;
1680  }
1681
1682  // Remove all stored data
1683  delete elData[id];
1684
1685  // Remove the elIdAttr property from the DOM node
1686  try {
1687    delete el[elIdAttr];
1688  } catch (e) {
1689    if (el.removeAttribute) {
1690      el.removeAttribute(elIdAttr);
1691    } else {
1692      // IE doesn't appear to support removeAttribute on the document element
1693      el[elIdAttr] = null;
1694    }
1695  }
1696}
1697
1698/**
1699 * @file events.js. An Event System (John Resig - Secrets of a JS Ninja http://jsninja.com/)
1700 * (Original book version wasn't completely usable, so fixed some things and made Closure Compiler compatible)
1701 * This should work very similarly to jQuery's events, however it's based off the book version which isn't as
1702 * robust as jquery's, so there's probably some differences.
1703 *
1704 * @module events
1705 */
1706
1707/**
1708 * Clean up the listener cache and dispatchers
1709 *
1710 * @param {Element|Object} elem
1711 *        Element to clean up
1712 *
1713 * @param {string} type
1714 *        Type of event to clean up
1715 */
1716function _cleanUpEvents(elem, type) {
1717  var data = getData(elem);
1718
1719  // Remove the events of a particular type if there are none left
1720  if (data.handlers[type].length === 0) {
1721    delete data.handlers[type];
1722    // data.handlers[type] = null;
1723    // Setting to null was causing an error with data.handlers
1724
1725    // Remove the meta-handler from the element
1726    if (elem.removeEventListener) {
1727      elem.removeEventListener(type, data.dispatcher, false);
1728    } else if (elem.detachEvent) {
1729      elem.detachEvent('on' + type, data.dispatcher);
1730    }
1731  }
1732
1733  // Remove the events object if there are no types left
1734  if (Object.getOwnPropertyNames(data.handlers).length <= 0) {
1735    delete data.handlers;
1736    delete data.dispatcher;
1737    delete data.disabled;
1738  }
1739
1740  // Finally remove the element data if there is no data left
1741  if (Object.getOwnPropertyNames(data).length === 0) {
1742    removeData(elem);
1743  }
1744}
1745
1746/**
1747 * Loops through an array of event types and calls the requested method for each type.
1748 *
1749 * @param {Function} fn
1750 *        The event method we want to use.
1751 *
vendor: 4,387 bytes, lines 1752-1886
1752 * @param {Element|Object} elem
1753 *        Element or object to bind listeners to
1754 *
1755 * @param {string} type
1756 *        Type of event to bind to.
1757 *
1758 * @param {EventTarget~EventListener} callback
1759 *        Event listener.
1760 */
1761function _handleMultipleEvents(fn, elem, types, callback) {
1762  types.forEach(function (type) {
1763    // Call the event method for each one of the types
1764    fn(elem, type, callback);
1765  });
1766}
1767
1768/**
1769 * Fix a native event to have standard property values
1770 *
1771 * @param {Object} event
1772 *        Event object to fix.
1773 *
1774 * @return {Object}
1775 *         Fixed event object.
1776 */
1777function fixEvent(event) {
1778
1779  function returnTrue() {
1780    return true;
1781  }
1782
1783  function returnFalse() {
1784    return false;
1785  }
1786
1787  // Test if fixing up is needed
1788  // Used to check if !event.stopPropagation instead of isPropagationStopped
1789  // But native events return true for stopPropagation, but don't have
1790  // other expected methods like isPropagationStopped. Seems to be a problem
1791  // with the Javascript Ninja code. So we're just overriding all events now.
1792  if (!event || !event.isPropagationStopped) {
1793    var old = event || window_1.event;
1794
1795    event = {};
1796    // Clone the old object so that we can modify the values event = {};
1797    // IE8 Doesn't like when you mess with native event properties
1798    // Firefox returns false for event.hasOwnProperty('type') and other props
1799    //  which makes copying more difficult.
1800    // TODO: Probably best to create a whitelist of event props
1801    for (var key in old) {
1802      // Safari 6.0.3 warns you if you try to copy deprecated layerX/Y
1803      // Chrome warns you if you try to copy deprecated keyboardEvent.keyLocation
1804      // and webkitMovementX/Y
1805      if (key !== 'layerX' && key !== 'layerY' && key !== 'keyLocation' && key !== 'webkitMovementX' && key !== 'webkitMovementY') {
1806        // Chrome 32+ warns if you try to copy deprecated returnValue, but
1807        // we still want to if preventDefault isn't supported (IE8).
1808        if (!(key === 'returnValue' && old.preventDefault)) {
1809          event[key] = old[key];
1810        }
1811      }
1812    }
1813
1814    // The event occurred on this element
1815    if (!event.target) {
1816      event.target = event.srcElement || document_1;
1817    }
1818
1819    // Handle which other element the event is related to
1820    if (!event.relatedTarget) {
1821      event.relatedTarget = event.fromElement === event.target ? event.toElement : event.fromElement;
1822    }
1823
1824    // Stop the default browser action
1825    event.preventDefault = function () {
1826      if (old.preventDefault) {
1827        old.preventDefault();
1828      }
1829      event.returnValue = false;
1830      old.returnValue = false;
1831      event.defaultPrevented = true;
1832    };
1833
1834    event.defaultPrevented = false;
1835
1836    // Stop the event from bubbling
1837    event.stopPropagation = function () {
1838      if (old.stopPropagation) {
1839        old.stopPropagation();
1840      }
1841      event.cancelBubble = true;
1842      old.cancelBubble = true;
1843      event.isPropagationStopped = returnTrue;
1844    };
1845
1846    event.isPropagationStopped = returnFalse;
1847
1848    // Stop the event from bubbling and executing other handlers
1849    event.stopImmediatePropagation = function () {
1850      if (old.stopImmediatePropagation) {
1851        old.stopImmediatePropagation();
1852      }
1853      event.isImmediatePropagationStopped = returnTrue;
1854      event.stopPropagation();
1855    };
1856
1857    event.isImmediatePropagationStopped = returnFalse;
1858
1859    // Handle mouse position
1860    if (event.clientX !== null && event.clientX !== undefined) {
1861      var doc = document_1.documentElement;
1862      var body = document_1.body;
1863
1864      event.pageX = event.clientX + (doc && doc.scrollLeft || body && body.scrollLeft || 0) - (doc && doc.clientLeft || body && body.clientLeft || 0);
1865      event.pageY = event.clientY + (doc && doc.scrollTop || body && body.scrollTop || 0) - (doc && doc.clientTop || body && body.clientTop || 0);
1866    }
1867
1868    // Handle key presses
1869    event.which = event.charCode || event.keyCode;
1870
1871    // Fix button for mouse clicks:
1872    // 0 == left; 1 == middle; 2 == right
1873    if (event.button !== null && event.button !== undefined) {
1874
1875      // The following is disabled because it does not pass videojs-standard
1876      // and... yikes.
1877      /* eslint-disable */
1878      event.button = event.button & 1 ? 0 : event.button & 4 ? 1 : event.button & 2 ? 2 : 0;
1879      /* eslint-enable */
1880    }
1881  }
1882
1883  // Returns fixed-up instance
1884  return event;
1885}
1886
vendor: 4,088 bytes, lines 1887-2068
1887/**
1888 * Whether passive event listeners are supported
1889 */
1890var _supportsPassive = false;
1891
1892(function () {
1893  try {
1894    var opts = Object.defineProperty({}, 'passive', {
1895      get: function get() {
1896        _supportsPassive = true;
1897      }
1898    });
1899
1900    window_1.addEventListener('test', null, opts);
1901    window_1.removeEventListener('test', null, opts);
1902  } catch (e) {
1903    // disregard
1904  }
1905})();
1906
1907/**
1908 * Touch events Chrome expects to be passive
1909 */
1910var passiveEvents = ['touchstart', 'touchmove'];
1911
1912/**
1913 * Add an event listener to element
1914 * It stores the handler function in a separate cache object
1915 * and adds a generic handler to the element's event,
1916 * along with a unique id (guid) to the element.
1917 *
1918 * @param {Element|Object} elem
1919 *        Element or object to bind listeners to
1920 *
1921 * @param {string|string[]} type
1922 *        Type of event to bind to.
1923 *
1924 * @param {EventTarget~EventListener} fn
1925 *        Event listener.
1926 */
1927function on(elem, type, fn) {
1928  if (Array.isArray(type)) {
1929    return _handleMultipleEvents(on, elem, type, fn);
1930  }
1931
1932  var data = getData(elem);
1933
1934  // We need a place to store all our handler data
1935  if (!data.handlers) {
1936    data.handlers = {};
1937  }
1938
1939  if (!data.handlers[type]) {
1940    data.handlers[type] = [];
1941  }
1942
1943  if (!fn.guid) {
1944    fn.guid = newGUID();
1945  }
1946
1947  data.handlers[type].push(fn);
1948
1949  if (!data.dispatcher) {
1950    data.disabled = false;
1951
1952    data.dispatcher = function (event, hash) {
1953
1954      if (data.disabled) {
1955        return;
1956      }
1957
1958      event = fixEvent(event);
1959
1960      var handlers = data.handlers[event.type];
1961
1962      if (handlers) {
1963        // Copy handlers so if handlers are added/removed during the process it doesn't throw everything off.
1964        var handlersCopy = handlers.slice(0);
1965
1966        for (var m = 0, n = handlersCopy.length; m < n; m++) {
1967          if (event.isImmediatePropagationStopped()) {
1968            break;
1969          } else {
1970            try {
1971              handlersCopy[m].call(elem, event, hash);
1972            } catch (e) {
1973              log$1.error(e);
1974            }
1975          }
1976        }
1977      }
1978    };
1979  }
1980
1981  if (data.handlers[type].length === 1) {
1982    if (elem.addEventListener) {
1983      var options = false;
1984
1985      if (_supportsPassive && passiveEvents.indexOf(type) > -1) {
1986        options = { passive: true };
1987      }
1988      elem.addEventListener(type, data.dispatcher, options);
1989    } else if (elem.attachEvent) {
1990      elem.attachEvent('on' + type, data.dispatcher);
1991    }
1992  }
1993}
1994
1995/**
1996 * Removes event listeners from an element
1997 *
1998 * @param {Element|Object} elem
1999 *        Object to remove listeners from.
2000 *
2001 * @param {string|string[]} [type]
2002 *        Type of listener to remove. Don't include to remove all events from element.
2003 *
2004 * @param {EventTarget~EventListener} [fn]
2005 *        Specific listener to remove. Don't include to remove listeners for an event
2006 *        type.
2007 */
2008function off(elem, type, fn) {
2009  // Don't want to add a cache object through getElData if not needed
2010  if (!hasData(elem)) {
2011    return;
2012  }
2013
2014  var data = getData(elem);
2015
2016  // If no events exist, nothing to unbind
2017  if (!data.handlers) {
2018    return;
2019  }
2020
2021  if (Array.isArray(type)) {
2022    return _handleMultipleEvents(off, elem, type, fn);
2023  }
2024
2025  // Utility function
2026  var removeType = function removeType(el, t) {
2027    data.handlers[t] = [];
2028    _cleanUpEvents(el, t);
2029  };
2030
2031  // Are we removing all bound events?
2032  if (type === undefined) {
2033    for (var t in data.handlers) {
2034      if (Object.prototype.hasOwnProperty.call(data.handlers || {}, t)) {
2035        removeType(elem, t);
2036      }
2037    }
2038    return;
2039  }
2040
2041  var handlers = data.handlers[type];
2042
2043  // If no handlers exist, nothing to unbind
2044  if (!handlers) {
2045    return;
2046  }
2047
2048  // If no listener was provided, remove all listeners for type
2049  if (!fn) {
2050    removeType(elem, type);
2051    return;
2052  }
2053
2054  // We're only removing a single handler
2055  if (fn.guid) {
2056    for (var n = 0; n < handlers.length; n++) {
2057      if (handlers[n].guid === fn.guid) {
2058        handlers.splice(n--, 1);
2059      }
2060    }
2061  }
2062
2063  _cleanUpEvents(elem, type);
2064}
2065
2066/**
2067 * Trigger an event for an element
2068 *
2069 * @param {Element|Object} elem
2070 *        Element to trigger an event on
2071 *
2072 * @param {EventTarget~Event|string} event
2073 *        A string (the type) or an event object with a type attribute
2074 *
2075 * @param {Object} [hash]
2076 *        data hash to pass along with the event
2077 *
2078 * @return {boolean|undefined}
2079 *         - Returns the opposite of `defaultPrevented` if default was prevented
2080 *         - Otherwise returns undefined
2081 */
2082function trigger(elem, event, hash) {
2083  // Fetches element data and a reference to the parent (for bubbling).
2084  // Don't want to add a data object to cache for every parent,
2085  // so checking hasElData first.
2086  var elemData = hasData(elem) ? getData(elem) : {};
2087  var parent = elem.parentNode || elem.ownerDocument;
2088  // type = event.type || event,
2089  // handler;
2090
2091  // If an event name was passed as a string, creates an event out of it
2092  if (typeof event === 'string') {
2093    event = { type: event, target: elem };
2094  } else if (!event.target) {
2095    event.target = elem;
2096  }
2097
2098  // Normalizes the event properties.
2099  event = fixEvent(event);
2100
2101  // If the passed element has a dispatcher, executes the established handlers.
2102  if (elemData.dispatcher) {
2103    elemData.dispatcher.call(elem, event, hash);
2104  }
2105
2106  // Unless explicitly stopped or the event does not bubble (e.g. media events)
2107  // recursively calls this function to bubble the event up the DOM.
2108  if (parent && !event.isPropagationStopped() && event.bubbles === true) {
2109    trigger.call(null, parent, event, hash);
2110
2111    // If at the top of the DOM, triggers the default action unless disabled.
2112  } else if (!parent && !event.defaultPrevented) {
2113    var targetData = getData(event.target);
2114
2115    // Checks if the target has a default action for this event.
2116    if (event.target[event.type]) {
2117      // Temporarily disables event dispatching on the target as we have already executed the handler.
2118      targetData.disabled = true;
2119      // Executes the default action.
2120      if (typeof event.target[event.type] === 'function') {
2121        event.target[event.type]();
2122      }
2123      // Re-enables event dispatching.
2124      targetData.disabled = false;
2125    }
2126  }
2127
2128  // Inform the triggerer if the default was prevented by returning false
2129  return !event.defaultPrevented;
2130}
2131
2132/**
2133 * Trigger a listener only once for an event
2134 *
2135 * @param {Element|Object} elem
2136 *        Element or object to bind to.
2137 *
2138 * @param {string|string[]} type
2139 *        Name/type of event
2140 *
2141 * @param {Event~EventListener} fn
2142 *        Event Listener function
2143 */
2144function one(elem, type, fn) {
2145  if (Array.isArray(type)) {
2146    return _handleMultipleEvents(one, elem, type, fn);
2147  }
2148  var func = function func() {
2149    off(elem, type, func);
2150    fn.apply(this, arguments);
2151  };
2152
2153  // copy the guid to the new function so it can removed using the original function's ID
2154  func.guid = fn.guid = fn.guid || newGUID();
2155  on(elem, type, func);
2156}
2157
2158var Events = (Object.freeze || Object)({
2159	fixEvent: fixEvent,
2160	on: on,
2161	off: off,
2162	trigger: trigger,
2163	one: one
2164});
2165
2166/**
2167 * @file setup.js - Functions for setting up a player without
2168 * user interaction based on the data-setup `attribute` of the video tag.
2169 *
2170 * @module setup
2171 */
2172var _windowLoaded = false;
2173var videojs$2 = void 0;
2174
2175/**
2176 * Set up any tags that have a data-setup `attribute` when the player is started.
2177 */
2178var autoSetup = function autoSetup() {
2179
2180  // Protect against breakage in non-browser environments and check global autoSetup option.
2181  if (!isReal() || videojs$2.options.autoSetup === false) {
2182    return;
2183  }
2184
2185  // One day, when we stop supporting IE8, go back to this, but in the meantime...*hack hack hack*
2186  // var vids = Array.prototype.slice.call(document.getElementsByTagName('video'));
2187  // var audios = Array.prototype.slice.call(document.getElementsByTagName('audio'));
2188  // var mediaEls = vids.concat(audios);
2189
2190  // Because IE8 doesn't support calling slice on a node list, we need to loop
2191  // through each list of elements to build up a new, combined list of elements.
2192  var vids = document_1.getElementsByTagName('video');
2193  var audios = document_1.getElementsByTagName('audio');
2194  var divs = document_1.getElementsByTagName('video-js');
2195  var mediaEls = [];
2196
2197  if (vids && vids.length > 0) {
2198    for (var i = 0, e = vids.length; i < e; i++) {
2199      mediaEls.push(vids[i]);
2200    }
2201  }
2202
2203  if (audios && audios.length > 0) {
2204    for (var _i = 0, _e = audios.length; _i < _e; _i++) {
2205      mediaEls.push(audios[_i]);
2206    }
2207  }
2208
2209  if (divs && divs.length > 0) {
2210    for (var _i2 = 0, _e2 = divs.length; _i2 < _e2; _i2++) {
2211      mediaEls.push(divs[_i2]);
2212    }
2213  }
2214
2215  // Check if any media elements exist
2216  if (mediaEls && mediaEls.length > 0) {
2217
2218    for (var _i3 = 0, _e3 = mediaEls.length; _i3 < _e3; _i3++) {
2219      var mediaEl = mediaEls[_i3];
2220
2221      // Check if element exists, has getAttribute func.
2222      // IE seems to consider typeof el.getAttribute == 'object' instead of
2223      // 'function' like expected, at least when loading the player immediately.
2224      if (mediaEl && mediaEl.getAttribute) {
2225
2226        // Make sure this player hasn't already been set up.
2227        if (mediaEl.player === undefined) {
2228          var options = mediaEl.getAttribute('data-setup');
2229
2230          // Check if data-setup attr exists.
2231          // We only auto-setup if they've added the data-setup attr.
2232          if (options !== null) {
2233            // Create new video.js instance.
2234            videojs$2(mediaEl);
2235          }
2236        }
2237
2238        // If getAttribute isn't defined, we need to wait for the DOM.
2239      } else {
2240        autoSetupTimeout(1);
2241        break;
2242      }
2243    }
2244
2245    // No videos were found, so keep looping unless page is finished loading.
2246  } else if (!_windowLoaded) {
2247    autoSetupTimeout(1);
2248  }
2249};
2250
2251/**
2252 * Wait until the page is loaded before running autoSetup. This will be called in
2253 * autoSetup if `hasLoaded` returns false.
2254 *
2255 * @param {number} wait
2256 *        How long to wait in ms
2257 *
2258 * @param {module:videojs} [vjs]
2259 *        The videojs library function
2260 */
2261function autoSetupTimeout(wait, vjs) {
2262  if (vjs) {
2263    videojs$2 = vjs;
2264  }
2265
2266  window_1.setTimeout(autoSetup, wait);
2267}
2268
2269if (isReal() && document_1.readyState === 'complete') {
2270  _windowLoaded = true;
2271} else {
2272  /**
2273   * Listen for the load event on window, and set _windowLoaded to true.
2274   *
2275   * @listens load
2276   */
2277  one(window_1, 'load', function () {
2278    _windowLoaded = true;
2279  });
2280}
2281
2282/**
2283 * @file stylesheet.js
2284 * @module stylesheet
2285 */
2286/**
2287 * Create a DOM syle element given a className for it.
2288 *
2289 * @param {string} className
2290 *        The className to add to the created style element.
2291 *
2292 * @return {Element}
2293 *         The element that was created.
2294 */
2295var createStyleElement = function createStyleElement(className) {
2296  var style = document_1.createElement('style');
2297
2298  style.className = className;
2299
2300  return style;
2301};
2302
2303/**
2304 * Add text to a DOM element.
2305 *
2306 * @param {Element} el
2307 *        The Element to add text content to.
2308 *
2309 * @param {string} content
2310 *        The text to add to the element.
2311 */
2312var setTextContent = function setTextContent(el, content) {
2313  if (el.styleSheet) {
2314    el.styleSheet.cssText = content;
2315  } else {
2316    el.textContent = content;
2317  }
2318};
2319
2320/**
2321 * @file fn.js
2322 * @module fn
2323 */
2324/**
2325 * Bind (a.k.a proxy or Context). A simple method for changing the context of a function
2326 * It also stores a unique id on the function so it can be easily removed from events.
2327 *
2328 * @param {Mixed} context
2329 *        The object to bind as scope.
2330 *
2331 * @param {Function} fn
2332 *        The function to be bound to a scope.
2333 *
2334 * @param {number} [uid]
2335 *        An optional unique ID for the function to be set
2336 *
2337 * @return {Function}
2338 *         The new function that will be bound into the context given
2339 */
2340var bind = function bind(context, fn, uid) {
2341  // Make sure the function has a unique ID
2342  if (!fn.guid) {
2343    fn.guid = newGUID();
2344  }
2345
2346  // Create the new function that changes the context
2347  var bound = function bound() {
2348    return fn.apply(context, arguments);
2349  };
2350
2351  // Allow for the ability to individualize this function
2352  // Needed in the case where multiple objects might share the same prototype
2353  // IF both items add an event listener with the same function, then you try to remove just one
2354  // it will remove both because they both have the same guid.
2355  // when using this, you need to use the bind method when you remove the listener as well.
2356  // currently used in text tracks
2357  bound.guid = uid ? uid + '_' + fn.guid : fn.guid;
2358
2359  return bound;
2360};
2361
2362/**
2363 * Wraps the given function, `fn`, with a new function that only invokes `fn`
2364 * at most once per every `wait` milliseconds.
2365 *
2366 * @param  {Function} fn
2367 *         The function to be throttled.
2368 *
2369 * @param  {Number}   wait
2370 *         The number of milliseconds by which to throttle.
2371 *
2372 * @return {Function}
2373 */
2374var throttle = function throttle(fn, wait) {
2375  var last = Date.now();
2376
2377  var throttled = function throttled() {
2378    var now = Date.now();
2379
2380    if (now - last >= wait) {
2381      fn.apply(undefined, arguments);
2382      last = now;
2383    }
2384  };
2385
2386  return throttled;
2387};
2388
2389/**
2390 * Creates a debounced function that delays invoking `func` until after `wait`
2391 * milliseconds have elapsed since the last time the debounced function was
2392 * invoked.
2393 *
2394 * Inspired by lodash and underscore implementations.
2395 *
2396 * @param  {Function} func
2397 *         The function to wrap with debounce behavior.
2398 *
2399 * @param  {number} wait
2400 *         The number of milliseconds to wait after the last invocation.
2401 *
2402 * @param  {boolean} [immediate]
2403 *         Whether or not to invoke the function immediately upon creation.
2404 *
2405 * @param  {Object} [context=window]
2406 *         The "context" in which the debounced function should debounce. For
2407 *         example, if this function should be tied to a Video.js player,
2408 *         the player can be passed here. Alternatively, defaults to the
2409 *         global `window` object.
2410 *
2411 * @return {Function}
2412 *         A debounced function.
2413 */
2414var debounce = function debounce(func, wait, immediate) {
2415  var context = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : window_1;
2416
2417  var timeout = void 0;
2418
2419  /* eslint-disable consistent-this */
2420  return function () {
2421    var self = this;
2422    var args = arguments;
2423
2424    var _later = function later() {
2425      timeout = null;
2426      _later = null;
2427      if (!immediate) {
2428        func.apply(self, args);
2429      }
2430    };
2431
2432    if (!timeout && immediate) {
2433      func.apply(self, args);
2434    }
2435
2436    context.clearTimeout(timeout);
2437    timeout = context.setTimeout(_later, wait);
2438  };
2439  /* eslint-enable consistent-this */
2440};
2441
2442/**
2443 * @file src/js/event-target.js
2444 */
2445/**
2446 * `EventTarget` is a class that can have the same API as the DOM `EventTarget`. It
2447 * adds shorthand functions that wrap around lengthy functions. For example:
2448 * the `on` function is a wrapper around `addEventListener`.
2449 *
2450 * @see [EventTarget Spec]{@link https://www.w3.org/TR/DOM-Level-2-Events/events.html#Events-EventTarget}
2451 * @class EventTarget
2452 */
2453var EventTarget = function EventTarget() {};
2454
2455/**
2456 * A Custom DOM event.
2457 *
2458 * @typedef {Object} EventTarget~Event
2459 * @see [Properties]{@link https://developer.mozilla.org/en-US/docs/Web/API/CustomEvent}
2460 */
2461
2462/**
2463 * All event listeners should follow the following format.
2464 *
2465 * @callback EventTarget~EventListener
2466 * @this {EventTarget}
2467 *
2468 * @param {EventTarget~Event} event
2469 *        the event that triggered this function
2470 *
2471 * @param {Object} [hash]
2472 *        hash of data sent during the event
2473 */
2474
2475/**
vendor: 8,182 bytes, lines 2476-2763
2476 * An object containing event names as keys and booleans as values.
2477 *
2478 * > NOTE: If an event name is set to a true value here {@link EventTarget#trigger}
2479 *         will have extra functionality. See that function for more information.
2480 *
2481 * @property EventTarget.prototype.allowedEvents_
2482 * @private
2483 */
2484EventTarget.prototype.allowedEvents_ = {};
2485
2486/**
2487 * Adds an `event listener` to an instance of an `EventTarget`. An `event listener` is a
2488 * function that will get called when an event with a certain name gets triggered.
2489 *
2490 * @param {string|string[]} type
2491 *        An event name or an array of event names.
2492 *
2493 * @param {EventTarget~EventListener} fn
2494 *        The function to call with `EventTarget`s
2495 */
2496EventTarget.prototype.on = function (type, fn) {
2497  // Remove the addEventListener alias before calling Events.on
2498  // so we don't get into an infinite type loop
2499  var ael = this.addEventListener;
2500
2501  this.addEventListener = function () {};
2502  on(this, type, fn);
2503  this.addEventListener = ael;
2504};
2505
2506/**
2507 * An alias of {@link EventTarget#on}. Allows `EventTarget` to mimic
2508 * the standard DOM API.
2509 *
2510 * @function
2511 * @see {@link EventTarget#on}
2512 */
2513EventTarget.prototype.addEventListener = EventTarget.prototype.on;
2514
2515/**
2516 * Removes an `event listener` for a specific event from an instance of `EventTarget`.
2517 * This makes it so that the `event listener` will no longer get called when the
2518 * named event happens.
2519 *
2520 * @param {string|string[]} type
2521 *        An event name or an array of event names.
2522 *
2523 * @param {EventTarget~EventListener} fn
2524 *        The function to remove.
2525 */
2526EventTarget.prototype.off = function (type, fn) {
2527  off(this, type, fn);
2528};
2529
2530/**
2531 * An alias of {@link EventTarget#off}. Allows `EventTarget` to mimic
2532 * the standard DOM API.
2533 *
2534 * @function
2535 * @see {@link EventTarget#off}
2536 */
2537EventTarget.prototype.removeEventListener = EventTarget.prototype.off;
2538
2539/**
2540 * This function will add an `event listener` that gets triggered only once. After the
2541 * first trigger it will get removed. This is like adding an `event listener`
2542 * with {@link EventTarget#on} that calls {@link EventTarget#off} on itself.
2543 *
2544 * @param {string|string[]} type
2545 *        An event name or an array of event names.
2546 *
2547 * @param {EventTarget~EventListener} fn
2548 *        The function to be called once for each event name.
2549 */
2550EventTarget.prototype.one = function (type, fn) {
2551  // Remove the addEventListener alialing Events.on
2552  // so we don't get into an infinite type loop
2553  var ael = this.addEventListener;
2554
2555  this.addEventListener = function () {};
2556  one(this, type, fn);
2557  this.addEventListener = ael;
2558};
2559
2560/**
2561 * This function causes an event to happen. This will then cause any `event listeners`
2562 * that are waiting for that event, to get called. If there are no `event listeners`
2563 * for an event then nothing will happen.
2564 *
2565 * If the name of the `Event` that is being triggered is in `EventTarget.allowedEvents_`.
2566 * Trigger will also call the `on` + `uppercaseEventName` function.
2567 *
2568 * Example:
2569 * 'click' is in `EventTarget.allowedEvents_`, so, trigger will attempt to call
2570 * `onClick` if it exists.
2571 *
2572 * @param {string|EventTarget~Event|Object} event
2573 *        The name of the event, an `Event`, or an object with a key of type set to
2574 *        an event name.
2575 */
2576EventTarget.prototype.trigger = function (event) {
2577  var type = event.type || event;
2578
2579  if (typeof event === 'string') {
2580    event = { type: type };
2581  }
2582  event = fixEvent(event);
2583
2584  if (this.allowedEvents_[type] && this['on' + type]) {
2585    this['on' + type](event);
2586  }
2587
2588  trigger(this, event);
2589};
2590
2591/**
2592 * An alias of {@link EventTarget#trigger}. Allows `EventTarget` to mimic
2593 * the standard DOM API.
2594 *
2595 * @function
2596 * @see {@link EventTarget#trigger}
2597 */
2598EventTarget.prototype.dispatchEvent = EventTarget.prototype.trigger;
2599
2600/**
2601 * @file mixins/evented.js
2602 * @module evented
2603 */
2604/**
2605 * Returns whether or not an object has had the evented mixin applied.
2606 *
2607 * @param  {Object} object
2608 *         An object to test.
2609 *
2610 * @return {boolean}
2611 *         Whether or not the object appears to be evented.
2612 */
2613var isEvented = function isEvented(object) {
2614  return object instanceof EventTarget || !!object.eventBusEl_ && ['on', 'one', 'off', 'trigger'].every(function (k) {
2615    return typeof object[k] === 'function';
2616  });
2617};
2618
2619/**
2620 * Whether a value is a valid event type - non-empty string or array.
2621 *
2622 * @private
2623 * @param  {string|Array} type
2624 *         The type value to test.
2625 *
2626 * @return {boolean}
2627 *         Whether or not the type is a valid event type.
2628 */
2629var isValidEventType = function isValidEventType(type) {
2630  return (
2631    // The regex here verifies that the `type` contains at least one non-
2632    // whitespace character.
2633    typeof type === 'string' && /\S/.test(type) || Array.isArray(type) && !!type.length
2634  );
2635};
2636
2637/**
2638 * Validates a value to determine if it is a valid event target. Throws if not.
2639 *
2640 * @private
2641 * @throws {Error}
2642 *         If the target does not appear to be a valid event target.
2643 *
2644 * @param  {Object} target
2645 *         The object to test.
2646 */
2647var validateTarget = function validateTarget(target) {
2648  if (!target.nodeName && !isEvented(target)) {
2649    throw new Error('Invalid target; must be a DOM node or evented object.');
2650  }
2651};
2652
2653/**
2654 * Validates a value to determine if it is a valid event target. Throws if not.
2655 *
2656 * @private
2657 * @throws {Error}
2658 *         If the type does not appear to be a valid event type.
2659 *
2660 * @param  {string|Array} type
2661 *         The type to test.
2662 */
2663var validateEventType = function validateEventType(type) {
2664  if (!isValidEventType(type)) {
2665    throw new Error('Invalid event type; must be a non-empty string or array.');
2666  }
2667};
2668
2669/**
2670 * Validates a value to determine if it is a valid listener. Throws if not.
2671 *
2672 * @private
2673 * @throws {Error}
2674 *         If the listener is not a function.
2675 *
2676 * @param  {Function} listener
2677 *         The listener to test.
2678 */
2679var validateListener = function validateListener(listener) {
2680  if (typeof listener !== 'function') {
2681    throw new Error('Invalid listener; must be a function.');
2682  }
2683};
2684
2685/**
2686 * Takes an array of arguments given to `on()` or `one()`, validates them, and
2687 * normalizes them into an object.
2688 *
2689 * @private
2690 * @param  {Object} self
2691 *         The evented object on which `on()` or `one()` was called. This
2692 *         object will be bound as the `this` value for the listener.
2693 *
2694 * @param  {Array} args
2695 *         An array of arguments passed to `on()` or `one()`.
2696 *
2697 * @return {Object}
2698 *         An object containing useful values for `on()` or `one()` calls.
2699 */
2700var normalizeListenArgs = function normalizeListenArgs(self, args) {
2701
2702  // If the number of arguments is less than 3, the target is always the
2703  // evented object itself.
2704  var isTargetingSelf = args.length < 3 || args[0] === self || args[0] === self.eventBusEl_;
2705  var target = void 0;
2706  var type = void 0;
2707  var listener = void 0;
2708
2709  if (isTargetingSelf) {
2710    target = self.eventBusEl_;
2711
2712    // Deal with cases where we got 3 arguments, but we are still listening to
2713    // the evented object itself.
2714    if (args.length >= 3) {
2715      args.shift();
2716    }
2717
2718    type = args[0];
2719    listener = args[1];
2720  } else {
2721    target = args[0];
2722    type = args[1];
2723    listener = args[2];
2724  }
2725
2726  validateTarget(target);
2727  validateEventType(type);
2728  validateListener(listener);
2729
2730  listener = bind(self, listener);
2731
2732  return { isTargetingSelf: isTargetingSelf, target: target, type: type, listener: listener };
2733};
2734
2735/**
2736 * Adds the listener to the event type(s) on the target, normalizing for
2737 * the type of target.
2738 *
2739 * @private
2740 * @param  {Element|Object} target
2741 *         A DOM node or evented object.
2742 *
2743 * @param  {string} method
2744 *         The event binding method to use ("on" or "one").
2745 *
2746 * @param  {string|Array} type
2747 *         One or more event type(s).
2748 *
2749 * @param  {Function} listener
2750 *         A listener function.
2751 */
2752var listen = function listen(target, method, type, listener) {
2753  validateTarget(target);
2754
2755  if (target.nodeName) {
2756    Events[method](target, type, listener);
2757  } else {
2758    target[method](type, listener);
2759  }
2760};
2761
2762/**
2763 * Contains methods that provide event capabilit
vendor: 8,396 bytes, lines 2763-3005
2763es to an object which is passed
2764 * to {@link module:evented|evented}.
2765 *
2766 * @mixin EventedMixin
2767 */
2768var EventedMixin = {
2769
2770  /**
2771   * Add a listener to an event (or events) on this object or another evented
2772   * object.
2773   *
2774   * @param  {string|Array|Element|Object} targetOrType
2775   *         If this is a string or array, it represents the event type(s)
2776   *         that will trigger the listener.
2777   *
2778   *         Another evented object can be passed here instead, which will
2779   *         cause the listener to listen for events on _that_ object.
2780   *
2781   *         In either case, the listener's `this` value will be bound to
2782   *         this object.
2783   *
2784   * @param  {string|Array|Function} typeOrListener
2785   *         If the first argument was a string or array, this should be the
2786   *         listener function. Otherwise, this is a string or array of event
2787   *         type(s).
2788   *
2789   * @param  {Function} [listener]
2790   *         If the first argument was another evented object, this will be
2791   *         the listener function.
2792   */
2793  on: function on$$1() {
2794    var _this = this;
2795
2796    for (var _len = arguments.length, args = Array(_len), _key = 0; _key < _len; _key++) {
2797      args[_key] = arguments[_key];
2798    }
2799
2800    var _normalizeListenArgs = normalizeListenArgs(this, args),
2801        isTargetingSelf = _normalizeListenArgs.isTargetingSelf,
2802        target = _normalizeListenArgs.target,
2803        type = _normalizeListenArgs.type,
2804        listener = _normalizeListenArgs.listener;
2805
2806    listen(target, 'on', type, listener);
2807
2808    // If this object is listening to another evented object.
2809    if (!isTargetingSelf) {
2810
2811      // If this object is disposed, remove the listener.
2812      var removeListenerOnDispose = function removeListenerOnDispose() {
2813        return _this.off(target, type, listener);
2814      };
2815
2816      // Use the same function ID as the listener so we can remove it later it
2817      // using the ID of the original listener.
2818      removeListenerOnDispose.guid = listener.guid;
2819
2820      // Add a listener to the target's dispose event as well. This ensures
2821      // that if the target is disposed BEFORE this object, we remove the
2822      // removal listener that was just added. Otherwise, we create a memory leak.
2823      var removeRemoverOnTargetDispose = function removeRemoverOnTargetDispose() {
2824        return _this.off('dispose', removeListenerOnDispose);
2825      };
2826
2827      // Use the same function ID as the listener so we can remove it later
2828      // it using the ID of the original listener.
2829      removeRemoverOnTargetDispose.guid = listener.guid;
2830
2831      listen(this, 'on', 'dispose', removeListenerOnDispose);
2832      listen(target, 'on', 'dispose', removeRemoverOnTargetDispose);
2833    }
2834  },
2835
2836
2837  /**
2838   * Add a listener to an event (or events) on this object or another evented
2839   * object. The listener will only be called once and then removed.
2840   *
2841   * @param  {string|Array|Element|Object} targetOrType
2842   *         If this is a string or array, it represents the event type(s)
2843   *         that will trigger the listener.
2844   *
2845   *         Another evented object can be passed here instead, which will
2846   *         cause the listener to listen for events on _that_ object.
2847   *
2848   *         In either case, the listener's `this` value will be bound to
2849   *         this object.
2850   *
2851   * @param  {string|Array|Function} typeOrListener
2852   *         If the first argument was a string or array, this should be the
2853   *         listener function. Otherwise, this is a string or array of event
2854   *         type(s).
2855   *
2856   * @param  {Function} [listener]
2857   *         If the first argument was another evented object, this will be
2858   *         the listener function.
2859   */
2860  one: function one$$1() {
2861    var _this2 = this;
2862
2863    for (var _len2 = arguments.length, args = Array(_len2), _key2 = 0; _key2 < _len2; _key2++) {
2864      args[_key2] = arguments[_key2];
2865    }
2866
2867    var _normalizeListenArgs2 = normalizeListenArgs(this, args),
2868        isTargetingSelf = _normalizeListenArgs2.isTargetingSelf,
2869        target = _normalizeListenArgs2.target,
2870        type = _normalizeListenArgs2.type,
2871        listener = _normalizeListenArgs2.listener;
2872
2873    // Targeting this evented object.
2874
2875
2876    if (isTargetingSelf) {
2877      listen(target, 'one', type, listener);
2878
2879      // Targeting another evented object.
2880    } else {
2881      var wrapper = function wrapper() {
2882        for (var _len3 = arguments.length, largs = Array(_len3), _key3 = 0; _key3 < _len3; _key3++) {
2883          largs[_key3] = arguments[_key3];
2884        }
2885
2886        _this2.off(target, type, wrapper);
2887        listener.apply(null, largs);
2888      };
2889
2890      // Use the same function ID as the listener so we can remove it later
2891      // it using the ID of the original listener.
2892      wrapper.guid = listener.guid;
2893      listen(target, 'one', type, wrapper);
2894    }
2895  },
2896
2897
2898  /**
2899   * Removes listener(s) from event(s) on an evented object.
2900   *
2901   * @param  {string|Array|Element|Object} [targetOrType]
2902   *         If this is a string or array, it represents the event type(s).
2903   *
2904   *         Another evented object can be passed here instead, in which case
2905   *         ALL 3 arguments are _required_.
2906   *
2907   * @param  {string|Array|Function} [typeOrListener]
2908   *         If the first argument was a string or array, this may be the
2909   *         listener function. Otherwise, this is a string or array of event
2910   *         type(s).
2911   *
2912   * @param  {Function} [listener]
2913   *         If the first argument was another evented object, this will be
2914   *         the listener function; otherwise, _all_ listeners bound to the
2915   *         event type(s) will be removed.
2916   */
2917  off: function off$$1(targetOrType, typeOrListener, listener) {
2918
2919    // Targeting this evented object.
2920    if (!targetOrType || isValidEventType(targetOrType)) {
2921      off(this.eventBusEl_, targetOrType, typeOrListener);
2922
2923      // Targeting another evented object.
2924    } else {
2925      var target = targetOrType;
2926      var type = typeOrListener;
2927
2928      // Fail fast and in a meaningful way!
2929      validateTarget(target);
2930      validateEventType(type);
2931      validateListener(listener);
2932
2933      // Ensure there's at least a guid, even if the function hasn't been used
2934      listener = bind(this, listener);
2935
2936      // Remove the dispose listener on this evented object, which was given
2937      // the same guid as the event listener in on().
2938      this.off('dispose', listener);
2939
2940      if (target.nodeName) {
2941        off(target, type, listener);
2942        off(target, 'dispose', listener);
2943      } else if (isEvented(target)) {
2944        target.off(type, listener);
2945        target.off('dispose', listener);
2946      }
2947    }
2948  },
2949
2950
2951  /**
2952   * Fire an event on this evented object, causing its listeners to be called.
2953   *
2954   * @param   {string|Object} event
2955   *          An event type or an object with a type property.
2956   *
2957   * @param   {Object} [hash]
2958   *          An additional object to pass along to listeners.
2959   *
2960   * @returns {boolean}
2961   *          Whether or not the default behavior was prevented.
2962   */
2963  trigger: function trigger$$1(event, hash) {
2964    return trigger(this.eventBusEl_, event, hash);
2965  }
2966};
2967
2968/**
2969 * Applies {@link module:evented~EventedMixin|EventedMixin} to a target object.
2970 *
2971 * @param  {Object} target
2972 *         The object to which to add event methods.
2973 *
2974 * @param  {Object} [options={}]
2975 *         Options for customizing the mixin behavior.
2976 *
2977 * @param  {String} [options.eventBusKey]
2978 *         By default, adds a `eventBusEl_` DOM element to the target object,
2979 *         which is used as an event bus. If the target object already has a
2980 *         DOM element that should be used, pass its key here.
2981 *
2982 * @return {Object}
2983 *         The target object.
2984 */
2985function evented(target) {
2986  var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
2987  var eventBusKey = options.eventBusKey;
2988
2989  // Set or create the eventBusEl_.
2990
2991  if (eventBusKey) {
2992    if (!target[eventBusKey].nodeName) {
2993      throw new Error('The eventBusKey "' + eventBusKey + '" does not refer to an element.');
2994    }
2995    target.eventBusEl_ = target[eventBusKey];
2996  } else {
2997    target.eventBusEl_ = createEl('span', { className: 'vjs-event-bus' });
2998  }
2999
3000  assign(target, EventedMixin);
3001
3002  // When any evented object is disposed, it removes all its listeners.
3003  target.on('dispose', function () {
3004    target.off();
3005    
vendor: 4,465 bytes, lines 3005-3176
3005window_1.setTimeout(function () {
3006      target.eventBusEl_ = null;
3007    }, 0);
3008  });
3009
3010  return target;
3011}
3012
3013/**
3014 * @file mixins/stateful.js
3015 * @module stateful
3016 */
3017/**
3018 * Contains methods that provide statefulness to an object which is passed
3019 * to {@link module:stateful}.
3020 *
3021 * @mixin StatefulMixin
3022 */
3023var StatefulMixin = {
3024
3025  /**
3026   * A hash containing arbitrary keys and values representing the state of
3027   * the object.
3028   *
3029   * @type {Object}
3030   */
3031  state: {},
3032
3033  /**
3034   * Set the state of an object by mutating its
3035   * {@link module:stateful~StatefulMixin.state|state} object in place.
3036   *
3037   * @fires   module:stateful~StatefulMixin#statechanged
3038   * @param   {Object|Function} stateUpdates
3039   *          A new set of properties to shallow-merge into the plugin state.
3040   *          Can be a plain object or a function returning a plain object.
3041   *
3042   * @returns {Object|undefined}
3043   *          An object containing changes that occurred. If no changes
3044   *          occurred, returns `undefined`.
3045   */
3046  setState: function setState(stateUpdates) {
3047    var _this = this;
3048
3049    // Support providing the `stateUpdates` state as a function.
3050    if (typeof stateUpdates === 'function') {
3051      stateUpdates = stateUpdates();
3052    }
3053
3054    var changes = void 0;
3055
3056    each(stateUpdates, function (value, key) {
3057
3058      // Record the change if the value is different from what's in the
3059      // current state.
3060      if (_this.state[key] !== value) {
3061        changes = changes || {};
3062        changes[key] = {
3063          from: _this.state[key],
3064          to: value
3065        };
3066      }
3067
3068      _this.state[key] = value;
3069    });
3070
3071    // Only trigger "statechange" if there were changes AND we have a trigger
3072    // function. This allows us to not require that the target object be an
3073    // evented object.
3074    if (changes && isEvented(this)) {
3075
3076      /**
3077       * An event triggered on an object that is both
3078       * {@link module:stateful|stateful} and {@link module:evented|evented}
3079       * indicating that its state has changed.
3080       *
3081       * @event    module:stateful~StatefulMixin#statechanged
3082       * @type     {Object}
3083       * @property {Object} changes
3084       *           A hash containing the properties that were changed and
3085       *           the values they were changed `from` and `to`.
3086       */
3087      this.trigger({
3088        changes: changes,
3089        type: 'statechanged'
3090      });
3091    }
3092
3093    return changes;
3094  }
3095};
3096
3097/**
3098 * Applies {@link module:stateful~StatefulMixin|StatefulMixin} to a target
3099 * object.
3100 *
3101 * If the target object is {@link module:evented|evented} and has a
3102 * `handleStateChanged` method, that method will be automatically bound to the
3103 * `statechanged` event on itself.
3104 *
3105 * @param   {Object} target
3106 *          The object to be made stateful.
3107 *
3108 * @param   {Object} [defaultState]
3109 *          A default set of properties to populate the newly-stateful object's
3110 *          `state` property.
3111 *
3112 * @returns {Object}
3113 *          Returns the `target`.
3114 */
3115function stateful(target, defaultState) {
3116  assign(target, StatefulMixin);
3117
3118  // This happens after the mixing-in because we need to replace the `state`
3119  // added in that step.
3120  target.state = assign({}, target.state, defaultState);
3121
3122  // Auto-bind the `handleStateChanged` method of the target object if it exists.
3123  if (typeof target.handleStateChanged === 'function' && isEvented(target)) {
3124    target.on('statechanged', target.handleStateChanged);
3125  }
3126
3127  return target;
3128}
3129
3130/**
3131 * @file to-title-case.js
3132 * @module to-title-case
3133 */
3134
3135/**
3136 * Uppercase the first letter of a string.
3137 *
3138 * @param {string} string
3139 *        String to be uppercased
3140 *
3141 * @return {string}
3142 *         The string with an uppercased first letter
3143 */
3144function toTitleCase(string) {
3145  if (typeof string !== 'string') {
3146    return string;
3147  }
3148
3149  return string.charAt(0).toUpperCase() + string.slice(1);
3150}
3151
3152/**
3153 * Compares the TitleCase versions of the two strings for equality.
3154 *
3155 * @param {string} str1
3156 *        The first string to compare
3157 *
3158 * @param {string} str2
3159 *        The second string to compare
3160 *
3161 * @return {boolean}
3162 *         Whether the TitleCase versions of the strings are equal
3163 */
3164function titleCaseEquals(str1, str2) {
3165  return toTitleCase(str1) === toTitleCase(str2);
3166}
3167
3168/**
3169 * @file merge-options.js
3170 * @module merge-options
3171 */
3172/**
3173 * Deep-merge one or more options objects, recursively merging **only** plain
3174 * object properties.
3175 *
3176 * @param   {Object[]} sources
3177 *          One or more objects to merge into a new object.
3178 *
3179 * @returns {Object}
3180 *          A new object that is the merged result of all sources.
3181 */
3182function mergeOptions() {
3183  var result = {};
3184
3185  for (var _len = arguments.length, sources = Array(_len), _key = 0; _key < _len; _key++) {
3186    sources[_key] = arguments[_key];
3187  }
3188
3189  sources.forEach(function (source) {
3190    if (!source) {
3191      return;
3192    }
3193
3194    each(source, function (value, key) {
3195      if (!isPlain(value)) {
3196        result[key] = value;
3197        return;
3198      }
3199
3200      if (!isPlain(result[key])) {
3201        result[key] = {};
3202      }
3203
3204      result[key] = mergeOptions(result[key], value);
3205    });
3206  });
3207
3208  return result;
3209}
3210
3211/**
3212 * Player Component - Base class for all UI objects
3213 *
3214 * @file component.js
3215 */
vendor: 30,502 bytes, lines 3215-4258
3215
3216/**
3217 * Base class for all UI Components.
3218 * Components are UI objects which represent both a javascript object and an element
3219 * in the DOM. They can be children of other components, and can have
3220 * children themselves.
3221 *
3222 * Components can also use methods from {@link EventTarget}
3223 */
3224
3225var Component = function () {
3226
3227  /**
3228   * A callback that is called when a component is ready. Does not have any
3229   * paramters and any callback value will be ignored.
3230   *
3231   * @callback Component~ReadyCallback
3232   * @this Component
3233   */
3234
3235  /**
3236   * Creates an instance of this class.
3237   *
3238   * @param {Player} player
3239   *        The `Player` that this class should be attached to.
3240   *
3241   * @param {Object} [options]
3242   *        The key/value store of player options.
3243   *
3244   * @param {Object[]} [options.children]
3245   *        An array of children objects to intialize this component with. Children objects have
3246   *        a name property that will be used if more than one component of the same type needs to be
3247   *        added.
3248   *
3249   * @param {Component~ReadyCallback} [ready]
3250   *        Function that gets called when the `Component` is ready.
3251   */
3252  function Component(player, options, ready) {
3253    classCallCheck(this, Component);
3254
3255
3256    // The component might be the player itself and we can't pass `this` to super
3257    if (!player && this.play) {
3258      this.player_ = player = this; // eslint-disable-line
3259    } else {
3260      this.player_ = player;
3261    }
3262
3263    // Make a copy of prototype.options_ to protect against overriding defaults
3264    this.options_ = mergeOptions({}, this.options_);
3265
3266    // Updated options with supplied options
3267    options = this.options_ = mergeOptions(this.options_, options);
3268
3269    // Get ID from options or options element if one is supplied
3270    this.id_ = options.id || options.el && options.el.id;
3271
3272    // If there was no ID from the options, generate one
3273    if (!this.id_) {
3274      // Don't require the player ID function in the case of mock players
3275      var id = player && player.id && player.id() || 'no_player';
3276
3277      this.id_ = id + '_component_' + newGUID();
3278    }
3279
3280    this.name_ = options.name || null;
3281
3282    // Create element if one wasn't provided in options
3283    if (options.el) {
3284      this.el_ = options.el;
3285    } else if (options.createEl !== false) {
3286      this.el_ = this.createEl();
3287    }
3288
3289    // if evented is anything except false, we want to mixin in evented
3290    if (options.evented !== false) {
3291      // Make this an evented object and use `el_`, if available, as its event bus
3292      evented(this, { eventBusKey: this.el_ ? 'el_' : null });
3293    }
3294    stateful(this, this.constructor.defaultState);
3295
3296    this.children_ = [];
3297    this.childIndex_ = {};
3298    this.childNameIndex_ = {};
3299
3300    // Add any child components in options
3301    if (options.initChildren !== false) {
3302      this.initChildren();
3303    }
3304
3305    this.ready(ready);
3306    // Don't want to trigger ready here or it will before init is actually
3307    // finished for all children that run this constructor
3308
3309    if (options.reportTouchActivity !== false) {
3310      this.enableTouchActivity();
3311    }
3312  }
3313
3314  /**
3315   * Dispose of the `Component` and all child components.
3316   *
3317   * @fires Component#dispose
3318   */
3319
3320
3321  Component.prototype.dispose = function dispose() {
3322
3323    /**
3324     * Triggered when a `Component` is disposed.
3325     *
3326     * @event Component#dispose
3327     * @type {EventTarget~Event}
3328     *
3329     * @property {boolean} [bubbles=false]
3330     *           set to false so that the close event does not
3331     *           bubble up
3332     */
3333    this.trigger({ type: 'dispose', bubbles: false });
3334
3335    // Dispose all children.
3336    if (this.children_) {
3337      for (var i = this.children_.length - 1; i >= 0; i--) {
3338        if (this.children_[i].dispose) {
3339          this.children_[i].dispose();
3340        }
3341      }
3342    }
3343
3344    // Delete child references
3345    this.children_ = null;
3346    this.childIndex_ = null;
3347    this.childNameIndex_ = null;
3348
3349    if (this.el_) {
3350      // Remove element from DOM
3351      if (this.el_.parentNode) {
3352        this.el_.parentNode.removeChild(this.el_);
3353      }
3354
3355      removeData(this.el_);
3356      this.el_ = null;
3357    }
3358
3359    // remove reference to the player after disposing of the element
3360    this.player_ = null;
3361  };
3362
3363  /**
3364   * Return the {@link Player} that the `Component` has attached to.
3365   *
3366   * @return {Player}
3367   *         The player that this `Component` has attached to.
3368   */
3369
3370
3371  Component.prototype.player = function player() {
3372    return this.player_;
3373  };
3374
3375  /**
3376   * Deep merge of options objects with new options.
3377   * > Note: When both `obj` and `options` contain properties whose values are objects.
3378   *         The two properties get merged using {@link module:mergeOptions}
3379   *
3380   * @param {Object} obj
3381   *        The object that contains new options.
3382   *
3383   * @return {Object}
3384   *         A new object of `this.options_` and `obj` merged together.
3385   *
3386   * @deprecated since version 5
3387   */
3388
3389
3390  Component.prototype.options = function options(obj) {
3391    log$1.warn('this.options() has been deprecated and will be moved to the constructor in 6.0');
3392
3393    if (!obj) {
3394      return this.options_;
3395    }
3396
3397    this.options_ = mergeOptions(this.options_, obj);
3398    return this.options_;
3399  };
3400
3401  /**
3402   * Get the `Component`s DOM element
3403   *
3404   * @return {Element}
3405   *         The DOM element for this `Component`.
3406   */
3407
3408
3409  Component.prototype.el = function el() {
3410    return this.el_;
3411  };
3412
3413  /**
3414   * Create the `Component`s DOM element.
3415   *
3416   * @param {string} [tagName]
3417   *        Element's DOM node type. e.g. 'div'
3418   *
3419   * @param {Object} [properties]
3420   *        An object of properties that should be set.
3421   *
3422   * @param {Object} [attributes]
3423   *        An object of attributes that should be set.
3424   *
3425   * @return {Element}
3426   *         The element that gets created.
3427   */
3428
3429
3430  Component.prototype.createEl = function createEl$$1(tagName, properties, attributes) {
3431    return createEl(tagName, properties, attributes);
3432  };
3433
3434  /**
3435   * Localize a string given the string in english.
3436   *
3437   * If tokens are provided, it'll try and run a simple token replacement on the provided string.
3438   * The tokens it looks for look like `{1}` with the index being 1-indexed into the tokens array.
3439   *
3440   * If a `defaultValue` is provided, it'll use that over `string`,
3441   * if a value isn't found in provided language files.
3442   * This is useful if you want to have a descriptive key for token replacement
3443   * but have a succinct localized string and not require `en.json` to be included.
3444   *
3445   * Currently, it is used for the progress bar timing.
3446   * ```js
3447   * {
3448   *   "progress bar timing: currentTime={1} duration={2}": "{1} of {2}"
3449   * }
3450   * ```
3451   * It is then used like so:
3452   * ```js
3453   * this.localize('progress bar timing: currentTime={1} duration{2}',
3454   *               [this.player_.currentTime(), this.player_.duration()],
3455   *               '{1} of {2}');
3456   * ```
3457   *
3458   * Which outputs something like: `01:23 of 24:56`.
3459   *
3460   *
3461   * @param {string} string
3462   *        The string to localize and the key to lookup in the language files.
3463   * @param {string[]} [tokens]
3464   *        If the current item has token replacements, provide the tokens here.
3465   * @param {string} [defaultValue]
3466   *        Defaults to `string`. Can be a default value to use for token replacement
3467   *        if the lookup key is needed to be separate.
3468   *
3469   * @return {string}
3470   *         The localized string or if no localization exists the english string.
3471   */
3472
3473
3474  Component.prototype.localize = function localize(string, tokens) {
3475    var defaultValue = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : string;
3476
3477    var code = this.player_.language && this.player_.language();
3478    var languages = this.player_.languages && this.player_.languages();
3479    var language = languages && languages[code];
3480    var primaryCode = code && code.split('-')[0];
3481    var primaryLang = languages && languages[primaryCode];
3482
3483    var localizedString = defaultValue;
3484
3485    if (language && language[string]) {
3486      localizedString = language[string];
3487    } else if (primaryLang && primaryLang[string]) {
3488      localizedString = primaryLang[string];
3489    }
3490
3491    if (tokens) {
3492      localizedString = localizedString.replace(/\{(\d+)\}/g, function (match, index) {
3493        var value = tokens[index - 1];
3494        var ret = value;
3495
3496        if (typeof value === 'undefined') {
3497          ret = match;
3498        }
3499
3500        return ret;
3501      });
3502    }
3503
3504    return localizedString;
3505  };
3506
3507  /**
3508   * Return the `Component`s DOM element. This is where children get inserted.
3509   * This will usually be the the same as the element returned in {@link Component#el}.
3510   *
3511   * @return {Element}
3512   *         The content element for this `Component`.
3513   */
3514
3515
3516  Component.prototype.contentEl = function contentEl() {
3517    return this.contentEl_ || this.el_;
3518  };
3519
3520  /**
3521   * Get this `Component`s ID
3522   *
3523   * @return {string}
3524   *         The id of this `Component`
3525   */
3526
3527
3528  Component.prototype.id = function id() {
3529    return this.id_;
3530  };
3531
3532  /**
3533   * Get the `Component`s name. The name gets used to reference the `Component`
3534   * and is set during registration.
3535   *
3536   * @return {string}
3537   *         The name of this `Component`.
3538   */
3539
3540
3541  Component.prototype.name = function name() {
3542    return this.name_;
3543  };
3544
3545  /**
3546   * Get an array of all child components
3547   *
3548   * @return {Array}
3549   *         The children
3550   */
3551
3552
3553  Component.prototype.children = function children() {
3554    return this.children_;
3555  };
3556
3557  /**
3558   * Returns the child `Component` with the given `id`.
3559   *
3560   * @param {string} id
3561   *        The id of the child `Component` to get.
3562   *
3563   * @return {Component|undefined}
3564   *         The child `Component` with the given `id` or undefined.
3565   */
3566
3567
3568  Component.prototype.getChildById = function getChildById(id) {
3569    return this.childIndex_[id];
3570  };
3571
3572  /**
3573   * Returns the child `Component` with the given `name`.
3574   *
3575   * @param {string} name
3576   *        The name of the child `Component` to get.
3577   *
3578   * @return {Component|undefined}
3579   *         The child `Component` with the given `name` or undefined.
3580   */
3581
3582
3583  Component.prototype.getChild = function getChild(name) {
3584    if (!name) {
3585      return;
3586    }
3587
3588    name = toTitleCase(name);
3589
3590    return this.childNameIndex_[name];
3591  };
3592
3593  /**
3594   * Add a child `Component` inside the current `Component`.
3595   *
3596   *
3597   * @param {string|Component} child
3598   *        The name or instance of a child to add.
3599   *
3600   * @param {Object} [options={}]
3601   *        The key/value store of options that will get passed to children of
3602   *        the child.
3603   *
3604   * @param {number} [index=this.children_.length]
3605   *        The index to attempt to add a child into.
3606   *
3607   * @return {Component}
3608   *         The `Component` that gets added as a child. When using a string the
3609   *         `Component` will get created by this process.
3610   */
3611
3612
3613  Component.prototype.addChild = function addChild(child) {
3614    var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
3615    var index = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : this.children_.length;
3616
3617    var component = void 0;
3618    var componentName = void 0;
3619
3620    // If child is a string, create component with options
3621    if (typeof child === 'string') {
3622      componentName = toTitleCase(child);
3623
3624      var componentClassName = options.componentClass || componentName;
3625
3626      // Set name through options
3627      options.name = componentName;
3628
3629      // Create a new object & element for this controls set
3630      // If there's no .player_, this is a player
3631      var ComponentClass = Component.getComponent(componentClassName);
3632
3633      if (!ComponentClass) {
3634        throw new Error('Component ' + componentClassName + ' does not exist');
3635      }
3636
3637      // data stored directly on the videojs object may be
3638      // misidentified as a component to retain
3639      // backwards-compatibility with 4.x. check to make sure the
3640      // component class can be instantiated.
3641      if (typeof ComponentClass !== 'function') {
3642        return null;
3643      }
3644
3645      component = new ComponentClass(this.player_ || this, options);
3646
3647      // child is a component instance
3648    } else {
3649      component = child;
3650    }
3651
3652    this.children_.splice(index, 0, component);
3653
3654    if (typeof component.id === 'function') {
3655      this.childIndex_[component.id()] = component;
3656    }
3657
3658    // If a name wasn't used to create the component, check if we can use the
3659    // name function of the component
3660    componentName = componentName || component.name && toTitleCase(component.name());
3661
3662    if (componentName) {
3663      this.childNameIndex_[componentName] = component;
3664    }
3665
3666    // Add the UI object's element to the container div (box)
3667    // Having an element is not required
3668    if (typeof component.el === 'function' && component.el()) {
3669      var childNodes = this.contentEl().children;
3670      var refNode = childNodes[index] || null;
3671
3672      this.contentEl().insertBefore(component.el(), refNode);
3673    }
3674
3675    // Return so it can stored on parent object if desired.
3676    return component;
3677  };
3678
3679  /**
3680   * Remove a child `Component` from this `Component`s list of children. Also removes
3681   * the child `Component`s element from this `Component`s element.
3682   *
3683   * @param {Component} component
3684   *        The child `Component` to remove.
3685   */
3686
3687
3688  Component.prototype.removeChild = function removeChild(component) {
3689    if (typeof component === 'string') {
3690      component = this.getChild(component);
3691    }
3692
3693    if (!component || !this.children_) {
3694      return;
3695    }
3696
3697    var childFound = false;
3698
3699    for (var i = this.children_.length - 1; i >= 0; i--) {
3700      if (this.children_[i] === component) {
3701        childFound = true;
3702        this.children_.splice(i, 1);
3703        break;
3704      }
3705    }
3706
3707    if (!childFound) {
3708      return;
3709    }
3710
3711    this.childIndex_[component.id()] = null;
3712    this.childNameIndex_[component.name()] = null;
3713
3714    var compEl = component.el();
3715
3716    if (compEl && compEl.parentNode === this.contentEl()) {
3717      this.contentEl().removeChild(component.el());
3718    }
3719  };
3720
3721  /**
3722   * Add and initialize default child `Component`s based upon options.
3723   */
3724
3725
3726  Component.prototype.initChildren = function initChildren() {
3727    var _this = this;
3728
3729    var children = this.options_.children;
3730
3731    if (children) {
3732      // `this` is `parent`
3733      var parentOptions = this.options_;
3734
3735      var handleAdd = function handleAdd(child) {
3736        var name = child.name;
3737        var opts = child.opts;
3738
3739        // Allow options for children to be set at the parent options
3740        // e.g. videojs(id, { controlBar: false });
3741        // instead of videojs(id, { children: { controlBar: false });
3742        if (parentOptions[name] !== undefined) {
3743          opts = parentOptions[name];
3744        }
3745
3746        // Allow for disabling default components
3747        // e.g. options['children']['posterImage'] = false
3748        if (opts === false) {
3749          return;
3750        }
3751
3752        // Allow options to be passed as a simple boolean if no configuration
3753        // is necessary.
3754        if (opts === true) {
3755          opts = {};
3756        }
3757
3758        // We also want to pass the original player options
3759        // to each component as well so they don't need to
3760        // reach back into the player for options later.
3761        opts.playerOptions = _this.options_.playerOptions;
3762
3763        // Create and add the child component.
3764        // Add a direct reference to the child by name on the parent instance.
3765        // If two of the same component are used, different names should be supplied
3766        // for each
3767        var newChild = _this.addChild(name, opts);
3768
3769        if (newChild) {
3770          _this[name] = newChild;
3771        }
3772      };
3773
3774      // Allow for an array of children details to passed in the options
3775      var workingChildren = void 0;
3776      var Tech = Component.getComponent('Tech');
3777
3778      if (Array.isArray(children)) {
3779        workingChildren = children;
3780      } else {
3781        workingChildren = Object.keys(children);
3782      }
3783
3784      workingChildren
3785      // children that are in this.options_ but also in workingChildren  would
3786      // give us extra children we do not want. So, we want to filter them out.
3787      .concat(Object.keys(this.options_).filter(function (child) {
3788        return !workingChildren.some(function (wchild) {
3789          if (typeof wchild === 'string') {
3790            return child === wchild;
3791          }
3792          return child === wchild.name;
3793        });
3794      })).map(function (child) {
3795        var name = void 0;
3796        var opts = void 0;
3797
3798        if (typeof child === 'string') {
3799          name = child;
3800          opts = children[name] || _this.options_[name] || {};
3801        } else {
3802          name = child.name;
3803          opts = child;
3804        }
3805
3806        return { name: name, opts: opts };
3807      }).filter(function (child) {
3808        // we have to make sure that child.name isn't in the techOrder since
3809        // techs are registerd as Components but can't aren't compatible
3810        // See https://github.com/videojs/video.js/issues/2772
3811        var c = Component.getComponent(child.opts.componentClass || toTitleCase(child.name));
3812
3813        return c && !Tech.isTech(c);
3814      }).forEach(handleAdd);
3815    }
3816  };
3817
3818  /**
3819   * Builds the default DOM class name. Should be overriden by sub-components.
3820   *
3821   * @return {string}
3822   *         The DOM class name for this object.
3823   *
3824   * @abstract
3825   */
3826
3827
3828  Component.prototype.buildCSSClass = function buildCSSClass() {
3829    // Child classes can include a function that does:
3830    // return 'CLASS NAME' + this._super();
3831    return '';
3832  };
3833
3834  /**
3835   * Bind a listener to the component's ready state.
3836   * Different from event listeners in that if the ready event has already happened
3837   * it will trigger the function immediately.
3838   *
3839   * @return {Component}
3840   *         Returns itself; method can be chained.
3841   */
3842
3843
3844  Component.prototype.ready = function ready(fn) {
3845    var sync = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false;
3846
3847    if (!fn) {
3848      return;
3849    }
3850
3851    if (!this.isReady_) {
3852      this.readyQueue_ = this.readyQueue_ || [];
3853      this.readyQueue_.push(fn);
3854      return;
3855    }
3856
3857    if (sync) {
3858      fn.call(this);
3859    } else {
3860      // Call the function asynchronously by default for consistency
3861      this.setTimeout(fn, 1);
3862    }
3863  };
3864
3865  /**
3866   * Trigger all the ready listeners for this `Component`.
3867   *
3868   * @fires Component#ready
3869   */
3870
3871
3872  Component.prototype.triggerReady = function triggerReady() {
3873    this.isReady_ = true;
3874
3875    // Ensure ready is triggered asynchronously
3876    this.setTimeout(function () {
3877      var readyQueue = this.readyQueue_;
3878
3879      // Reset Ready Queue
3880      this.readyQueue_ = [];
3881
3882      if (readyQueue && readyQueue.length > 0) {
3883        readyQueue.forEach(function (fn) {
3884          fn.call(this);
3885        }, this);
3886      }
3887
3888      // Allow for using event listeners also
3889      /**
3890       * Triggered when a `Component` is ready.
3891       *
3892       * @event Component#ready
3893       * @type {EventTarget~Event}
3894       */
3895      this.trigger('ready');
3896    }, 1);
3897  };
3898
3899  /**
3900   * Find a single DOM element matching a `selector`. This can be within the `Component`s
3901   * `contentEl()` or another custom context.
3902   *
3903   * @param {string} selector
3904   *        A valid CSS selector, which will be passed to `querySelector`.
3905   *
3906   * @param {Element|string} [context=this.contentEl()]
3907   *        A DOM element within which to query. Can also be a selector string in
3908   *        which case the first matching element will get used as context. If
3909   *        missing `this.contentEl()` gets used. If  `this.contentEl()` returns
3910   *        nothing it falls back to `document`.
3911   *
3912   * @return {Element|null}
3913   *         the dom element that was found, or null
3914   *
3915   * @see [Information on CSS Selectors](https://developer.mozilla.org/en-US/docs/Web/Guide/CSS/Getting_Started/Selectors)
3916   */
3917
3918
3919  Component.prototype.$ = function $$$1(selector, context) {
3920    return $(selector, context || this.contentEl());
3921  };
3922
3923  /**
3924   * Finds all DOM element matching a `selector`. This can be within the `Component`s
3925   * `contentEl()` or another custom context.
3926   *
3927   * @param {string} selector
3928   *        A valid CSS selector, which will be passed to `querySelectorAll`.
3929   *
3930   * @param {Element|string} [context=this.contentEl()]
3931   *        A DOM element within which to query. Can also be a selector string in
3932   *        which case the first matching element will get used as context. If
3933   *        missing `this.contentEl()` gets used. If  `this.contentEl()` returns
3934   *        nothing it falls back to `document`.
3935   *
3936   * @return {NodeList}
3937   *         a list of dom elements that were found
3938   *
3939   * @see [Information on CSS Selectors](https://developer.mozilla.org/en-US/docs/Web/Guide/CSS/Getting_Started/Selectors)
3940   */
3941
3942
3943  Component.prototype.$$ = function $$$$1(selector, context) {
3944    return $$(selector, context || this.contentEl());
3945  };
3946
3947  /**
3948   * Check if a component's element has a CSS class name.
3949   *
3950   * @param {string} classToCheck
3951   *        CSS class name to check.
3952   *
3953   * @return {boolean}
3954   *         - True if the `Component` has the class.
3955   *         - False if the `Component` does not have the class`
3956   */
3957
3958
3959  Component.prototype.hasClass = function hasClass$$1(classToCheck) {
3960    return hasClass(this.el_, classToCheck);
3961  };
3962
3963  /**
3964   * Add a CSS class name to the `Component`s element.
3965   *
3966   * @param {string} classToAdd
3967   *        CSS class name to add
3968   */
3969
3970
3971  Component.prototype.addClass = function addClass$$1(classToAdd) {
3972    addClass(this.el_, classToAdd);
3973  };
3974
3975  /**
3976   * Remove a CSS class name from the `Component`s element.
3977   *
3978   * @param {string} classToRemove
3979   *        CSS class name to remove
3980   */
3981
3982
3983  Component.prototype.removeClass = function removeClass$$1(classToRemove) {
3984    removeClass(this.el_, classToRemove);
3985  };
3986
3987  /**
3988   * Add or remove a CSS class name from the component's element.
3989   * - `classToToggle` gets added when {@link Component#hasClass} would return false.
3990   * - `classToToggle` gets removed when {@link Component#hasClass} would return true.
3991   *
3992   * @param  {string} classToToggle
3993   *         The class to add or remove based on (@link Component#hasClass}
3994   *
3995   * @param  {boolean|Dom~predicate} [predicate]
3996   *         An {@link Dom~predicate} function or a boolean
3997   */
3998
3999
4000  Component.prototype.toggleClass = function toggleClass$$1(classToToggle, predicate) {
4001    toggleClass(this.el_, classToToggle, predicate);
4002  };
4003
4004  /**
4005   * Show the `Component`s element if it is hidden by removing the
4006   * 'vjs-hidden' class name from it.
4007   */
4008
4009
4010  Component.prototype.show = function show() {
4011    this.removeClass('vjs-hidden');
4012  };
4013
4014  /**
4015   * Hide the `Component`s element if it is currently showing by adding the
4016   * 'vjs-hidden` class name to it.
4017   */
4018
4019
4020  Component.prototype.hide = function hide() {
4021    this.addClass('vjs-hidden');
4022  };
4023
4024  /**
4025   * Lock a `Component`s element in its visible state by adding the 'vjs-lock-showing'
4026   * class name to it. Used during fadeIn/fadeOut.
4027   *
4028   * @private
4029   */
4030
4031
4032  Component.prototype.lockShowing = function lockShowing() {
4033    this.addClass('vjs-lock-showing');
4034  };
4035
4036  /**
4037   * Unlock a `Component`s element from its visible state by removing the 'vjs-lock-showing'
4038   * class name from it. Used during fadeIn/fadeOut.
4039   *
4040   * @private
4041   */
4042
4043
4044  Component.prototype.unlockShowing = function unlockShowing() {
4045    this.removeClass('vjs-lock-showing');
4046  };
4047
4048  /**
4049   * Get the value of an attribute on the `Component`s element.
4050   *
4051   * @param {string} attribute
4052   *        Name of the attribute to get the value from.
4053   *
4054   * @return {string|null}
4055   *         - The value of the attribute that was asked for.
4056   *         - Can be an empty string on some browsers if the attribute does not exist
4057   *           or has no value
4058   *         - Most browsers will return null if the attibute does not exist or has
4059   *           no value.
4060   *
4061   * @see [DOM API]{@link https://developer.mozilla.org/en-US/docs/Web/API/Element/getAttribute}
4062   */
4063
4064
4065  Component.prototype.getAttribute = function getAttribute$$1(attribute) {
4066    return getAttribute(this.el_, attribute);
4067  };
4068
4069  /**
4070   * Set the value of an attribute on the `Component`'s element
4071   *
4072   * @param {string} attribute
4073   *        Name of the attribute to set.
4074   *
4075   * @param {string} value
4076   *        Value to set the attribute to.
4077   *
4078   * @see [DOM API]{@link https://developer.mozilla.org/en-US/docs/Web/API/Element/setAttribute}
4079   */
4080
4081
4082  Component.prototype.setAttribute = function setAttribute$$1(attribute, value) {
4083    setAttribute(this.el_, attribute, value);
4084  };
4085
4086  /**
4087   * Remove an attribute from the `Component`s element.
4088   *
4089   * @param {string} attribute
4090   *        Name of the attribute to remove.
4091   *
4092   * @see [DOM API]{@link https://developer.mozilla.org/en-US/docs/Web/API/Element/removeAttribute}
4093   */
4094
4095
4096  Component.prototype.removeAttribute = function removeAttribute$$1(attribute) {
4097    removeAttribute(this.el_, attribute);
4098  };
4099
4100  /**
4101   * Get or set the width of the component based upon the CSS styles.
4102   * See {@link Component#dimension} for more detailed information.
4103   *
4104   * @param {number|string} [num]
4105   *        The width that you want to set postfixed with '%', 'px' or nothing.
4106   *
4107   * @param {boolean} [skipListeners]
4108   *        Skip the componentresize event trigger
4109   *
4110   * @return {number|string}
4111   *         The width when getting, zero if there is no width. Can be a string
4112   *           postpixed with '%' or 'px'.
4113   */
4114
4115
4116  Component.prototype.width = function width(num, skipListeners) {
4117    return this.dimension('width', num, skipListeners);
4118  };
4119
4120  /**
4121   * Get or set the height of the component based upon the CSS styles.
4122   * See {@link Component#dimension} for more detailed information.
4123   *
4124   * @param {number|string} [num]
4125   *        The height that you want to set postfixed with '%', 'px' or nothing.
4126   *
4127   * @param {boolean} [skipListeners]
4128   *        Skip the componentresize event trigger
4129   *
4130   * @return {number|string}
4131   *         The width when getting, zero if there is no width. Can be a string
4132   *         postpixed with '%' or 'px'.
4133   */
4134
4135
4136  Component.prototype.height = function height(num, skipListeners) {
4137    return this.dimension('height', num, skipListeners);
4138  };
4139
4140  /**
4141   * Set both the width and height of the `Component` element at the same time.
4142   *
4143   * @param  {number|string} width
4144   *         Width to set the `Component`s element to.
4145   *
4146   * @param  {number|string} height
4147   *         Height to set the `Component`s element to.
4148   */
4149
4150
4151  Component.prototype.dimensions = function dimensions(width, height) {
4152    // Skip componentresize listeners on width for optimization
4153    this.width(width, true);
4154    this.height(height);
4155  };
4156
4157  /**
4158   * Get or set width or height of the `Component` element. This is the shared code
4159   * for the {@link Component#width} and {@link Component#height}.
4160   *
4161   * Things to know:
4162   * - If the width or height in an number this will return the number postfixed with 'px'.
4163   * - If the width/height is a percent this will return the percent postfixed with '%'
4164   * - Hidden elements have a width of 0 with `window.getComputedStyle`. This function
4165   *   defaults to the `Component`s `style.width` and falls back to `window.getComputedStyle`.
4166   *   See [this]{@link http://www.foliotek.com/devblog/getting-the-width-of-a-hidden-element-with-jquery-using-width/}
4167   *   for more information
4168   * - If you want the computed style of the component, use {@link Component#currentWidth}
4169   *   and {@link {Component#currentHeight}
4170   *
4171   * @fires Component#componentresize
4172   *
4173   * @param {string} widthOrHeight
4174   8        'width' or 'height'
4175   *
4176   * @param  {number|string} [num]
4177   8         New dimension
4178   *
4179   * @param  {boolean} [skipListeners]
4180   *         Skip componentresize event trigger
4181   *
4182   * @return {number}
4183   *         The dimension when getting or 0 if unset
4184   */
4185
4186
4187  Component.prototype.dimension = function dimension(widthOrHeight, num, skipListeners) {
4188    if (num !== undefined) {
4189      // Set to zero if null or literally NaN (NaN !== NaN)
4190      if (num === null || num !== num) {
4191        num = 0;
4192      }
4193
4194      // Check if using css width/height (% or px) and adjust
4195      if (('' + num).indexOf('%') !== -1 || ('' + num).indexOf('px') !== -1) {
4196        this.el_.style[widthOrHeight] = num;
4197      } else if (num === 'auto') {
4198        this.el_.style[widthOrHeight] = '';
4199      } else {
4200        this.el_.style[widthOrHeight] = num + 'px';
4201      }
4202
4203      // skipListeners allows us to avoid triggering the resize event when setting both width and height
4204      if (!skipListeners) {
4205        /**
4206         * Triggered when a component is resized.
4207         *
4208         * @event Component#componentresize
4209         * @type {EventTarget~Event}
4210         */
4211        this.trigger('componentresize');
4212      }
4213
4214      return;
4215    }
4216
4217    // Not setting a value, so getting it
4218    // Make sure element exists
4219    if (!this.el_) {
4220      return 0;
4221    }
4222
4223    // Get dimension value from style
4224    var val = this.el_.style[widthOrHeight];
4225    var pxIndex = val.indexOf('px');
4226
4227    if (pxIndex !== -1) {
4228      // Return the pixel value with no 'px'
4229      return parseInt(val.slice(0, pxIndex), 10);
4230    }
4231
4232    // No px so using % or no style was set, so falling back to offsetWidth/height
4233    // If component has display:none, offset will return 0
4234    // TODO: handle display:none and no dimension style using px
4235    return parseInt(this.el_['offset' + toTitleCase(widthOrHeight)], 10);
4236  };
4237
4238  /**
4239   * Get the width or the height of the `Component` elements computed style. Uses
4240   * `window.getComputedStyle`.
4241   *
4242   * @param {string} widthOrHeight
4243   *        A string containing 'width' or 'height'. Whichever one you want to get.
4244   *
4245   * @return {number}
4246   *         The dimension that gets asked for or 0 if nothing was set
4247   *         for that dimension.
4248   */
4249
4250
4251  Component.prototype.currentDimension = function currentDimension(widthOrHeight) {
4252    var computedWidthOrHeight = 0;
4253
4254    if (widthOrHeight !== 'width' && widthOrHeight !== 'height') {
4255      throw new Error('currentDimension only accepts width or height value');
4256    }
4257
4258    if (typeof 
vendor: 11,811 bytes, lines 4258-4607
4258window_1.getComputedStyle === 'function') {
4259      var computedStyle = window_1.getComputedStyle(this.el_);
4260
4261      computedWidthOrHeight = computedStyle.getPropertyValue(widthOrHeight) || computedStyle[widthOrHeight];
4262    }
4263
4264    // remove 'px' from variable and parse as integer
4265    computedWidthOrHeight = parseFloat(computedWidthOrHeight);
4266
4267    // if the computed value is still 0, it's possible that the browser is lying
4268    // and we want to check the offset values.
4269    // This code also runs on IE8 and wherever getComputedStyle doesn't exist.
4270    if (computedWidthOrHeight === 0) {
4271      var rule = 'offset' + toTitleCase(widthOrHeight);
4272
4273      computedWidthOrHeight = this.el_[rule];
4274    }
4275
4276    return computedWidthOrHeight;
4277  };
4278
4279  /**
4280   * An object that contains width and height values of the `Component`s
4281   * computed style. Uses `window.getComputedStyle`.
4282   *
4283   * @typedef {Object} Component~DimensionObject
4284   *
4285   * @property {number} width
4286   *           The width of the `Component`s computed style.
4287   *
4288   * @property {number} height
4289   *           The height of the `Component`s computed style.
4290   */
4291
4292  /**
4293   * Get an object that contains width and height values of the `Component`s
4294   * computed style.
4295   *
4296   * @return {Component~DimensionObject}
4297   *         The dimensions of the components element
4298   */
4299
4300
4301  Component.prototype.currentDimensions = function currentDimensions() {
4302    return {
4303      width: this.currentDimension('width'),
4304      height: this.currentDimension('height')
4305    };
4306  };
4307
4308  /**
4309   * Get the width of the `Component`s computed style. Uses `window.getComputedStyle`.
4310   *
4311   * @return {number} width
4312   *           The width of the `Component`s computed style.
4313   */
4314
4315
4316  Component.prototype.currentWidth = function currentWidth() {
4317    return this.currentDimension('width');
4318  };
4319
4320  /**
4321   * Get the height of the `Component`s computed style. Uses `window.getComputedStyle`.
4322   *
4323   * @return {number} height
4324   *           The height of the `Component`s computed style.
4325   */
4326
4327
4328  Component.prototype.currentHeight = function currentHeight() {
4329    return this.currentDimension('height');
4330  };
4331
4332  /**
4333   * Set the focus to this component
4334   */
4335
4336
4337  Component.prototype.focus = function focus() {
4338    this.el_.focus();
4339  };
4340
4341  /**
4342   * Remove the focus from this component
4343   */
4344
4345
4346  Component.prototype.blur = function blur() {
4347    this.el_.blur();
4348  };
4349
4350  /**
4351   * Emit a 'tap' events when touch event support gets detected. This gets used to
4352   * support toggling the controls through a tap on the video. They get enabled
4353   * because every sub-component would have extra overhead otherwise.
4354   *
4355   * @private
4356   * @fires Component#tap
4357   * @listens Component#touchstart
4358   * @listens Component#touchmove
4359   * @listens Component#touchleave
4360   * @listens Component#touchcancel
4361   * @listens Component#touchend
4362    */
4363
4364
4365  Component.prototype.emitTapEvents = function emitTapEvents() {
4366    // Track the start time so we can determine how long the touch lasted
4367    var touchStart = 0;
4368    var firstTouch = null;
4369
4370    // Maximum movement allowed during a touch event to still be considered a tap
4371    // Other popular libs use anywhere from 2 (hammer.js) to 15,
4372    // so 10 seems like a nice, round number.
4373    var tapMovementThreshold = 10;
4374
4375    // The maximum length a touch can be while still being considered a tap
4376    var touchTimeThreshold = 200;
4377
4378    var couldBeTap = void 0;
4379
4380    this.on('touchstart', function (event) {
4381      // If more than one finger, don't consider treating this as a click
4382      if (event.touches.length === 1) {
4383        // Copy pageX/pageY from the object
4384        firstTouch = {
4385          pageX: event.touches[0].pageX,
4386          pageY: event.touches[0].pageY
4387        };
4388        // Record start time so we can detect a tap vs. "touch and hold"
4389        touchStart = new Date().getTime();
4390        // Reset couldBeTap tracking
4391        couldBeTap = true;
4392      }
4393    });
4394
4395    this.on('touchmove', function (event) {
4396      // If more than one finger, don't consider treating this as a click
4397      if (event.touches.length > 1) {
4398        couldBeTap = false;
4399      } else if (firstTouch) {
4400        // Some devices will throw touchmoves for all but the slightest of taps.
4401        // So, if we moved only a small distance, this could still be a tap
4402        var xdiff = event.touches[0].pageX - firstTouch.pageX;
4403        var ydiff = event.touches[0].pageY - firstTouch.pageY;
4404        var touchDistance = Math.sqrt(xdiff * xdiff + ydiff * ydiff);
4405
4406        if (touchDistance > tapMovementThreshold) {
4407          couldBeTap = false;
4408        }
4409      }
4410    });
4411
4412    var noTap = function noTap() {
4413      couldBeTap = false;
4414    };
4415
4416    // TODO: Listen to the original target. http://youtu.be/DujfpXOKUp8?t=13m8s
4417    this.on('touchleave', noTap);
4418    this.on('touchcancel', noTap);
4419
4420    // When the touch ends, measure how long it took and trigger the appropriate
4421    // event
4422    this.on('touchend', function (event) {
4423      firstTouch = null;
4424      // Proceed only if the touchmove/leave/cancel event didn't happen
4425      if (couldBeTap === true) {
4426        // Measure how long the touch lasted
4427        var touchTime = new Date().getTime() - touchStart;
4428
4429        // Make sure the touch was less than the threshold to be considered a tap
4430        if (touchTime < touchTimeThreshold) {
4431          // Don't let browser turn this into a click
4432          event.preventDefault();
4433          /**
4434           * Triggered when a `Component` is tapped.
4435           *
4436           * @event Component#tap
4437           * @type {EventTarget~Event}
4438           */
4439          this.trigger('tap');
4440          // It may be good to copy the touchend event object and change the
4441          // type to tap, if the other event properties aren't exact after
4442          // Events.fixEvent runs (e.g. event.target)
4443        }
4444      }
4445    });
4446  };
4447
4448  /**
4449   * This function reports user activity whenever touch events happen. This can get
4450   * turned off by any sub-components that wants touch events to act another way.
4451   *
4452   * Report user touch activity when touch events occur. User activity gets used to
4453   * determine when controls should show/hide. It is simple when it comes to mouse
4454   * events, because any mouse event should show the controls. So we capture mouse
4455   * events that bubble up to the player and report activity when that happens.
4456   * With touch events it isn't as easy as `touchstart` and `touchend` toggle player
4457   * controls. So touch events can't help us at the player level either.
4458   *
4459   * User activity gets checked asynchronously. So what could happen is a tap event
4460   * on the video turns the controls off. Then the `touchend` event bubbles up to
4461   * the player. Which, if it reported user activity, would turn the controls right
4462   * back on. We also don't want to completely block touch events from bubbling up.
4463   * Furthermore a `touchmove` event and anything other than a tap, should not turn
4464   * controls back on.
4465   *
4466   * @listens Component#touchstart
4467   * @listens Component#touchmove
4468   * @listens Component#touchend
4469   * @listens Component#touchcancel
4470   */
4471
4472
4473  Component.prototype.enableTouchActivity = function enableTouchActivity() {
4474    // Don't continue if the root player doesn't support reporting user activity
4475    if (!this.player() || !this.player().reportUserActivity) {
4476      return;
4477    }
4478
4479    // listener for reporting that the user is active
4480    var report = bind(this.player(), this.player().reportUserActivity);
4481
4482    var touchHolding = void 0;
4483
4484    this.on('touchstart', function () {
4485      report();
4486      // For as long as the they are touching the device or have their mouse down,
4487      // we consider them active even if they're not moving their finger or mouse.
4488      // So we want to continue to update that they are active
4489      this.clearInterval(touchHolding);
4490      // report at the same interval as activityCheck
4491      touchHolding = this.setInterval(report, 250);
4492    });
4493
4494    var touchEnd = function touchEnd(event) {
4495      report();
4496      // stop the interval that maintains activity if the touch is holding
4497      this.clearInterval(touchHolding);
4498    };
4499
4500    this.on('touchmove', report);
4501    this.on('touchend', touchEnd);
4502    this.on('touchcancel', touchEnd);
4503  };
4504
4505  /**
4506   * A callback that has no parameters and is bound into `Component`s context.
4507   *
4508   * @callback Component~GenericCallback
4509   * @this Component
4510   */
4511
4512  /**
4513   * Creates a function that runs after an `x` millisecond timeout. This function is a
4514   * wrapper around `window.setTimeout`. There are a few reasons to use this one
4515   * instead though:
4516   * 1. It gets cleared via  {@link Component#clearTimeout} when
4517   *    {@link Component#dispose} gets called.
4518   * 2. The function callback will gets turned into a {@link Component~GenericCallback}
4519   *
4520   * > Note: You can't use `window.clearTimeout` on the id returned by this function. This
4521   *         will cause its dispose listener not to get cleaned up! Please use
4522   *         {@link Component#clearTimeout} or {@link Component#dispose} instead.
4523   *
4524   * @param {Component~GenericCallback} fn
4525   *        The function that will be run after `timeout`.
4526   *
4527   * @param {number} timeout
4528   *        Timeout in milliseconds to delay before executing the specified function.
4529   *
4530   * @return {number}
4531   *         Returns a timeout ID that gets used to identify the timeout. It can also
4532   *         get used in {@link Component#clearTimeout} to clear the timeout that
4533   *         was set.
4534   *
4535   * @listens Component#dispose
4536   * @see [Similar to]{@link https://developer.mozilla.org/en-US/docs/Web/API/WindowTimers/setTimeout}
4537   */
4538
4539
4540  Component.prototype.setTimeout = function setTimeout(fn, timeout) {
4541    var _this2 = this;
4542
4543    // declare as variables so they are properly available in timeout function
4544    // eslint-disable-next-line
4545    var timeoutId, disposeFn;
4546
4547    fn = bind(this, fn);
4548
4549    timeoutId = window_1.setTimeout(function () {
4550      _this2.off('dispose', disposeFn);
4551      fn();
4552    }, timeout);
4553
4554    disposeFn = function disposeFn() {
4555      return _this2.clearTimeout(timeoutId);
4556    };
4557
4558    disposeFn.guid = 'vjs-timeout-' + timeoutId;
4559
4560    this.on('dispose', disposeFn);
4561
4562    return timeoutId;
4563  };
4564
4565  /**
4566   * Clears a timeout that gets created via `window.setTimeout` or
4567   * {@link Component#setTimeout}. If you set a timeout via {@link Component#setTimeout}
4568   * use this function instead of `window.clearTimout`. If you don't your dispose
4569   * listener will not get cleaned up until {@link Component#dispose}!
4570   *
4571   * @param {number} timeoutId
4572   *        The id of the timeout to clear. The return value of
4573   *        {@link Component#setTimeout} or `window.setTimeout`.
4574   *
4575   * @return {number}
4576   *         Returns the timeout id that was cleared.
4577   *
4578   * @see [Similar to]{@link https://developer.mozilla.org/en-US/docs/Web/API/WindowTimers/clearTimeout}
4579   */
4580
4581
4582  Component.prototype.clearTimeout = function clearTimeout(timeoutId) {
4583    window_1.clearTimeout(timeoutId);
4584
4585    var disposeFn = function disposeFn() {};
4586
4587    disposeFn.guid = 'vjs-timeout-' + timeoutId;
4588
4589    this.off('dispose', disposeFn);
4590
4591    return timeoutId;
4592  };
4593
4594  /**
4595   * Creates a function that gets run every `x` milliseconds. This function is a wrapper
4596   * around `window.setInterval`. There are a few reasons to use this one instead though.
4597   * 1. It gets cleared via  {@link Component#clearInterval} when
4598   *    {@link Component#dispose} gets called.
4599   * 2. The function callback will be a {@link Component~GenericCallback}
4600   *
4601   * @param {Component~GenericCallback} fn
4602   *        The function to run every `x` seconds.
4603   *
4604   * @param {number} interval
4605   *        Execute the specified function every `x` milliseconds.
4606   *
4607   * @return {number}
4608   *         Returns an id that can be used to identify the interval. It can also be be used in
4609   *         {@link Component#clearInterval} to clear the interval.
4610   *
4611   * @listens Component#dispose
4612   * @see [Similar to]{@link https://developer.mozilla.org/en-US/docs/Web/API/WindowTimers/setInterval}
4613   */
4614
4615
4616  Component.prototype.setInterval = function setInterval(fn, interval) {
4617    var _this3 = this;
4618
4619    fn = bind(this, fn);
4620
4621    var intervalId = window_1.setInterval(fn, interval);
4622
4623    var disposeFn = function disposeFn() {
4624      return _this3.clearInterval(intervalId);
4625    };
4626
4627    disposeFn.guid = 'vjs-interval-' + intervalId;
4628
4629    this.on('dispose', disposeFn);
4630
4631    return intervalId;
4632  };
4633
4634  /**
4635   * Clears an interval that gets created via `window.setInterval` or
4636   * {@link Component#setInterval}. If you set an inteval via {@link Component#setInterval}
4637   * use this function instead of `window.clearInterval`. If you don't your dispose
4638   * listener will not get cleaned up until {@link Component#dispose}!
4639   *
4640   * @param {number} intervalId
4641   *        The id of the interval to clear. The return value of
4642   *        {@link Component#setInterval} or `window.setInterval`.
4643   *
4644   * @return {number}
4645   *         Returns the interval id that was cleared.
4646   *
4647   * @see [Similar to]{@link https://developer.mozilla.org/en-US/docs/Web/API/WindowTimers/clearInterval}
4648   */
4649
4650
4651  Component.prototype.clearInterval = function clearInterval(intervalId) {
4652    window_1.clearInterval(intervalId);
4653
4654    var disposeFn = function disposeFn() {};
4655
4656    disposeFn.guid = 'vjs-interval-' + intervalId;
4657
4658    this.off('dispose', disposeFn);
4659
4660    return intervalId;
4661  };
4662
4663  /**
4664   * Queues up a callback to be passed to requestAnimationFrame (rAF), but
4665   * with a few extra bonuses:
4666   *
4667   * - Supports browsers that do not support rAF by falling back to
4668   *   {@link Component#setTimeout}.
4669   *
4670   * - The callback is turned into a {@link Component~GenericCallback} (i.e.
4671   *   bound to the component).
4672   *
4673   * - Automatic cancellation of the rAF callback is handled if the component
4674   *   is disposed before it is called.
4675   *
4676   * @param  {Component~GenericCallback} fn
4677   *         A function that will be bound to this component and executed just
4678   *         before the browser's next repaint.
4679   *
4680   * @return {number}
4681   *         Returns an rAF ID that gets used to identify the timeout. It can
4682   *         also be used in {@link Component#cancelAnimationFrame} to cancel
4683   *         the animation frame callback.
4684   *
4685   * @listens Component#dispose
4686   * @see [Similar to]{@link https://developer.mozilla.org/en-US/docs/Web/API/window/requestAnimationFrame}
4687   */
4688
4689
4690  Component.prototype.requestAnimationFrame = function requestAnimationFrame(fn) {
4691    var _this4 = this;
4692
4693    // declare as variables so they are properly available in rAF function
4694    // eslint-disable-next-line
4695    var id, disposeFn;
4696
4697    if (this.supportsRaf_) {
4698      fn = bind(this, fn);
4699
4700      id = window_1.requestAnimationFrame(function () {
4701        _this4.off('dispose', disposeFn);
4702        fn();
4703      });
4704
4705      disposeFn = function disposeFn() {
4706        return _this4.cancelAnimationFrame(id);
4707      };
4708
4709      disposeFn.guid = 'vjs-raf-' + id;
4710      this.on('dispose', disposeFn);
4711
4712      return id;
4713    }
4714
4715    // Fall back to using a timer.
4716    return this.setTimeout(fn, 1000 / 60);
4717  };
4718
4719  /**
4720   * Cancels a queued callback passed to {@link Component#requestAnimationFrame}
4721   * (rAF).
4722   *
4723   * If you queue an rAF callback via {@link Component#requestAnimationFrame},
4724   * use this function instead of `window.cancelAnimationFrame`. If you don't,
4725   * your dispose listener will not get cleaned up until {@link Component#dispose}!
4726   *
4727   * @param {number} id
4728   *        The rAF ID to clear. The return value of {@link Component#requestAnimationFrame}.
4729   *
4730   * @return {number}
4731   *         Returns the rAF ID that was cleared.
4732   *
4733   * @see [Similar to]{@link https://developer.mozilla.org/en-US/docs/Web/API/window/cancelAnimationFrame}
4734   */
4735
4736
4737  Component.prototype.cancelAnimationFrame = function cancelAnimationFrame(id) {
4738    if (this.supportsRaf_) {
4739      window_1.cancelAnimationFrame(id);
4740
4741      var disposeFn = function disposeFn() {};
4742
4743      disposeFn.guid = 'vjs-raf-' + id;
4744
4745      this.off('dispose', disposeFn);
4746
4747      return id;
4748    }
4749
4750    // Fall back to using a timer.
4751    return this.clearTimeout(id);
4752  };
4753
4754  /**
4755   * Register a `Component` with `videojs` given the name and the component.
4756   *
4757   * > NOTE: {@link Tech}s should not be registered as a `Component`. {@link Tech}s
4758   *         should be registered using {@link Tech.registerTech} or
4759   *         {@link videojs:videojs.registerTech}.
4760   *
4761   * > NOTE: This function can also be seen on videojs as
4762   *         {@link videojs:videojs.registerComponent}.
4763   *
4764   * @param {string} name
4765   *        The name of the `Component` to register.
4766   *
4767   * @param {Component} ComponentToRegister
4768   *        The `Component` class to register.
4769   *
4770   * @return {Component}
4771   *         The `Component` that was registered.
4772   */
4773
4774
4775  Component.registerComponent = function registerComponent(name, ComponentToRegister) {
4776    if (typeof name !== 'string' || !name) {
4777      throw new Error('Illegal component name, "' + name + '"; must be a non-empty string.');
4778    }
4779
4780    var Tech = Component.getComponent('Tech');
4781
4782    // We need to make sure this check is only done if Tech has been registered.
4783    var isTech = Tech && Tech.isTech(ComponentToRegister);
4784    var isComp = Component === ComponentToRegister || Component.prototype.isPrototypeOf(ComponentToRegister.prototype);
4785
4786    if (isTech || !isComp) {
4787      var reason = void 0;
4788
4789      if (isTech) {
4790        reason = 'techs must be registered using Tech.registerTech()';
4791      } else {
4792        reason = 'must be a Component subclass';
4793      }
4794
4795      throw new Error('Illegal component, "' + name + '"; ' + reason + '.');
4796    }
4797
4798    name = toTitleCase(name);
4799
4800    if (!Component.components_) {
4801      Component.components_ = {};
4802    }
4803
4804    var Player = Component.getComponent('Player');
4805
4806    if (name === 'Player' && Player && Player.players) {
4807      var players = Player.players;
4808      var playerNames = Object.keys(players);
4809
4810      // If we have players that were disposed, then their name will still be
4811      // in Players.players. So, we must loop through and verify that the value
4812      // for each item is not null. This allows registration of the Player component
4813      // after all players have been disposed or before any were created.
4814      if (players && playerNames.length > 0 && playerNames.map(function (pname) {
4815        return players[pname];
4816      }).every(Boolean)) {
4817        throw new Error('Can not register Player component after player has been created.');
4818      }
4819    }
4820
4821    Component.components_[name] = ComponentToRegister;
4822
4823    return ComponentToRegister;
4824  };
4825
4826  /**
4827   * Get a `Component` based on the name it was registered with.
4828   *
4829   * @param {string} name
4830   *        The Name of the component to get.
4831   *
4832   * @return {Component}
4833   *         The `Component` that got registered under the given name.
4834   *
4835   * @deprecated In `videojs` 6 this will not return `Component`s that were not
4836   *             registered using {@link Component.registerComponent}. Currently we
4837   *             check the global `videojs` object for a `Component` name and
4838   *             return that if it exists.
4839   */
4840
4841
4842  Component.getComponent = function getComponent(name) {
4843    if (!name) {
4844      return;
4845    }
4846
4847    name = toTitleCase(name);
4848
4849    if (Component.components_ && Component.components_[name]) {
4850      return Component.components_[name];
4851    }
4852  };
4853
4854  return Component;
4855}();
4856
4857/**
4858 * Whether or not this component supports `requestAnimationFrame`.
4859 *
4860 * This is exposed primarily for testing purposes.
4861 *
4862 * @private
4863 * @type {Boolean}
4864 */
4865
4866
4867Component.prototype.supportsRaf_ = typeof window_1.requestAnimationFrame === 'function' && typeof window_1.cancelAnimationFrame === 'function';
4868
4869Component.registerComponent('Component', Component);
4870
4871/**
4872 * @file time-ranges.js
4873 * @module time-ranges
4874 */
4875
4876/**
4877 * Returns the time for the specified index at the start or end
4878 * of a TimeRange object.
4879 *
4880 * @function time-ranges:indexFunction
4881 *
4882 * @param {number} [index=0]
4883 *        The range number to return the time for.
4884 *
4885 * @return {number}
4886 *         The time that offset at the specified index.
4887 *
4888 * @depricated index must be set to a value, in the future this will throw an error.
4889 */
4890
4891/**
4892 * An object that contains ranges of time for various reasons.
4893 *
4894 * @typedef {Object} TimeRange
4895 *
4896 * @property {number} length
4897 *           The number of time ranges represented by this Object
4898 *
4899 * @property {time-ranges:indexFunction} start
4900 *           Returns the time offset at which a specified time range begins.
4901 *
4902 * @property {time-ranges:indexFunction} end
4903 *           Returns the time offset at which a specified time range ends.
4904 *
4905 * @see https://developer.mozilla.org/en-US/docs/Web/API/TimeRanges
4906 */
4907
vendor: 6,234 bytes, lines 4908-5119
4908/**
4909 * Check if any of the time ranges are over the maximum index.
4910 *
4911 * @param {string} fnName
4912 *        The function name to use for logging
4913 *
4914 * @param {number} index
4915 *        The index to check
4916 *
4917 * @param {number} maxIndex
4918 *        The maximum possible index
4919 *
4920 * @throws {Error} if the timeRanges provided are over the maxIndex
4921 */
4922function rangeCheck(fnName, index, maxIndex) {
4923  if (typeof index !== 'number' || index < 0 || index > maxIndex) {
4924    throw new Error('Failed to execute \'' + fnName + '\' on \'TimeRanges\': The index provided (' + index + ') is non-numeric or out of bounds (0-' + maxIndex + ').');
4925  }
4926}
4927
4928/**
4929 * Get the time for the specified index at the start or end
4930 * of a TimeRange object.
4931 *
4932 * @param {string} fnName
4933 *        The function name to use for logging
4934 *
4935 * @param {string} valueIndex
4936 *        The proprety that should be used to get the time. should be 'start' or 'end'
4937 *
4938 * @param {Array} ranges
4939 *        An array of time ranges
4940 *
4941 * @param {Array} [rangeIndex=0]
4942 *        The index to start the search at
4943 *
4944 * @return {number}
4945 *         The time that offset at the specified index.
4946 *
4947 *
4948 * @depricated rangeIndex must be set to a value, in the future this will throw an error.
4949 * @throws {Error} if rangeIndex is more than the length of ranges
4950 */
4951function getRange(fnName, valueIndex, ranges, rangeIndex) {
4952  rangeCheck(fnName, rangeIndex, ranges.length - 1);
4953  return ranges[rangeIndex][valueIndex];
4954}
4955
4956/**
4957 * Create a time range object given ranges of time.
4958 *
4959 * @param {Array} [ranges]
4960 *        An array of time ranges.
4961 */
4962function createTimeRangesObj(ranges) {
4963  if (ranges === undefined || ranges.length === 0) {
4964    return {
4965      length: 0,
4966      start: function start() {
4967        throw new Error('This TimeRanges object is empty');
4968      },
4969      end: function end() {
4970        throw new Error('This TimeRanges object is empty');
4971      }
4972    };
4973  }
4974  return {
4975    length: ranges.length,
4976    start: getRange.bind(null, 'start', 0, ranges),
4977    end: getRange.bind(null, 'end', 1, ranges)
4978  };
4979}
4980
4981/**
4982 * Should create a fake `TimeRange` object which mimics an HTML5 time range instance.
4983 *
4984 * @param {number|Array} start
4985 *        The start of a single range or an array of ranges
4986 *
4987 * @param {number} end
4988 *        The end of a single range.
4989 *
4990 * @private
4991 */
4992function createTimeRanges(start, end) {
4993  if (Array.isArray(start)) {
4994    return createTimeRangesObj(start);
4995  } else if (start === undefined || end === undefined) {
4996    return createTimeRangesObj();
4997  }
4998  return createTimeRangesObj([[start, end]]);
4999}
5000
5001/**
5002 * @file buffer.js
5003 * @module buffer
5004 */
5005/**
5006 * Compute the percentage of the media that has been buffered.
5007 *
5008 * @param {TimeRange} buffered
5009 *        The current `TimeRange` object representing buffered time ranges
5010 *
5011 * @param {number} duration
5012 *        Total duration of the media
5013 *
5014 * @return {number}
5015 *         Percent buffered of the total duration in decimal form.
5016 */
5017function bufferedPercent(buffered, duration) {
5018  var bufferedDuration = 0;
5019  var start = void 0;
5020  var end = void 0;
5021
5022  if (!duration) {
5023    return 0;
5024  }
5025
5026  if (!buffered || !buffered.length) {
5027    buffered = createTimeRanges(0, 0);
5028  }
5029
5030  for (var i = 0; i < buffered.length; i++) {
5031    start = buffered.start(i);
5032    end = buffered.end(i);
5033
5034    // buffered end can be bigger than duration by a very small fraction
5035    if (end > duration) {
5036      end = duration;
5037    }
5038
5039    bufferedDuration += end - start;
5040  }
5041
5042  return bufferedDuration / duration;
5043}
5044
5045/**
5046 * @file fullscreen-api.js
5047 * @module fullscreen-api
5048 * @private
5049 */
5050/**
5051 * Store the browser-specific methods for the fullscreen API.
5052 *
5053 * @type {Object}
5054 * @see [Specification]{@link https://fullscreen.spec.whatwg.org}
5055 * @see [Map Approach From Screenfull.js]{@link https://github.com/sindresorhus/screenfull.js}
5056 */
5057var FullscreenApi = {};
5058
5059// browser API methods
5060var apiMap = [['requestFullscreen', 'exitFullscreen', 'fullscreenElement', 'fullscreenEnabled', 'fullscreenchange', 'fullscreenerror'],
5061// WebKit
5062['webkitRequestFullscreen', 'webkitExitFullscreen', 'webkitFullscreenElement', 'webkitFullscreenEnabled', 'webkitfullscreenchange', 'webkitfullscreenerror'],
5063// Old WebKit (Safari 5.1)
5064['webkitRequestFullScreen', 'webkitCancelFullScreen', 'webkitCurrentFullScreenElement', 'webkitCancelFullScreen', 'webkitfullscreenchange', 'webkitfullscreenerror'],
5065// Mozilla
5066['mozRequestFullScreen', 'mozCancelFullScreen', 'mozFullScreenElement', 'mozFullScreenEnabled', 'mozfullscreenchange', 'mozfullscreenerror'],
5067// Microsoft
5068['msRequestFullscreen', 'msExitFullscreen', 'msFullscreenElement', 'msFullscreenEnabled', 'MSFullscreenChange', 'MSFullscreenError']];
5069
5070var specApi = apiMap[0];
5071var browserApi = void 0;
5072
5073// determine the supported set of functions
5074for (var i = 0; i < apiMap.length; i++) {
5075  // check for exitFullscreen function
5076  if (apiMap[i][1] in document_1) {
5077    browserApi = apiMap[i];
5078    break;
5079  }
5080}
5081
5082// map the browser API names to the spec API names
5083if (browserApi) {
5084  for (var _i = 0; _i < browserApi.length; _i++) {
5085    FullscreenApi[specApi[_i]] = browserApi[_i];
5086  }
5087}
5088
5089/**
5090 * @file media-error.js
5091 */
5092/**
5093 * A Custom `MediaError` class which mimics the standard HTML5 `MediaError` class.
5094 *
5095 * @param {number|string|Object|MediaError} value
5096 *        This can be of multiple types:
5097 *        - number: should be a standard error code
5098 *        - string: an error message (the code will be 0)
5099 *        - Object: arbitrary properties
5100 *        - `MediaError` (native): used to populate a video.js `MediaError` object
5101 *        - `MediaError` (video.js): will return itself if it's already a
5102 *          video.js `MediaError` object.
5103 *
5104 * @see [MediaError Spec]{@link https://dev.w3.org/html5/spec-author-view/video.html#mediaerror}
5105 * @see [Encrypted MediaError Spec]{@link https://www.w3.org/TR/2013/WD-encrypted-media-20130510/#error-codes}
5106 *
5107 * @class MediaError
5108 */
5109function MediaError(value) {
5110
5111  // Allow redundant calls to this constructor to avoid having `instanceof`
5112  // checks peppered around the code.
5113  if (value instanceof MediaError) {
5114    return value;
5115  }
5116
5117  if (typeof value === 'number') {
5118    this.code = value;
5119  } else if (typeof value === 'string') {
vendor: 4,783 bytes, lines 5120-5284
5120    // default code is zero, so this is a custom error
5121    this.message = value;
5122  } else if (isObject(value)) {
5123
5124    // We assign the `code` property manually because native `MediaError` objects
5125    // do not expose it as an own/enumerable property of the object.
5126    if (typeof value.code === 'number') {
5127      this.code = value.code;
5128    }
5129
5130    assign(this, value);
5131  }
5132
5133  if (!this.message) {
5134    this.message = MediaError.defaultMessages[this.code] || '';
5135  }
5136}
5137
5138/**
5139 * The error code that refers two one of the defined `MediaError` types
5140 *
5141 * @type {Number}
5142 */
5143MediaError.prototype.code = 0;
5144
5145/**
5146 * An optional message that to show with the error. Message is not part of the HTML5
5147 * video spec but allows for more informative custom errors.
5148 *
5149 * @type {String}
5150 */
5151MediaError.prototype.message = '';
5152
5153/**
5154 * An optional status code that can be set by plugins to allow even more detail about
5155 * the error. For example a plugin might provide a specific HTTP status code and an
5156 * error message for that code. Then when the plugin gets that error this class will
5157 * know how to display an error message for it. This allows a custom message to show
5158 * up on the `Player` error overlay.
5159 *
5160 * @type {Array}
5161 */
5162MediaError.prototype.status = null;
5163
5164/**
5165 * Errors indexed by the W3C standard. The order **CANNOT CHANGE**! See the
5166 * specification listed under {@link MediaError} for more information.
5167 *
5168 * @enum {array}
5169 * @readonly
5170 * @property {string} 0 - MEDIA_ERR_CUSTOM
5171 * @property {string} 1 - MEDIA_ERR_CUSTOM
5172 * @property {string} 2 - MEDIA_ERR_ABORTED
5173 * @property {string} 3 - MEDIA_ERR_NETWORK
5174 * @property {string} 4 - MEDIA_ERR_SRC_NOT_SUPPORTED
5175 * @property {string} 5 - MEDIA_ERR_ENCRYPTED
5176 */
5177MediaError.errorTypes = ['MEDIA_ERR_CUSTOM', 'MEDIA_ERR_ABORTED', 'MEDIA_ERR_NETWORK', 'MEDIA_ERR_DECODE', 'MEDIA_ERR_SRC_NOT_SUPPORTED', 'MEDIA_ERR_ENCRYPTED'];
5178
5179/**
5180 * The default `MediaError` messages based on the {@link MediaError.errorTypes}.
5181 *
5182 * @type {Array}
5183 * @constant
5184 */
5185MediaError.defaultMessages = {
5186  1: 'You aborted the media playback',
5187  2: 'A network error caused the media download to fail part-way.',
5188  3: 'The media playback was aborted due to a corruption problem or because the media used features your browser did not support.',
5189  4: 'The media could not be loaded, either because the server or network failed or because the format is not supported.',
5190  5: 'The media is encrypted and we do not have the keys to decrypt it.'
5191};
5192
5193// Add types as properties on MediaError
5194// e.g. MediaError.MEDIA_ERR_SRC_NOT_SUPPORTED = 4;
5195for (var errNum = 0; errNum < MediaError.errorTypes.length; errNum++) {
5196  MediaError[MediaError.errorTypes[errNum]] = errNum;
5197  // values should be accessible on both the class and instance
5198  MediaError.prototype[MediaError.errorTypes[errNum]] = errNum;
5199}
5200
5201var tuple = SafeParseTuple;
5202
5203function SafeParseTuple(obj, reviver) {
5204    var json;
5205    var error = null;
5206
5207    try {
5208        json = JSON.parse(obj, reviver);
5209    } catch (err) {
5210        error = err;
5211    }
5212
5213    return [error, json]
5214}
5215
5216/**
5217 * Returns whether an object is `Promise`-like (i.e. has a `then` method).
5218 *
5219 * @param  {Object}  value
5220 *         An object that may or may not be `Promise`-like.
5221 *
5222 * @return {Boolean}
5223 *         Whether or not the object is `Promise`-like.
5224 */
5225function isPromise(value) {
5226  return value !== undefined && value !== null && typeof value.then === 'function';
5227}
5228
5229/**
5230 * Silence a Promise-like object.
5231 *
5232 * This is useful for avoiding non-harmful, but potentially confusing "uncaught
5233 * play promise" rejection error messages.
5234 *
5235 * @param  {Object} value
5236 *         An object that may or may not be `Promise`-like.
5237 */
5238function silencePromise(value) {
5239  if (isPromise(value)) {
5240    value.then(null, function (e) {});
5241  }
5242}
5243
5244/**
5245 * @file text-track-list-converter.js Utilities for capturing text track state and
5246 * re-creating tracks based on a capture.
5247 *
5248 * @module text-track-list-converter
5249 */
5250
5251/**
5252 * Examine a single {@link TextTrack} and return a JSON-compatible javascript object that
5253 * represents the {@link TextTrack}'s state.
5254 *
5255 * @param {TextTrack} track
5256 *        The text track to query.
5257 *
5258 * @return {Object}
5259 *         A serializable javascript representation of the TextTrack.
5260 * @private
5261 */
5262var trackToJson_ = function trackToJson_(track) {
5263  var ret = ['kind', 'label', 'language', 'id', 'inBandMetadataTrackDispatchType', 'mode', 'src'].reduce(function (acc, prop, i) {
5264
5265    if (track[prop]) {
5266      acc[prop] = track[prop];
5267    }
5268
5269    return acc;
5270  }, {
5271    cues: track.cues && Array.prototype.map.call(track.cues, function (cue) {
5272      return {
5273        startTime: cue.startTime,
5274        endTime: cue.endTime,
5275        text: cue.text,
5276        id: cue.id
5277      };
5278    })
5279  });
5280
5281  return ret;
5282};
5283
5284/**
5285 * Examine a {@link Tech} and return a JSON-compatible javascript array that represents the
5286 * state of all {@link TextTrack}s currently configured. The return array is compatible with
5287 * {@link text-track-list-converter:jsonToTextTracks}.
5288 *
5289 * @param {Tech} tech
5290 *        The tech object to query
5291 *
5292 * @return {Array}
5293 *         A serializable javascript representation of the {@link Tech}s
5294 *         {@link TextTrackList}.
5295 */
5296var textTracksToJson = function textTracksToJson(tech) {
5297
5298  var trackEls = tech.$$('track');
5299
5300  var trackObjs = Array.prototype.map.call(trackEls, function (t) {
5301    return t.track;
5302  });
5303  var tracks = Array.prototype.map.call(trackEls, function (trackEl) {
5304    var json = trackToJson_(trackEl.track);
5305
5306    if (trackEl.src) {
5307      json.src = trackEl.src;
5308    }
5309    return json;
5310  });
5311
5312  return tracks.concat(Array.prototype.filter.call(tech.textTracks(), function (track) {
5313    return trackObjs.indexOf(track) === -1;
5314  }).map(trackToJson_));
5315};
5316
5317/**
5318 * Create a set of remote {@link TextTrack}s on a {@link Tech} based on an array of javascript
5319 * object {@link TextTrack} representations.
5320 *
5321 * @param {Array} json
5322 *        An array of `TextTrack` representation objects, like those that would be
5323 *        produced by `textTracksToJson`.
5324 *
5325 * @param {Tech} tech
5326 *        The `Tech` to create the `TextTrack`s on.
5327 */
5328var jsonToTextTracks = function jsonToTextTracks(json, tech) {
5329  json.forEach(function (track) {
5330    var addedTrack = tech.addRemoteTextTrack(track).track;
5331
5332    if (!track.src && track.cues) {
5333      track.cues.forEach(function (cue) {
5334        return addedTrack.addCue(cue);
5335      });
5336    }
5337  });
5338
5339  return tech.textTracks();
5340};
5341
5342var textTrackConverter = { textTracksToJson: textTracksToJson, jsonToTextTracks: jsonToTextTracks, trackToJson_: trackToJson_ };
5343
5344/**
5345 * @file modal-dialog.js
5346 */
vendor: 12,535 bytes, lines 5346-5805
5346
5347var MODAL_CLASS_NAME = 'vjs-modal-dialog';
5348var ESC = 27;
5349
5350/**
5351 * The `ModalDialog` displays over the video and its controls, which blocks
5352 * interaction with the player until it is closed.
5353 *
5354 * Modal dialogs include a "Close" button and will close when that button
5355 * is activated - or when ESC is pressed anywhere.
5356 *
5357 * @extends Component
5358 */
5359
5360var ModalDialog = function (_Component) {
5361  inherits(ModalDialog, _Component);
5362
5363  /**
5364   * Create an instance of this class.
5365   *
5366   * @param {Player} player
5367   *        The `Player` that this class should be attached to.
5368   *
5369   * @param {Object} [options]
5370   *        The key/value store of player options.
5371   *
5372   * @param {Mixed} [options.content=undefined]
5373   *        Provide customized content for this modal.
5374   *
5375   * @param {string} [options.description]
5376   *        A text description for the modal, primarily for accessibility.
5377   *
5378   * @param {boolean} [options.fillAlways=false]
5379   *        Normally, modals are automatically filled only the first time
5380   *        they open. This tells the modal to refresh its content
5381   *        every time it opens.
5382   *
5383   * @param {string} [options.label]
5384   *        A text label for the modal, primarily for accessibility.
5385   *
5386   * @param {boolean} [options.temporary=true]
5387   *        If `true`, the modal can only be opened once; it will be
5388   *        disposed as soon as it's closed.
5389   *
5390   * @param {boolean} [options.uncloseable=false]
5391   *        If `true`, the user will not be able to close the modal
5392   *        through the UI in the normal ways. Programmatic closing is
5393   *        still possible.
5394   */
5395  function ModalDialog(player, options) {
5396    classCallCheck(this, ModalDialog);
5397
5398    var _this = possibleConstructorReturn(this, _Component.call(this, player, options));
5399
5400    _this.opened_ = _this.hasBeenOpened_ = _this.hasBeenFilled_ = false;
5401
5402    _this.closeable(!_this.options_.uncloseable);
5403    _this.content(_this.options_.content);
5404
5405    // Make sure the contentEl is defined AFTER any children are initialized
5406    // because we only want the contents of the modal in the contentEl
5407    // (not the UI elements like the close button).
5408    _this.contentEl_ = createEl('div', {
5409      className: MODAL_CLASS_NAME + '-content'
5410    }, {
5411      role: 'document'
5412    });
5413
5414    _this.descEl_ = createEl('p', {
5415      className: MODAL_CLASS_NAME + '-description vjs-control-text',
5416      id: _this.el().getAttribute('aria-describedby')
5417    });
5418
5419    textContent(_this.descEl_, _this.description());
5420    _this.el_.appendChild(_this.descEl_);
5421    _this.el_.appendChild(_this.contentEl_);
5422    return _this;
5423  }
5424
5425  /**
5426   * Create the `ModalDialog`'s DOM element
5427   *
5428   * @return {Element}
5429   *         The DOM element that gets created.
5430   */
5431
5432
5433  ModalDialog.prototype.createEl = function createEl$$1() {
5434    return _Component.prototype.createEl.call(this, 'div', {
5435      className: this.buildCSSClass(),
5436      tabIndex: -1
5437    }, {
5438      'aria-describedby': this.id() + '_description',
5439      'aria-hidden': 'true',
5440      'aria-label': this.label(),
5441      'role': 'dialog'
5442    });
5443  };
5444
5445  ModalDialog.prototype.dispose = function dispose() {
5446    this.contentEl_ = null;
5447    this.descEl_ = null;
5448    this.previouslyActiveEl_ = null;
5449
5450    _Component.prototype.dispose.call(this);
5451  };
5452
5453  /**
5454   * Builds the default DOM `className`.
5455   *
5456   * @return {string}
5457   *         The DOM `className` for this object.
5458   */
5459
5460
5461  ModalDialog.prototype.buildCSSClass = function buildCSSClass() {
5462    return MODAL_CLASS_NAME + ' vjs-hidden ' + _Component.prototype.buildCSSClass.call(this);
5463  };
5464
5465  /**
5466   * Handles `keydown` events on the document, looking for ESC, which closes
5467   * the modal.
5468   *
5469   * @param {EventTarget~Event} e
5470   *        The keypress that triggered this event.
5471   *
5472   * @listens keydown
5473   */
5474
5475
5476  ModalDialog.prototype.handleKeyPress = function handleKeyPress(e) {
5477    if (e.which === ESC && this.closeable()) {
5478      this.close();
5479    }
5480  };
5481
5482  /**
5483   * Returns the label string for this modal. Primarily used for accessibility.
5484   *
5485   * @return {string}
5486   *         the localized or raw label of this modal.
5487   */
5488
5489
5490  ModalDialog.prototype.label = function label() {
5491    return this.localize(this.options_.label || 'Modal Window');
5492  };
5493
5494  /**
5495   * Returns the description string for this modal. Primarily used for
5496   * accessibility.
5497   *
5498   * @return {string}
5499   *         The localized or raw description of this modal.
5500   */
5501
5502
5503  ModalDialog.prototype.description = function description() {
5504    var desc = this.options_.description || this.localize('This is a modal window.');
5505
5506    // Append a universal closeability message if the modal is closeable.
5507    if (this.closeable()) {
5508      desc += ' ' + this.localize('This modal can be closed by pressing the Escape key or activating the close button.');
5509    }
5510
5511    return desc;
5512  };
5513
5514  /**
5515   * Opens the modal.
5516   *
5517   * @fires ModalDialog#beforemodalopen
5518   * @fires ModalDialog#modalopen
5519   */
5520
5521
5522  ModalDialog.prototype.open = function open() {
5523    if (!this.opened_) {
5524      var player = this.player();
5525
5526      /**
5527        * Fired just before a `ModalDialog` is opened.
5528        *
5529        * @event ModalDialog#beforemodalopen
5530        * @type {EventTarget~Event}
5531        */
5532      this.trigger('beforemodalopen');
5533      this.opened_ = true;
5534
5535      // Fill content if the modal has never opened before and
5536      // never been filled.
5537      if (this.options_.fillAlways || !this.hasBeenOpened_ && !this.hasBeenFilled_) {
5538        this.fill();
5539      }
5540
5541      // If the player was playing, pause it and take note of its previously
5542      // playing state.
5543      this.wasPlaying_ = !player.paused();
5544
5545      if (this.options_.pauseOnOpen && this.wasPlaying_) {
5546        player.pause();
5547      }
5548
5549      if (this.closeable()) {
5550        this.on(this.el_.ownerDocument, 'keydown', bind(this, this.handleKeyPress));
5551      }
5552
5553      // Hide controls and note if they were enabled.
5554      this.hadControls_ = player.controls();
5555      player.controls(false);
5556
5557      this.show();
5558      this.conditionalFocus_();
5559      this.el().setAttribute('aria-hidden', 'false');
5560
5561      /**
5562        * Fired just after a `ModalDialog` is opened.
5563        *
5564        * @event ModalDialog#modalopen
5565        * @type {EventTarget~Event}
5566        */
5567      this.trigger('modalopen');
5568      this.hasBeenOpened_ = true;
5569    }
5570  };
5571
5572  /**
5573   * If the `ModalDialog` is currently open or closed.
5574   *
5575   * @param  {boolean} [value]
5576   *         If given, it will open (`true`) or close (`false`) the modal.
5577   *
5578   * @return {boolean}
5579   *         the current open state of the modaldialog
5580   */
5581
5582
5583  ModalDialog.prototype.opened = function opened(value) {
5584    if (typeof value === 'boolean') {
5585      this[value ? 'open' : 'close']();
5586    }
5587    return this.opened_;
5588  };
5589
5590  /**
5591   * Closes the modal, does nothing if the `ModalDialog` is
5592   * not open.
5593   *
5594   * @fires ModalDialog#beforemodalclose
5595   * @fires ModalDialog#modalclose
5596   */
5597
5598
5599  ModalDialog.prototype.close = function close() {
5600    if (!this.opened_) {
5601      return;
5602    }
5603    var player = this.player();
5604
5605    /**
5606      * Fired just before a `ModalDialog` is closed.
5607      *
5608      * @event ModalDialog#beforemodalclose
5609      * @type {EventTarget~Event}
5610      */
5611    this.trigger('beforemodalclose');
5612    this.opened_ = false;
5613
5614    if (this.wasPlaying_ && this.options_.pauseOnOpen) {
5615      player.play();
5616    }
5617
5618    if (this.closeable()) {
5619      this.off(this.el_.ownerDocument, 'keydown', bind(this, this.handleKeyPress));
5620    }
5621
5622    if (this.hadControls_) {
5623      player.controls(true);
5624    }
5625
5626    this.hide();
5627    this.el().setAttribute('aria-hidden', 'true');
5628
5629    /**
5630      * Fired just after a `ModalDialog` is closed.
5631      *
5632      * @event ModalDialog#modalclose
5633      * @type {EventTarget~Event}
5634      */
5635    this.trigger('modalclose');
5636    this.conditionalBlur_();
5637
5638    if (this.options_.temporary) {
5639      this.dispose();
5640    }
5641  };
5642
5643  /**
5644   * Check to see if the `ModalDialog` is closeable via the UI.
5645   *
5646   * @param  {boolean} [value]
5647   *         If given as a boolean, it will set the `closeable` option.
5648   *
5649   * @return {boolean}
5650   *         Returns the final value of the closable option.
5651   */
5652
5653
5654  ModalDialog.prototype.closeable = function closeable(value) {
5655    if (typeof value === 'boolean') {
5656      var closeable = this.closeable_ = !!value;
5657      var close = this.getChild('closeButton');
5658
5659      // If this is being made closeable and has no close button, add one.
5660      if (closeable && !close) {
5661
5662        // The close button should be a child of the modal - not its
5663        // content element, so temporarily change the content element.
5664        var temp = this.contentEl_;
5665
5666        this.contentEl_ = this.el_;
5667        close = this.addChild('closeButton', { controlText: 'Close Modal Dialog' });
5668        this.contentEl_ = temp;
5669        this.on(close, 'close', this.close);
5670      }
5671
5672      // If this is being made uncloseable and has a close button, remove it.
5673      if (!closeable && close) {
5674        this.off(close, 'close', this.close);
5675        this.removeChild(close);
5676        close.dispose();
5677      }
5678    }
5679    return this.closeable_;
5680  };
5681
5682  /**
5683   * Fill the modal's content element with the modal's "content" option.
5684   * The content element will be emptied before this change takes place.
5685   */
5686
5687
5688  ModalDialog.prototype.fill = function fill() {
5689    this.fillWith(this.content());
5690  };
5691
5692  /**
5693   * Fill the modal's content element with arbitrary content.
5694   * The content element will be emptied before this change takes place.
5695   *
5696   * @fires ModalDialog#beforemodalfill
5697   * @fires ModalDialog#modalfill
5698   *
5699   * @param {Mixed} [content]
5700   *        The same rules apply to this as apply to the `content` option.
5701   */
5702
5703
5704  ModalDialog.prototype.fillWith = function fillWith(content) {
5705    var contentEl = this.contentEl();
5706    var parentEl = contentEl.parentNode;
5707    var nextSiblingEl = contentEl.nextSibling;
5708
5709    /**
5710     * Fired just before a `ModalDialog` is filled with content.
5711     *
5712     * @event ModalDialog#beforemodalfill
5713     * @type {EventTarget~Event}
5714     */
5715    this.trigger('beforemodalfill');
5716    this.hasBeenFilled_ = true;
5717
5718    // Detach the content element from the DOM before performing
5719    // manipulation to avoid modifying the live DOM multiple times.
5720    parentEl.removeChild(contentEl);
5721    this.empty();
5722    insertContent(contentEl, content);
5723    /**
5724     * Fired just after a `ModalDialog` is filled with content.
5725     *
5726     * @event ModalDialog#modalfill
5727     * @type {EventTarget~Event}
5728     */
5729    this.trigger('modalfill');
5730
5731    // Re-inject the re-filled content element.
5732    if (nextSiblingEl) {
5733      parentEl.insertBefore(contentEl, nextSiblingEl);
5734    } else {
5735      parentEl.appendChild(contentEl);
5736    }
5737
5738    // make sure that the close button is last in the dialog DOM
5739    var closeButton = this.getChild('closeButton');
5740
5741    if (closeButton) {
5742      parentEl.appendChild(closeButton.el_);
5743    }
5744  };
5745
5746  /**
5747   * Empties the content element. This happens anytime the modal is filled.
5748   *
5749   * @fires ModalDialog#beforemodalempty
5750   * @fires ModalDialog#modalempty
5751   */
5752
5753
5754  ModalDialog.prototype.empty = function empty() {
5755    /**
5756     * Fired just before a `ModalDialog` is emptied.
5757     *
5758     * @event ModalDialog#beforemodalempty
5759     * @type {EventTarget~Event}
5760     */
5761    this.trigger('beforemodalempty');
5762    emptyEl(this.contentEl());
5763
5764    /**
5765     * Fired just after a `ModalDialog` is emptied.
5766     *
5767     * @event ModalDialog#modalempty
5768     * @type {EventTarget~Event}
5769     */
5770    this.trigger('modalempty');
5771  };
5772
5773  /**
5774   * Gets or sets the modal content, which gets normalized before being
5775   * rendered into the DOM.
5776   *
5777   * This does not update the DOM or fill the modal, but it is called during
5778   * that process.
5779   *
5780   * @param  {Mixed} [value]
5781   *         If defined, sets the internal content value to be used on the
5782   *         next call(s) to `fill`. This value is normalized before being
5783   *         inserted. To "clear" the internal content value, pass `null`.
5784   *
5785   * @return {Mixed}
5786   *         The current content of the modal dialog
5787   */
5788
5789
5790  ModalDialog.prototype.content = function content(value) {
5791    if (typeof value !== 'undefined') {
5792      this.content_ = value;
5793    }
5794    return this.content_;
5795  };
5796
5797  /**
5798   * conditionally focus the modal dialog if focus was previously on the player.
5799   *
5800   * @private
5801   */
5802
5803
5804  ModalDialog.prototype.conditionalFocus_ = function conditionalFocus_() {
5805    var activeEl = 
vendor: 1,619 bytes, lines 5805-5878
5805document_1.activeElement;
5806    var playerEl = this.player_.el_;
5807
5808    this.previouslyActiveEl_ = null;
5809
5810    if (playerEl.contains(activeEl) || playerEl === activeEl) {
5811      this.previouslyActiveEl_ = activeEl;
5812
5813      this.focus();
5814
5815      this.on(document_1, 'keydown', this.handleKeyDown);
5816    }
5817  };
5818
5819  /**
5820   * conditionally blur the element and refocus the last focused element
5821   *
5822   * @private
5823   */
5824
5825
5826  ModalDialog.prototype.conditionalBlur_ = function conditionalBlur_() {
5827    if (this.previouslyActiveEl_) {
5828      this.previouslyActiveEl_.focus();
5829      this.previouslyActiveEl_ = null;
5830    }
5831
5832    this.off(document_1, 'keydown', this.handleKeyDown);
5833  };
5834
5835  /**
5836   * Keydown handler. Attached when modal is focused.
5837   *
5838   * @listens keydown
5839   */
5840
5841
5842  ModalDialog.prototype.handleKeyDown = function handleKeyDown(event) {
5843    // exit early if it isn't a tab key
5844    if (event.which !== 9) {
5845      return;
5846    }
5847
5848    var focusableEls = this.focusableEls_();
5849    var activeEl = this.el_.querySelector(':focus');
5850    var focusIndex = void 0;
5851
5852    for (var i = 0; i < focusableEls.length; i++) {
5853      if (activeEl === focusableEls[i]) {
5854        focusIndex = i;
5855        break;
5856      }
5857    }
5858
5859    if (document_1.activeElement === this.el_) {
5860      focusIndex = 0;
5861    }
5862
5863    if (event.shiftKey && focusIndex === 0) {
5864      focusableEls[focusableEls.length - 1].focus();
5865      event.preventDefault();
5866    } else if (!event.shiftKey && focusIndex === focusableEls.length - 1) {
5867      focusableEls[0].focus();
5868      event.preventDefault();
5869    }
5870  };
5871
5872  /**
5873   * get all focusable elements
5874   *
5875   * @private
5876   */
5877
5878
vendor: 6,242 bytes, lines 5879-6132
5879  ModalDialog.prototype.focusableEls_ = function focusableEls_() {
5880    var allChildren = this.el_.querySelectorAll('*');
5881
5882    return Array.prototype.filter.call(allChildren, function (child) {
5883      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');
5884    });
5885  };
5886
5887  return ModalDialog;
5888}(Component);
5889
5890/**
5891 * Default options for `ModalDialog` default options.
5892 *
5893 * @type {Object}
5894 * @private
5895 */
5896
5897
5898ModalDialog.prototype.options_ = {
5899  pauseOnOpen: true,
5900  temporary: true
5901};
5902
5903Component.registerComponent('ModalDialog', ModalDialog);
5904
5905/**
5906 * @file track-list.js
5907 */
5908/**
5909 * Common functionaliy between {@link TextTrackList}, {@link AudioTrackList}, and
5910 * {@link VideoTrackList}
5911 *
5912 * @extends EventTarget
5913 */
5914
5915var TrackList = function (_EventTarget) {
5916  inherits(TrackList, _EventTarget);
5917
5918  /**
5919   * Create an instance of this class
5920   *
5921   * @param {Track[]} tracks
5922   *        A list of tracks to initialize the list with.
5923   *
5924   * @param {Object} [list]
5925   *        The child object with inheritance done manually for ie8.
5926   *
5927   * @abstract
5928   */
5929  function TrackList() {
5930    var tracks = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : [];
5931
5932    var _ret;
5933
5934    var list = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : null;
5935    classCallCheck(this, TrackList);
5936
5937    var _this = possibleConstructorReturn(this, _EventTarget.call(this));
5938
5939    if (!list) {
5940      list = _this; // eslint-disable-line
5941      if (IS_IE8) {
5942        list = document_1.createElement('custom');
5943        for (var prop in TrackList.prototype) {
5944          if (prop !== 'constructor') {
5945            list[prop] = TrackList.prototype[prop];
5946          }
5947        }
5948      }
5949    }
5950
5951    list.tracks_ = [];
5952
5953    /**
5954     * @memberof TrackList
5955     * @member {number} length
5956     *         The current number of `Track`s in the this Trackist.
5957     * @instance
5958     */
5959    Object.defineProperty(list, 'length', {
5960      get: function get$$1() {
5961        return this.tracks_.length;
5962      }
5963    });
5964
5965    for (var i = 0; i < tracks.length; i++) {
5966      list.addTrack(tracks[i]);
5967    }
5968
5969    // must return the object, as for ie8 it will not be this
5970    // but a reference to a document object
5971    return _ret = list, possibleConstructorReturn(_this, _ret);
5972  }
5973
5974  /**
5975   * Add a {@link Track} to the `TrackList`
5976   *
5977   * @param {Track} track
5978   *        The audio, video, or text track to add to the list.
5979   *
5980   * @fires TrackList#addtrack
5981   */
5982
5983
5984  TrackList.prototype.addTrack = function addTrack(track) {
5985    var index = this.tracks_.length;
5986
5987    if (!('' + index in this)) {
5988      Object.defineProperty(this, index, {
5989        get: function get$$1() {
5990          return this.tracks_[index];
5991        }
5992      });
5993    }
5994
5995    // Do not add duplicate tracks
5996    if (this.tracks_.indexOf(track) === -1) {
5997      this.tracks_.push(track);
5998      /**
5999       * Triggered when a track is added to a track list.
6000       *
6001       * @event TrackList#addtrack
6002       * @type {EventTarget~Event}
6003       * @property {Track} track
6004       *           A reference to track that was added.
6005       */
6006      this.trigger({
6007        track: track,
6008        type: 'addtrack'
6009      });
6010    }
6011  };
6012
6013  /**
6014   * Remove a {@link Track} from the `TrackList`
6015   *
6016   * @param {Track} rtrack
6017   *        The audio, video, or text track to remove from the list.
6018   *
6019   * @fires TrackList#removetrack
6020   */
6021
6022
6023  TrackList.prototype.removeTrack = function removeTrack(rtrack) {
6024    var track = void 0;
6025
6026    for (var i = 0, l = this.length; i < l; i++) {
6027      if (this[i] === rtrack) {
6028        track = this[i];
6029        if (track.off) {
6030          track.off();
6031        }
6032
6033        this.tracks_.splice(i, 1);
6034
6035        break;
6036      }
6037    }
6038
6039    if (!track) {
6040      return;
6041    }
6042
6043    /**
6044     * Triggered when a track is removed from track list.
6045     *
6046     * @event TrackList#removetrack
6047     * @type {EventTarget~Event}
6048     * @property {Track} track
6049     *           A reference to track that was removed.
6050     */
6051    this.trigger({
6052      track: track,
6053      type: 'removetrack'
6054    });
6055  };
6056
6057  /**
6058   * Get a Track from the TrackList by a tracks id
6059   *
6060   * @param {String} id - the id of the track to get
6061   * @method getTrackById
6062   * @return {Track}
6063   * @private
6064   */
6065
6066
6067  TrackList.prototype.getTrackById = function getTrackById(id) {
6068    var result = null;
6069
6070    for (var i = 0, l = this.length; i < l; i++) {
6071      var track = this[i];
6072
6073      if (track.id === id) {
6074        result = track;
6075        break;
6076      }
6077    }
6078
6079    return result;
6080  };
6081
6082  return TrackList;
6083}(EventTarget);
6084
6085/**
6086 * Triggered when a different track is selected/enabled.
6087 *
6088 * @event TrackList#change
6089 * @type {EventTarget~Event}
6090 */
6091
6092/**
6093 * Events that can be called with on + eventName. See {@link EventHandler}.
6094 *
6095 * @property {Object} TrackList#allowedEvents_
6096 * @private
6097 */
6098
6099
6100TrackList.prototype.allowedEvents_ = {
6101  change: 'change',
6102  addtrack: 'addtrack',
6103  removetrack: 'removetrack'
6104};
6105
6106// emulate attribute EventHandler support to allow for feature detection
6107for (var event in TrackList.prototype.allowedEvents_) {
6108  TrackList.prototype['on' + event] = null;
6109}
6110
6111/**
6112 * @file audio-track-list.js
6113 */
6114/**
6115 * Anywhere we call this function we diverge from the spec
6116 * as we only support one enabled audiotrack at a time
6117 *
6118 * @param {AudioTrackList} list
6119 *        list to work on
6120 *
6121 * @param {AudioTrack} track
6122 *        The track to skip
6123 *
6124 * @private
6125 */
6126var disableOthers = function disableOthers(list, track) {
6127  for (var i = 0; i < list.length; i++) {
6128    if (!Object.keys(list[i]).length || track.id === list[i].id) {
6129      continue;
6130    }
6131    // another audio track is enabled, disable it
6132    list[i].enabled = false;
6133  }
6134};
6135
6136/**
6137 * The current list of {@link AudioTrack} for a media file.
6138 *
6139 * @see [Spec]{@link https://html.spec.whatwg.org/multipage/embedded-content.html#audiotracklist}
6140 * @extends TrackList
6141 */
6142
6143var AudioTrackList = function (_TrackList) {
6144  inherits(AudioTrackList, _TrackList);
6145
6146  /**
6147   * Create an instance of this class.
6148   *
6149   * @param {AudioTrack[]} [tracks=[]]
6150   *        A list of `AudioTrack` to instantiate the list with.
6151   */
6152  function AudioTrackList() {
6153    var _this, _ret;
6154
6155    var tracks = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : [];
6156    classCallCheck(this, AudioTrackList);
6157
6158    var list = void 0;
6159
6160    // make sure only 1 track is enabled
6161    // sorted from last index to first index
6162    for (var i = tracks.length - 1; i >= 0; i--) {
6163      if (tracks[i].enabled) {
6164        disableOthers(tracks, tracks[i]);
6165        break;
6166      }
6167    }
6168
6169    // IE8 forces us to implement inheritance ourselves
6170    // as it does not support Object.defineProperty properly
6171    if (IS_IE8) {
6172      list = document_1.createElement('custom');
6173      for (var prop in TrackList.prototype) {
6174        if (prop !== 'constructor') {
6175          list[prop] = TrackList.prototype[prop];
6176        }
6177      }
6178      for (var _prop in AudioTrackList.prototype) {
6179        if (_prop !== 'constructor') {
6180          list[_prop] = AudioTrackList.prototype[_prop];
6181        }
6182      }
6183    }
6184
6185    list = (_this = possibleConstructorReturn(this, _TrackList.call(this, tracks, list)), _this);
6186    list.changing_ = false;
6187
6188    return _ret = list, possibleConstructorReturn(_this, _ret);
6189  }
6190
6191  /**
6192   * Add an {@link AudioTrack} to the `AudioTrackList`.
6193   *
6194   * @param {AudioTrack} track
6195   *        The AudioTrack to add to the list
6196   *
6197   * @fires TrackList#addtrack
6198   */
6199
6200
6201  AudioTrackList.prototype.addTrack = function addTrack(track) {
6202    var _this2 = this;
6203
6204    if (track.enabled) {
6205      disableOthers(this, track);
6206    }
6207
6208    _TrackList.prototype.addTrack.call(this, track);
6209    // native tracks don't have this
6210    if (!track.addEventListener) {
6211      return;
6212    }
6213
6214    /**
6215     * @listens AudioTrack#enabledchange
6216     * @fires TrackList#change
6217     */
6218    track.addEventListener('enabledchange', function () {
6219      // when we are disabling other tracks (since we don't support
6220      // more than one track at a time) we will set changing_
6221      // to true so that we don't trigger additional change events
6222      if (_this2.changing_) {
6223        return;
6224      }
6225      _this2.changing_ = true;
6226      disableOthers(_this2, track);
6227      _this2.changing_ = false;
6228      _this2.trigger('change');
6229    });
6230  };
6231
6232  return AudioTrackList;
6233}(TrackList);
6234
6235/**
6236 * @file video-track-list.js
6237 */
6238/**
6239 * Un-select all other {@link VideoTrack}s that are selected.
6240 *
6241 * @param {VideoTrackList} list
6242 *        list to work on
6243 *
6244 * @param {VideoTrack} track
6245 *        The track to skip
6246 *
6247 * @private
6248 */
6249var disableOthers$1 = function disableOthers(list, track) {
6250  for (var i = 0; i < list.length; i++) {
6251    if (!Object.keys(list[i]).length || track.id === list[i].id) {
6252      continue;
6253    }
6254    // another video track is enabled, disable it
6255    list[i].selected = false;
6256  }
6257};
6258
6259/**
6260 * The current list of {@link VideoTrack} for a video.
6261 *
6262 * @see [Spec]{@link https://html.spec.whatwg.org/multipage/embedded-content.html#videotracklist}
6263 * @extends TrackList
6264 */
6265
6266var VideoTrackList = function (_TrackList) {
6267  inherits(VideoTrackList, _TrackList);
6268
6269  /**
6270   * Create an instance of this class.
6271   *
6272   * @param {VideoTrack[]} [tracks=[]]
6273   *        A list of `VideoTrack` to instantiate the list with.
6274   */
6275  function VideoTrackList() {
6276    var _this, _ret;
6277
6278    var tracks = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : [];
6279    classCallCheck(this, VideoTrackList);
6280
6281    var list = void 0;
6282
6283    // make sure only 1 track is enabled
6284    // sorted from last index to first index
6285    for (var i = tracks.length - 1; i >= 0; i--) {
6286      if (tracks[i].selected) {
6287        disableOthers$1(tracks, tracks[i]);
6288        break;
6289      }
6290    }
6291
6292    // IE8 forces us to implement inheritance ourselves
6293    // as it does not support Object.defineProperty properly
6294    if (IS_IE8) {
6295      list = document_1.createElement('custom');
6296      for (var prop in TrackList.prototype) {
6297        if (prop !== 'constructor') {
6298          list[prop] = TrackList.prototype[prop];
6299        }
6300      }
6301      for (var _prop in VideoTrackList.prototype) {
6302        if (_prop !== 'constructor') {
6303          list[_prop] = VideoTrackList.prototype[_prop];
6304        }
6305      }
6306    }
6307
6308    list = (_this = possibleConstructorReturn(this, _TrackList.call(this, tracks, list)), _this);
6309    list.changing_ = false;
6310
6311    /**
6312     * @member {number} VideoTrackList#selectedIndex
6313     *         The current index of the selected {@link VideoTrack`}.
6314     */
6315    Object.defineProperty(list, 'selectedIndex', {
6316      get: function get$$1() {
6317        for (var _i = 0; _i < this.length; _i++) {
6318          if (this[_i].selected) {
6319            return _i;
6320          }
6321        }
6322        return -1;
6323      },
6324      set: function set$$1() {}
6325    });
6326
6327    return _ret = list, possibleConstructorReturn(_this, _ret);
6328  }
6329
6330  /**
6331   * Add a {@link VideoTrack} to the `VideoTrackList`.
6332   *
6333   * @param {VideoTrack} track
6334   *        The VideoTrack to add to the list
6335   *
6336   * @fires TrackList#addtrack
6337   */
6338
6339
6340  VideoTrackList.prototype.addTrack = function addTrack(track) {
6341    var _this2 = this;
6342
6343    if (track.selected) {
6344      disableOthers$1(this, track);
6345    }
6346
6347    _TrackList.prototype.addTrack.call(this, track);
6348    // native tracks don't have this
6349    if (!track.addEventListener) {
6350      return;
6351    }
6352
6353    /**
6354     * @listens VideoTrack#selectedchange
6355     * @fires TrackList#change
6356     */
6357    track.addEventListener('selectedchange', function () {
6358      if (_this2.changing_) {
6359        return;
6360      }
6361      _this2.changing_ = true;
6362      disableOthers$1(_this2, track);
6363      _this2.changing_ = false;
6364      _this2.trigger('change');
6365    });
6366  };
6367
6368  return VideoTrackList;
6369}(TrackList);
6370
6371/**
6372 * @file text-track-list.js
6373 */
6374/**
6375 * The current list of {@link TextTrack} for a media file.
6376 *
6377 * @see [Spec]{@link https://html.spec.whatwg.org/multipage/embedded-content.html#texttracklist}
6378 * @extends TrackList
6379 */
6380
6381var TextTrackList = function (_TrackList) {
6382  inherits(TextTrackList, _TrackList);
6383
6384  /**
6385   * Create an instance of this class.
6386   *
6387   * @param {TextTrack[]} [tracks=[]]
6388   *        A list of `TextTrack` to instantiate the list with.
6389   */
6390  function TextTrackList() {
6391    var _this, _ret;
6392
6393    var tracks = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : [];
6394    classCallCheck(this, TextTrackList);
6395
6396    var list = void 0;
6397
6398    // IE8 forces us to implement inheritance ourselves
6399    // as it does not support Object.defineProperty properly
6400    if (IS_IE8) {
6401      list = document_1.createElement('custom');
6402      for (var prop in TrackList.prototype) {
6403        if (prop !== 'constructor') {
6404          list[prop] = TrackList.prototype[prop];
6405        }
6406      }
6407      for (var _prop in TextTrackList.prototype) {
6408        if (_prop !== 'constructor') {
6409          list[_prop] = TextTrackList.prototype[_prop];
6410        }
6411      }
6412    }
6413
vendor: 7,767 bytes, lines 6414-6748
6414    list = (_this = possibleConstructorReturn(this, _TrackList.call(this, tracks, list)), _this);
6415    return _ret = list, possibleConstructorReturn(_this, _ret);
6416  }
6417
6418  /**
6419   * Add a {@link TextTrack} to the `TextTrackList`
6420   *
6421   * @param {TextTrack} track
6422   *        The text track to add to the list.
6423   *
6424   * @fires TrackList#addtrack
6425   */
6426
6427
6428  TextTrackList.prototype.addTrack = function addTrack(track) {
6429    _TrackList.prototype.addTrack.call(this, track);
6430
6431    /**
6432     * @listens TextTrack#modechange
6433     * @fires TrackList#change
6434     */
6435    track.addEventListener('modechange', bind(this, function () {
6436      this.trigger('change');
6437    }));
6438
6439    var nonLanguageTextTrackKind = ['metadata', 'chapters'];
6440
6441    if (nonLanguageTextTrackKind.indexOf(track.kind) === -1) {
6442      track.addEventListener('modechange', bind(this, function () {
6443        this.trigger('selectedlanguagechange');
6444      }));
6445    }
6446  };
6447
6448  return TextTrackList;
6449}(TrackList);
6450
6451/**
6452 * @file html-track-element-list.js
6453 */
6454
6455/**
6456 * The current list of {@link HtmlTrackElement}s.
6457 */
6458
6459var HtmlTrackElementList = function () {
6460
6461  /**
6462   * Create an instance of this class.
6463   *
6464   * @param {HtmlTrackElement[]} [tracks=[]]
6465   *        A list of `HtmlTrackElement` to instantiate the list with.
6466   */
6467  function HtmlTrackElementList() {
6468    var trackElements = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : [];
6469    classCallCheck(this, HtmlTrackElementList);
6470
6471    var list = this; // eslint-disable-line
6472
6473    if (IS_IE8) {
6474      list = document_1.createElement('custom');
6475
6476      for (var prop in HtmlTrackElementList.prototype) {
6477        if (prop !== 'constructor') {
6478          list[prop] = HtmlTrackElementList.prototype[prop];
6479        }
6480      }
6481    }
6482
6483    list.trackElements_ = [];
6484
6485    /**
6486     * @memberof HtmlTrackElementList
6487     * @member {number} length
6488     *         The current number of `Track`s in the this Trackist.
6489     * @instance
6490     */
6491    Object.defineProperty(list, 'length', {
6492      get: function get$$1() {
6493        return this.trackElements_.length;
6494      }
6495    });
6496
6497    for (var i = 0, length = trackElements.length; i < length; i++) {
6498      list.addTrackElement_(trackElements[i]);
6499    }
6500
6501    if (IS_IE8) {
6502      return list;
6503    }
6504  }
6505
6506  /**
6507   * Add an {@link HtmlTrackElement} to the `HtmlTrackElementList`
6508   *
6509   * @param {HtmlTrackElement} trackElement
6510   *        The track element to add to the list.
6511   *
6512   * @private
6513   */
6514
6515
6516  HtmlTrackElementList.prototype.addTrackElement_ = function addTrackElement_(trackElement) {
6517    var index = this.trackElements_.length;
6518
6519    if (!('' + index in this)) {
6520      Object.defineProperty(this, index, {
6521        get: function get$$1() {
6522          return this.trackElements_[index];
6523        }
6524      });
6525    }
6526
6527    // Do not add duplicate elements
6528    if (this.trackElements_.indexOf(trackElement) === -1) {
6529      this.trackElements_.push(trackElement);
6530    }
6531  };
6532
6533  /**
6534   * Get an {@link HtmlTrackElement} from the `HtmlTrackElementList` given an
6535   * {@link TextTrack}.
6536   *
6537   * @param {TextTrack} track
6538   *        The track associated with a track element.
6539   *
6540   * @return {HtmlTrackElement|undefined}
6541   *         The track element that was found or undefined.
6542   *
6543   * @private
6544   */
6545
6546
6547  HtmlTrackElementList.prototype.getTrackElementByTrack_ = function getTrackElementByTrack_(track) {
6548    var trackElement_ = void 0;
6549
6550    for (var i = 0, length = this.trackElements_.length; i < length; i++) {
6551      if (track === this.trackElements_[i].track) {
6552        trackElement_ = this.trackElements_[i];
6553
6554        break;
6555      }
6556    }
6557
6558    return trackElement_;
6559  };
6560
6561  /**
6562   * Remove a {@link HtmlTrackElement} from the `HtmlTrackElementList`
6563   *
6564   * @param {HtmlTrackElement} trackElement
6565   *        The track element to remove from the list.
6566   *
6567   * @private
6568   */
6569
6570
6571  HtmlTrackElementList.prototype.removeTrackElement_ = function removeTrackElement_(trackElement) {
6572    for (var i = 0, length = this.trackElements_.length; i < length; i++) {
6573      if (trackElement === this.trackElements_[i]) {
6574        this.trackElements_.splice(i, 1);
6575
6576        break;
6577      }
6578    }
6579  };
6580
6581  return HtmlTrackElementList;
6582}();
6583
6584/**
6585 * @file text-track-cue-list.js
6586 */
6587/**
6588 * @typedef {Object} TextTrackCueList~TextTrackCue
6589 *
6590 * @property {string} id
6591 *           The unique id for this text track cue
6592 *
6593 * @property {number} startTime
6594 *           The start time for this text track cue
6595 *
6596 * @property {number} endTime
6597 *           The end time for this text track cue
6598 *
6599 * @property {boolean} pauseOnExit
6600 *           Pause when the end time is reached if true.
6601 *
6602 * @see [Spec]{@link https://html.spec.whatwg.org/multipage/embedded-content.html#texttrackcue}
6603 */
6604
6605/**
6606 * A List of TextTrackCues.
6607 *
6608 * @see [Spec]{@link https://html.spec.whatwg.org/multipage/embedded-content.html#texttrackcuelist}
6609 */
6610
6611var TextTrackCueList = function () {
6612
6613  /**
6614   * Create an instance of this class..
6615   *
6616   * @param {Array} cues
6617   *        A list of cues to be initialized with
6618   */
6619  function TextTrackCueList(cues) {
6620    classCallCheck(this, TextTrackCueList);
6621
6622    var list = this; // eslint-disable-line
6623
6624    if (IS_IE8) {
6625      list = document_1.createElement('custom');
6626
6627      for (var prop in TextTrackCueList.prototype) {
6628        if (prop !== 'constructor') {
6629          list[prop] = TextTrackCueList.prototype[prop];
6630        }
6631      }
6632    }
6633
6634    TextTrackCueList.prototype.setCues_.call(list, cues);
6635
6636    /**
6637     * @memberof TextTrackCueList
6638     * @member {number} length
6639     *         The current number of `TextTrackCue`s in the TextTrackCueList.
6640     * @instance
6641     */
6642    Object.defineProperty(list, 'length', {
6643      get: function get$$1() {
6644        return this.length_;
6645      }
6646    });
6647
6648    if (IS_IE8) {
6649      return list;
6650    }
6651  }
6652
6653  /**
6654   * A setter for cues in this list. Creates getters
6655   * an an index for the cues.
6656   *
6657   * @param {Array} cues
6658   *        An array of cues to set
6659   *
6660   * @private
6661   */
6662
6663
6664  TextTrackCueList.prototype.setCues_ = function setCues_(cues) {
6665    var oldLength = this.length || 0;
6666    var i = 0;
6667    var l = cues.length;
6668
6669    this.cues_ = cues;
6670    this.length_ = cues.length;
6671
6672    var defineProp = function defineProp(index) {
6673      if (!('' + index in this)) {
6674        Object.defineProperty(this, '' + index, {
6675          get: function get$$1() {
6676            return this.cues_[index];
6677          }
6678        });
6679      }
6680    };
6681
6682    if (oldLength < l) {
6683      i = oldLength;
6684
6685      for (; i < l; i++) {
6686        defineProp.call(this, i);
6687      }
6688    }
6689  };
6690
6691  /**
6692   * Get a `TextTrackCue` that is currently in the `TextTrackCueList` by id.
6693   *
6694   * @param {string} id
6695   *        The id of the cue that should be searched for.
6696   *
6697   * @return {TextTrackCueList~TextTrackCue|null}
6698   *         A single cue or null if none was found.
6699   */
6700
6701
6702  TextTrackCueList.prototype.getCueById = function getCueById(id) {
6703    var result = null;
6704
6705    for (var i = 0, l = this.length; i < l; i++) {
6706      var cue = this[i];
6707
6708      if (cue.id === id) {
6709        result = cue;
6710        break;
6711      }
6712    }
6713
6714    return result;
6715  };
6716
6717  return TextTrackCueList;
6718}();
6719
6720/**
6721 * @file track-kinds.js
6722 */
6723
6724/**
6725 * All possible `VideoTrackKind`s
6726 *
6727 * @see https://html.spec.whatwg.org/multipage/embedded-content.html#dom-videotrack-kind
6728 * @typedef VideoTrack~Kind
6729 * @enum
6730 */
6731var VideoTrackKind = {
6732  alternative: 'alternative',
6733  captions: 'captions',
6734  main: 'main',
6735  sign: 'sign',
6736  subtitles: 'subtitles',
6737  commentary: 'commentary'
6738};
6739
6740/**
6741 * All possible `AudioTrackKind`s
6742 *
6743 * @see https://html.spec.whatwg.org/multipage/embedded-content.html#dom-audiotrack-kind
6744 * @typedef AudioTrack~Kind
6745 * @enum
6746 */
6747var AudioTrackKind = {
6748  'alternative': 'alternative',
6749  'descriptions': 'descriptions',
6750  'main': 'main',
6751  'main-desc': 'main-desc',
6752  'translation': 'translation',
6753  'commentary': 'commentary'
6754};
6755
6756/**
6757 * All possible `TextTrackKind`s
6758 *
6759 * @see https://html.spec.whatwg.org/multipage/embedded-content.html#dom-texttrack-kind
6760 * @typedef TextTrack~Kind
6761 * @enum
6762 */
6763var TextTrackKind = {
6764  subtitles: 'subtitles',
6765  captions: 'captions',
6766  descriptions: 'descriptions',
6767  chapters: 'chapters',
6768  metadata: 'metadata'
6769};
6770
6771/**
6772 * All possible `TextTrackMode`s
6773 *
6774 * @see https://html.spec.whatwg.org/multipage/embedded-content.html#texttrackmode
6775 * @typedef TextTrack~Mode
6776 * @enum
6777 */
6778var TextTrackMode = {
6779  disabled: 'disabled',
6780  hidden: 'hidden',
6781  showing: 'showing'
6782};
6783
6784/**
6785 * @file track.js
6786 */
6787/**
6788 * A Track class that contains all of the common functionality for {@link AudioTrack},
6789 * {@link VideoTrack}, and {@link TextTrack}.
6790 *
6791 * > Note: This class should not be used directly
6792 *
6793 * @see {@link https://html.spec.whatwg.org/multipage/embedded-content.html}
6794 * @extends EventTarget
6795 * @abstract
6796 */
6797
6798var Track = function (_EventTarget) {
6799  inherits(Track, _EventTarget);
6800
6801  /**
6802   * Create an instance of this class.
6803   *
6804   * @param {Object} [options={}]
6805   *        Object of option names and values
6806   *
6807   * @param {string} [options.kind='']
6808   *        A valid kind for the track type you are creating.
6809   *
6810   * @param {string} [options.id='vjs_track_' + Guid.newGUID()]
6811   *        A unique id for this AudioTrack.
6812   *
6813   * @param {string} [options.label='']
6814   *        The menu label for this track.
6815   *
6816   * @param {string} [options.language='']
6817   *        A valid two character language code.
6818   *
6819   * @abstract
6820   */
6821  function Track() {
6822    var _ret;
6823
6824    var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
6825    classCallCheck(this, Track);
6826
6827    var _this = possibleConstructorReturn(this, _EventTarget.call(this));
6828
6829    var track = _this; // eslint-disable-line
6830
6831    if (IS_IE8) {
6832      track = document_1.createElement('custom');
6833      for (var prop in Track.prototype) {
6834        if (prop !== 'constructor') {
6835          track[prop] = Track.prototype[prop];
6836        }
6837      }
6838    }
6839
6840    var trackProps = {
6841      id: options.id || 'vjs_track_' + newGUID(),
6842      kind: options.kind || '',
6843      label: options.label || '',
6844      language: options.language || ''
6845    };
6846
6847    /**
6848     * @memberof Track
6849     * @member {string} id
6850     *         The id of this track. Cannot be changed after creation.
6851     * @instance
6852     *
6853     * @readonly
6854     */
6855
6856    /**
6857     * @memberof Track
6858     * @member {string} kind
6859     *         The kind of track that this is. Cannot be changed after creation.
6860     * @instance
6861     *
6862     * @readonly
6863     */
6864
6865    /**
6866     * @memberof Track
6867     * @member {string} label
6868     *         The label of this track. Cannot be changed after creation.
6869     * @instance
6870     *
6871     * @readonly
6872     */
6873
6874    /**
6875     * @memberof Track
6876     * @member {string} language
6877     *         The two letter language code for this track. Cannot be changed after
6878     *         creation.
6879     * @instance
6880     *
6881     * @readonly
6882     */
6883
6884    var _loop = function _loop(key) {
6885      Object.defineProperty(track, key, {
6886        get: function get$$1() {
6887          return trackProps[key];
6888        },
6889        set: function set$$1() {}
6890      });
6891    };
6892
6893    for (var key in trackProps) {
6894      _loop(key);
6895    }
6896
6897    return _ret = track, possibleConstructorReturn(_this, _ret);
6898  }
6899
6900  return Track;
6901}(EventTarget);
6902
6903/**
6904 * @file url.js
6905 * @module url
6906 */
6907/**
6908 * @typedef {Object} url:URLObject
6909 *
6910 * @property {string} protocol
6911 *           The protocol of the url that was parsed.
6912 *
6913 * @property {string} hostname
6914 *           The hostname of the url that was parsed.
6915 *
6916 * @property {string} port
6917 *           The port of the url that was parsed.
6918 *
6919 * @property {string} pathname
6920 *           The pathname of the url that was parsed.
6921 *
6922 * @property {string} search
6923 *           The search query of the url that was parsed.
6924 *
6925 * @property {string} hash
6926 *           The hash of the url that was parsed.
6927 *
6928 * @property {string} host
6929 *           The host of the url that was parsed.
6930 */
6931
6932/**
6933 * Resolve and parse the elements of a URL.
6934 *
6935 * @param  {String} url
6936 *         The url to parse
6937 *
6938 * @return {url:URLObject}
6939 *         An object of url details
6940 */
6941var parseUrl = function parseUrl(url) {
6942  var props = ['protocol', 'hostname', 'port', 'pathname', 'search', 'hash', 'host'];
6943
6944  // add the url to an anchor and let the browser parse the URL
6945  var a = document_1.createElement('a');
6946
6947  a.href = url;
6948
6949  // IE8 (and 9?) Fix
vendor: 4,164 bytes, lines 6950-7104
6950  // ie8 doesn't parse the URL correctly until the anchor is actually
6951  // added to the body, and an innerHTML is needed to trigger the parsing
6952  var addToBody = a.host === '' && a.protocol !== 'file:';
6953  var div = void 0;
6954
6955  if (addToBody) {
6956    div = document_1.createElement('div');
6957    div.innerHTML = '<a href="' + url + '"></a>';
6958    a = div.firstChild;
6959    // prevent the div from affecting layout
6960    div.setAttribute('style', 'display:none; position:absolute;');
6961    document_1.body.appendChild(div);
6962  }
6963
6964  // Copy the specific URL properties to a new object
6965  // This is also needed for IE8 because the anchor loses its
6966  // properties when it's removed from the dom
6967  var details = {};
6968
6969  for (var i = 0; i < props.length; i++) {
6970    details[props[i]] = a[props[i]];
6971  }
6972
6973  // IE9 adds the port to the host property unlike everyone else. If
6974  // a port identifier is added for standard ports, strip it.
6975  if (details.protocol === 'http:') {
6976    details.host = details.host.replace(/:80$/, '');
6977  }
6978
6979  if (details.protocol === 'https:') {
6980    details.host = details.host.replace(/:443$/, '');
6981  }
6982
6983  if (!details.protocol) {
6984    details.protocol = window_1.location.protocol;
6985  }
6986
6987  if (addToBody) {
6988    document_1.body.removeChild(div);
6989  }
6990
6991  return details;
6992};
6993
6994/**
6995 * Get absolute version of relative URL. Used to tell flash correct URL.
6996 *
6997 *
6998 * @param  {string} url
6999 *         URL to make absolute
7000 *
7001 * @return {string}
7002 *         Absolute URL
7003 *
7004 * @see http://stackoverflow.com/questions/470832/getting-an-absolute-url-from-a-relative-one-ie6-issue
7005 */
7006var getAbsoluteURL = function getAbsoluteURL(url) {
7007  // Check if absolute URL
7008  if (!url.match(/^https?:\/\//)) {
7009    // Convert to absolute URL. Flash hosted off-site needs an absolute URL.
7010    var div = document_1.createElement('div');
7011
7012    div.innerHTML = '<a href="' + url + '">x</a>';
7013    url = div.firstChild.href;
7014  }
7015
7016  return url;
7017};
7018
7019/**
7020 * Returns the extension of the passed file name. It will return an empty string
7021 * if passed an invalid path.
7022 *
7023 * @param {string} path
7024 *        The fileName path like '/path/to/file.mp4'
7025 *
7026 * @returns {string}
7027 *          The extension in lower case or an empty string if no
7028 *          extension could be found.
7029 */
7030var getFileExtension = function getFileExtension(path) {
7031  if (typeof path === 'string') {
7032    var splitPathRe = /^(\/?)([\s\S]*?)((?:\.{1,2}|[^\/]+?)(\.([^\.\/\?]+)))(?:[\/]*|[\?].*)$/i;
7033    var pathParts = splitPathRe.exec(path);
7034
7035    if (pathParts) {
7036      return pathParts.pop().toLowerCase();
7037    }
7038  }
7039
7040  return '';
7041};
7042
7043/**
7044 * Returns whether the url passed is a cross domain request or not.
7045 *
7046 * @param {string} url
7047 *        The url to check.
7048 *
7049 * @return {boolean}
7050 *         Whether it is a cross domain request or not.
7051 */
7052var isCrossOrigin = function isCrossOrigin(url) {
7053  var winLoc = window_1.location;
7054  var urlInfo = parseUrl(url);
7055
7056  // IE8 protocol relative urls will return ':' for protocol
7057  var srcProtocol = urlInfo.protocol === ':' ? winLoc.protocol : urlInfo.protocol;
7058
7059  // Check if url is for another domain/origin
7060  // IE8 doesn't know location.origin, so we won't rely on it here
7061  var crossOrigin = srcProtocol + urlInfo.host !== winLoc.protocol + winLoc.host;
7062
7063  return crossOrigin;
7064};
7065
7066var Url = (Object.freeze || Object)({
7067	parseUrl: parseUrl,
7068	getAbsoluteURL: getAbsoluteURL,
7069	getFileExtension: getFileExtension,
7070	isCrossOrigin: isCrossOrigin
7071});
7072
7073var isFunction_1 = isFunction;
7074
7075var toString$1 = Object.prototype.toString;
7076
7077function isFunction (fn) {
7078  var string = toString$1.call(fn);
7079  return string === '[object Function]' ||
7080    (typeof fn === 'function' && string !== '[object RegExp]') ||
7081    (typeof window !== 'undefined' &&
7082     // IE8 and below
7083     (fn === window.setTimeout ||
7084      fn === window.alert ||
7085      fn === window.confirm ||
7086      fn === window.prompt))
7087}
7088
7089var trim_1 = createCommonjsModule(function (module, exports) {
7090exports = module.exports = trim;
7091
7092function trim(str){
7093  return str.replace(/^\s*|\s*$/g, '');
7094}
7095
7096exports.left = function(str){
7097  return str.replace(/^\s*/, '');
7098};
7099
7100exports.right = function(str){
7101  return str.replace(/\s*$/, '');
7102};
7103});
7104
7105var forEach_1 = forEach;
7106
7107var toString$2 = Object.prototype.toString;
7108var hasOwnProperty = Object.prototype.hasOwnProperty;
7109
7110function forEach(list, iterator, context) {
7111    if (!isFunction_1(iterator)) {
7112        throw new TypeError('iterator must be a function')
7113    }
7114
7115    if (arguments.length < 3) {
7116        context = this;
7117    }
7118    
7119    if (toString$2.call(list) === '[object Array]')
7120        forEachArray$1(list, iterator, context);
7121    else if (typeof list === 'string')
7122        forEachString(list, iterator, context);
7123    else
7124        forEachObject(list, iterator, context);
7125}
7126
7127function forEachArray$1(array, iterator, context) {
7128    for (var i = 0, len = array.length; i < len; i++) {
7129        if (hasOwnProperty.call(array, i)) {
7130            iterator.call(context, array[i], i, array);
7131        }
7132    }
7133}
7134
7135function forEachString(string, iterator, context) {
7136    for (var i = 0, len = string.length; i < len; i++) {
7137        // no such thing as a sparse string.
7138        iterator.call(context, string.charAt(i), i, string);
7139    }
7140}
7141
7142function forEachObject(object, iterator, context) {
7143    for (var k in object) {
7144        if (hasOwnProperty.call(object, k)) {
7145            iterator.call(context, object[k], k, object);
7146        }
7147    }
7148}
7149
7150var isArray = function(arg) {
7151      return Object.prototype.toString.call(arg) === '[object Array]';
7152    };
7153
7154var parseHeaders = function (headers) {
7155  if (!headers)
7156    return {}
7157
7158  var result = {};
7159
7160  forEach_1(
7161      trim_1(headers).split('\n')
7162    , function (row) {
7163        var index = row.indexOf(':')
7164          , key = trim_1(row.slice(0, index)).toLowerCase()
7165          , value = trim_1(row.slice(index + 1));
7166
7167        if (typeof(result[key]) === 'undefined') {
7168          result[key] = value;
7169        } else if (isArray(result[key])) {
7170          result[key].push(value);
7171        } else {
7172          result[key] = [ result[key], value ];
7173        }
7174      }
7175  );
7176
7177  return result
7178};
7179
7180var immutable = extend;
7181
7182var hasOwnProperty$1 = Object.prototype.hasOwnProperty;
7183
7184function extend() {
7185    var target = {};
7186
7187    for (var i = 0; i < arguments.length; i++) {
7188        var source = arguments[i];
7189
7190        for (var key in source) {
7191            if (hasOwnProperty$1.call(source, key)) {
7192                target[key] = source[key];
7193            }
7194        }
7195    }
7196
7197    return target
7198}
7199
7200var xhr = createXHR;
7201createXHR.XMLHttpRequest = window_1.XMLHttpRequest || noop;
7202createXHR.XDomainRequest = "withCredentials" in (new createXHR.XMLHttpRequest()) ? createXHR.XMLHttpRequest : window_1.XDomainRequest;
7203
7204forEachArray(["get", "put", "post", "patch", "head", "delete"], function(method) {
7205    createXHR[method === "delete" ? "del" : method] = function(uri, options, callback) {
7206        options = initParams(uri, options, callback);
7207        options.method = method.toUpperCase();
7208        return _createXHR(options)
7209    };
7210});
7211
7212function forEachArray(array, iterator) {
7213    for (var i = 0; i < array.length; i++) {
7214        iterator(array[i]);
7215    }
7216}
7217
7218function isEmpty(obj){
7219    for(var i in obj){
7220        if(obj.hasOwnProperty(i)) return false
7221    }
7222    return true
7223}
7224
7225function initParams(uri, options, callback) {
7226    var params = uri;
7227
7228    if (isFunction_1(options)) {
7229        callback = options;
7230        if (typeof uri === "string") {
7231            params = {uri:uri};
7232        }
7233    } else {
7234        params = immutable(options, {uri: uri});
7235    }
7236
7237    params.callback = callback;
7238    return params
7239}
7240
7241function createXHR(uri, options, callback) {
7242    options = initParams(uri, options, callback);
7243    return _createXHR(options)
7244}
7245
7246function _createXHR(options) {
7247    if(typeof options.callback === "undefined"){
7248        throw new Error("callback argument missing")
7249    }
7250
7251    var called = false;
7252    var callback = function cbOnce(err, response, body){
7253        if(!called){
7254            called = true;
7255            options.callback(err, response, body);
7256        }
7257    };
7258
7259    function readystatechange() {
7260        if (xhr.readyState === 4) {
7261            setTimeout(loadFunc, 0);
7262        }
7263    }
7264
7265    function getBody() {
7266        // Chrome with requestType=blob throws errors arround when even testing access to responseText
7267        var body = undefined;
7268
7269        if (xhr.response) {
7270            body = xhr.response;
7271        } else {
7272            body = xhr.responseText || getXml(xhr);
7273        }
7274
7275        if (isJson) {
7276            try {
7277                body = JSON.parse(body);
7278            } catch (e) {}
7279        }
7280
7281        return body
7282    }
7283
7284    function errorFunc(evt) {
7285        clearTimeout(timeoutTimer);
7286        if(!(evt instanceof Error)){
7287            evt = new Error("" + (evt || "Unknown XMLHttpRequest Error") );
7288        }
7289        evt.statusCode = 0;
7290        return callback(evt, failureResponse)
7291    }
7292
7293    // will load the data & process the response in a special response object
7294    function loadFunc() {
7295        if (aborted) return
7296        var status;
7297        clearTimeout(timeoutTimer);
7298        if(options.useXDR && xhr.status===undefined) {
7299            //IE8 CORS GET successful response doesn't have a status field, but body is fine
7300            status = 200;
7301        } else {
7302            status = (xhr.status === 1223 ? 204 : xhr.status);
7303        }
7304        var response = failureResponse;
7305        var err = null;
7306
7307        if (status !== 0){
7308            response = {
7309                body: getBody(),
7310                statusCode: status,
7311                method: method,
7312                headers: {},
vendor: 4,867 bytes, lines 7313-7473
7313                url: uri,
7314                rawRequest: xhr
7315            };
7316            if(xhr.getAllResponseHeaders){ //remember xhr can in fact be XDR for CORS in IE
7317                response.headers = parseHeaders(xhr.getAllResponseHeaders());
7318            }
7319        } else {
7320            err = new Error("Internal XMLHttpRequest Error");
7321        }
7322        return callback(err, response, response.body)
7323    }
7324
7325    var xhr = options.xhr || null;
7326
7327    if (!xhr) {
7328        if (options.cors || options.useXDR) {
7329            xhr = new createXHR.XDomainRequest();
7330        }else{
7331            xhr = new createXHR.XMLHttpRequest();
7332        }
7333    }
7334
7335    var key;
7336    var aborted;
7337    var uri = xhr.url = options.uri || options.url;
7338    var method = xhr.method = options.method || "GET";
7339    var body = options.body || options.data;
7340    var headers = xhr.headers = options.headers || {};
7341    var sync = !!options.sync;
7342    var isJson = false;
7343    var timeoutTimer;
7344    var failureResponse = {
7345        body: undefined,
7346        headers: {},
7347        statusCode: 0,
7348        method: method,
7349        url: uri,
7350        rawRequest: xhr
7351    };
7352
7353    if ("json" in options && options.json !== false) {
7354        isJson = true;
7355        headers["accept"] || headers["Accept"] || (headers["Accept"] = "application/json"); //Don't override existing accept header declared by user
7356        if (method !== "GET" && method !== "HEAD") {
7357            headers["content-type"] || headers["Content-Type"] || (headers["Content-Type"] = "application/json"); //Don't override existing accept header declared by user
7358            body = JSON.stringify(options.json === true ? body : options.json);
7359        }
7360    }
7361
7362    xhr.onreadystatechange = readystatechange;
7363    xhr.onload = loadFunc;
7364    xhr.onerror = errorFunc;
7365    // IE9 must have onprogress be set to a unique function.
7366    xhr.onprogress = function () {
7367        // IE must die
7368    };
7369    xhr.onabort = function(){
7370        aborted = true;
7371    };
7372    xhr.ontimeout = errorFunc;
7373    xhr.open(method, uri, !sync, options.username, options.password);
7374    //has to be after open
7375    if(!sync) {
7376        xhr.withCredentials = !!options.withCredentials;
7377    }
7378    // Cannot set timeout with sync request
7379    // not setting timeout on the xhr object, because of old webkits etc. not handling that correctly
7380    // both npm's request and jquery 1.x use this kind of timeout, so this is being consistent
7381    if (!sync && options.timeout > 0 ) {
7382        timeoutTimer = setTimeout(function(){
7383            if (aborted) return
7384            aborted = true;//IE9 may still call readystatechange
7385            xhr.abort("timeout");
7386            var e = new Error("XMLHttpRequest timeout");
7387            e.code = "ETIMEDOUT";
7388            errorFunc(e);
7389        }, options.timeout );
7390    }
7391
7392    if (xhr.setRequestHeader) {
7393        for(key in headers){
7394            if(headers.hasOwnProperty(key)){
7395                xhr.setRequestHeader(key, headers[key]);
7396            }
7397        }
7398    } else if (options.headers && !isEmpty(options.headers)) {
7399        throw new Error("Headers cannot be set on an XDomainRequest object")
7400    }
7401
7402    if ("responseType" in options) {
7403        xhr.responseType = options.responseType;
7404    }
7405
7406    if ("beforeSend" in options &&
7407        typeof options.beforeSend === "function"
7408    ) {
7409        options.beforeSend(xhr);
7410    }
7411
7412    // Microsoft Edge browser sends "undefined" when send is called with undefined value.
7413    // XMLHttpRequest spec says to pass null as body to indicate no body
7414    // See https://github.com/naugtur/xhr/issues/100.
7415    xhr.send(body || null);
7416
7417    return xhr
7418
7419
7420}
7421
7422function getXml(xhr) {
7423    if (xhr.responseType === "document") {
7424        return xhr.responseXML
7425    }
7426    var firefoxBugTakenEffect = xhr.responseXML && xhr.responseXML.documentElement.nodeName === "parsererror";
7427    if (xhr.responseType === "" && !firefoxBugTakenEffect) {
7428        return xhr.responseXML
7429    }
7430
7431    return null
7432}
7433
7434function noop() {}
7435
7436/**
7437 * @file text-track.js
7438 */
7439/**
7440 * Takes a webvtt file contents and parses it into cues
7441 *
7442 * @param {string} srcContent
7443 *        webVTT file contents
7444 *
7445 * @param {TextTrack} track
7446 *        TextTrack to add cues to. Cues come from the srcContent.
7447 *
7448 * @private
7449 */
7450var parseCues = function parseCues(srcContent, track) {
7451  var parser = new window_1.WebVTT.Parser(window_1, window_1.vttjs, window_1.WebVTT.StringDecoder());
7452  var errors = [];
7453
7454  parser.oncue = function (cue) {
7455    track.addCue(cue);
7456  };
7457
7458  parser.onparsingerror = function (error) {
7459    errors.push(error);
7460  };
7461
7462  parser.onflush = function () {
7463    track.trigger({
7464      type: 'loadeddata',
7465      target: track
7466    });
7467  };
7468
7469  parser.parse(srcContent);
7470  if (errors.length > 0) {
7471    if (window_1.console && window_1.console.groupCollapsed) {
7472      window_1.console.groupCollapsed('Text Track parsing errors for ' + track.src);
7473    }
7474    errors.forEach(function (error) {
7475      return log$1.error(error);
7476    });
7477    if (window_1.console && window_1.console.groupEnd) {
7478      window_1.console.groupEnd();
7479    }
7480  }
7481
7482  parser.flush();
7483};
7484
7485/**
7486 * Load a `TextTrack` from a specifed url.
7487 *
7488 * @param {string} src
7489 *        Url to load track from.
7490 *
7491 * @param {TextTrack} track
7492 *        Track to add cues to. Comes from the content at the end of `url`.
7493 *
7494 * @private
7495 */
7496var loadTrack = function loadTrack(src, track) {
7497  var opts = {
7498    uri: src
7499  };
7500  var crossOrigin = isCrossOrigin(src);
7501
7502  if (crossOrigin) {
7503    opts.cors = crossOrigin;
7504  }
7505
7506  xhr(opts, bind(this, function (err, response, responseBody) {
7507    if (err) {
7508      return log$1.error(err, response);
7509    }
7510
7511    track.loaded_ = true;
7512
7513    // Make sure that vttjs has loaded, otherwise, wait till it finished loading
7514    // NOTE: this is only used for the alt/video.novtt.js build
7515    if (typeof window_1.WebVTT !== 'function') {
7516      if (track.tech_) {
7517        var loadHandler = function loadHandler() {
7518          return parseCues(responseBody, track);
7519        };
7520
7521        track.tech_.on('vttjsloaded', loadHandler);
7522        track.tech_.on('vttjserror', function () {
7523          log$1.error('vttjs failed to load, stopping trying to process ' + track.src);
7524          track.tech_.off('vttjsloaded', loadHandler);
7525        });
7526      }
7527    } else {
7528      parseCues(responseBody, track);
7529    }
7530  }));
7531};
7532
7533/**
7534 * A representation of a single `TextTrack`.
7535 *
7536 * @see [Spec]{@link https://html.spec.whatwg.org/multipage/embedded-content.html#texttrack}
7537 * @extends Track
7538 */
7539
7540var TextTrack = function (_Track) {
7541  inherits(TextTrack, _Track);
7542
7543  /**
7544   * Create an instance of this class.
7545   *
7546   * @param {Object} options={}
7547   *        Object of option names and values
7548   *
7549   * @param {Tech} options.tech
7550   *        A reference to the tech that owns this TextTrack.
7551   *
7552   * @param {TextTrack~Kind} [options.kind='subtitles']
7553   *        A valid text track kind.
7554   *
7555   * @param {TextTrack~Mode} [options.mode='disabled']
7556   *        A valid text track mode.
7557   *
7558   * @param {string} [options.id='vjs_track_' + Guid.newGUID()]
7559   *        A unique id for this TextTrack.
7560   *
7561   * @param {string} [options.label='']
7562   *        The menu label for this track.
7563   *
7564   * @param {string} [options.language='']
7565   *        A valid two character language code.
7566   *
7567   * @param {string} [options.srclang='']
7568   *        A valid two character language code. An alternative, but deprioritized
7569   *        vesion of `options.language`
7570   *
7571   * @param {string} [options.src]
7572   *        A url to TextTrack cues.
7573   *
7574   * @param {boolean} [options.default]
7575   *        If this track should default to on or off.
7576   */
7577  function TextTrack() {
7578    var _this, _ret;
7579
7580    var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
7581    classCallCheck(this, TextTrack);
7582
7583    if (!options.tech) {
7584      throw new Error('A tech was not provided.');
7585    }
7586
7587    var settings = mergeOptions(options, {
7588      kind: TextTrackKind[options.kind] || 'subtitles',
7589      language: options.language || options.srclang || ''
7590    });
7591    var mode = TextTrackMode[settings.mode] || 'disabled';
7592    var default_ = settings['default'];
7593
7594    if (settings.kind === 'metadata' || settings.kind === 'chapters') {
7595      mode = 'hidden';
7596    }
7597    // on IE8 this will be a document element
7598    // for every other browser this will be a normal object
7599    var tt = (_this = possibleConstructorReturn(this, _Track.call(this, settings)), _this);
7600
7601    tt.tech_ = settings.tech;
7602
7603    if (IS_IE8) {
7604      for (var prop in TextTrack.prototype) {
7605        if (prop !== 'constructor') {
7606          tt[prop] = TextTrack.prototype[prop];
7607        }
7608      }
7609    }
7610
7611    tt.cues_ = [];
7612    tt.activeCues_ = [];
7613
7614    var cues = new TextTrackCueList(tt.cues_);
7615    var activeCues = new TextTrackCueList(tt.activeCues_);
7616    var changed = false;
7617    var timeupdateHandler = bind(tt, function () {
7618
7619      // Accessing this.activeCues for the side-effects of updating itself
7620      // due to it's nature as a getter function. Do not remove or cues will
7621      // stop updating!
vendor: 6,824 bytes, lines 7622-7885
7622      // Use the setter to prevent deletion from uglify (pure_getters rule)
7623      this.activeCues = this.activeCues;
7624      if (changed) {
7625        this.trigger('cuechange');
7626        changed = false;
7627      }
7628    });
7629
7630    if (mode !== 'disabled') {
7631      tt.tech_.ready(function () {
7632        tt.tech_.on('timeupdate', timeupdateHandler);
7633      }, true);
7634    }
7635
7636    /**
7637     * @memberof TextTrack
7638     * @member {boolean} default
7639     *         If this track was set to be on or off by default. Cannot be changed after
7640     *         creation.
7641     * @instance
7642     *
7643     * @readonly
7644     */
7645    Object.defineProperty(tt, 'default', {
7646      get: function get$$1() {
7647        return default_;
7648      },
7649      set: function set$$1() {}
7650    });
7651
7652    /**
7653     * @memberof TextTrack
7654     * @member {string} mode
7655     *         Set the mode of this TextTrack to a valid {@link TextTrack~Mode}. Will
7656     *         not be set if setting to an invalid mode.
7657     * @instance
7658     *
7659     * @fires TextTrack#modechange
7660     */
7661    Object.defineProperty(tt, 'mode', {
7662      get: function get$$1() {
7663        return mode;
7664      },
7665      set: function set$$1(newMode) {
7666        var _this2 = this;
7667
7668        if (!TextTrackMode[newMode]) {
7669          return;
7670        }
7671        mode = newMode;
7672        if (mode === 'showing') {
7673
7674          this.tech_.ready(function () {
7675            _this2.tech_.on('timeupdate', timeupdateHandler);
7676          }, true);
7677        }
7678        /**
7679         * An event that fires when mode changes on this track. This allows
7680         * the TextTrackList that holds this track to act accordingly.
7681         *
7682         * > Note: This is not part of the spec!
7683         *
7684         * @event TextTrack#modechange
7685         * @type {EventTarget~Event}
7686         */
7687        this.trigger('modechange');
7688      }
7689    });
7690
7691    /**
7692     * @memberof TextTrack
7693     * @member {TextTrackCueList} cues
7694     *         The text track cue list for this TextTrack.
7695     * @instance
7696     */
7697    Object.defineProperty(tt, 'cues', {
7698      get: function get$$1() {
7699        if (!this.loaded_) {
7700          return null;
7701        }
7702
7703        return cues;
7704      },
7705      set: function set$$1() {}
7706    });
7707
7708    /**
7709     * @memberof TextTrack
7710     * @member {TextTrackCueList} activeCues
7711     *         The list text track cues that are currently active for this TextTrack.
7712     * @instance
7713     */
7714    Object.defineProperty(tt, 'activeCues', {
7715      get: function get$$1() {
7716        if (!this.loaded_) {
7717          return null;
7718        }
7719
7720        // nothing to do
7721        if (this.cues.length === 0) {
7722          return activeCues;
7723        }
7724
7725        var ct = this.tech_.currentTime();
7726        var active = [];
7727
7728        for (var i = 0, l = this.cues.length; i < l; i++) {
7729          var cue = this.cues[i];
7730
7731          if (cue.startTime <= ct && cue.endTime >= ct) {
7732            active.push(cue);
7733          } else if (cue.startTime === cue.endTime && cue.startTime <= ct && cue.startTime + 0.5 >= ct) {
7734            active.push(cue);
7735          }
7736        }
7737
7738        changed = false;
7739
7740        if (active.length !== this.activeCues_.length) {
7741          changed = true;
7742        } else {
7743          for (var _i = 0; _i < active.length; _i++) {
7744            if (this.activeCues_.indexOf(active[_i]) === -1) {
7745              changed = true;
7746            }
7747          }
7748        }
7749
7750        this.activeCues_ = active;
7751        activeCues.setCues_(this.activeCues_);
7752
7753        return activeCues;
7754      },
7755
7756
7757      // /!\ Keep this setter empty (see the timeupdate handler above)
7758      set: function set$$1() {}
7759    });
7760
7761    if (settings.src) {
7762      tt.src = settings.src;
7763      loadTrack(settings.src, tt);
7764    } else {
7765      tt.loaded_ = true;
7766    }
7767
7768    return _ret = tt, possibleConstructorReturn(_this, _ret);
7769  }
7770
7771  /**
7772   * Add a cue to the internal list of cues.
7773   *
7774   * @param {TextTrack~Cue} cue
7775   *        The cue to add to our internal list
7776   */
7777
7778
7779  TextTrack.prototype.addCue = function addCue(originalCue) {
7780    var cue = originalCue;
7781
7782    if (window_1.vttjs && !(originalCue instanceof window_1.vttjs.VTTCue)) {
7783      cue = new window_1.vttjs.VTTCue(originalCue.startTime, originalCue.endTime, originalCue.text);
7784
7785      for (var prop in originalCue) {
7786        if (!(prop in cue)) {
7787          cue[prop] = originalCue[prop];
7788        }
7789      }
7790
7791      // make sure that `id` is copied over
7792      cue.id = originalCue.id;
7793      cue.originalCue_ = originalCue;
7794    }
7795
7796    var tracks = this.tech_.textTracks();
7797
7798    for (var i = 0; i < tracks.length; i++) {
7799      if (tracks[i] !== this) {
7800        tracks[i].removeCue(cue);
7801      }
7802    }
7803
7804    this.cues_.push(cue);
7805    this.cues.setCues_(this.cues_);
7806  };
7807
7808  /**
7809   * Remove a cue from our internal list
7810   *
7811   * @param {TextTrack~Cue} removeCue
7812   *        The cue to remove from our internal list
7813   */
7814
7815
7816  TextTrack.prototype.removeCue = function removeCue(_removeCue) {
7817    var i = this.cues_.length;
7818
7819    while (i--) {
7820      var cue = this.cues_[i];
7821
7822      if (cue === _removeCue || cue.originalCue_ && cue.originalCue_ === _removeCue) {
7823        this.cues_.splice(i, 1);
7824        this.cues.setCues_(this.cues_);
7825        break;
7826      }
7827    }
7828  };
7829
7830  return TextTrack;
7831}(Track);
7832
7833/**
7834 * cuechange - One or more cues in the track have become active or stopped being active.
7835 */
7836
7837
7838TextTrack.prototype.allowedEvents_ = {
7839  cuechange: 'cuechange'
7840};
7841
7842/**
7843 * A representation of a single `AudioTrack`. If it is part of an {@link AudioTrackList}
7844 * only one `AudioTrack` in the list will be enabled at a time.
7845 *
7846 * @see [Spec]{@link https://html.spec.whatwg.org/multipage/embedded-content.html#audiotrack}
7847 * @extends Track
7848 */
7849
7850var AudioTrack = function (_Track) {
7851  inherits(AudioTrack, _Track);
7852
7853  /**
7854   * Create an instance of this class.
7855   *
7856   * @param {Object} [options={}]
7857   *        Object of option names and values
7858   *
7859   * @param {AudioTrack~Kind} [options.kind='']
7860   *        A valid audio track kind
7861   *
7862   * @param {string} [options.id='vjs_track_' + Guid.newGUID()]
7863   *        A unique id for this AudioTrack.
7864   *
7865   * @param {string} [options.label='']
7866   *        The menu label for this track.
7867   *
7868   * @param {string} [options.language='']
7869   *        A valid two character language code.
7870   *
7871   * @param {boolean} [options.enabled]
7872   *        If this track is the one that is currently playing. If this track is part of
7873   *        an {@link AudioTrackList}, only one {@link AudioTrack} will be enabled.
7874   */
7875  function AudioTrack() {
7876    var _this, _ret;
7877
7878    var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
7879    classCallCheck(this, AudioTrack);
7880
7881    var settings = mergeOptions(options, {
7882      kind: AudioTrackKind[options.kind] || ''
7883    });
7884    // on IE8 this will be a document element
7885    // for every other browser this will be a normal object
7886    var track = (_this = possibleConstructorReturn(this, _Track.call(this, settings)), _this);
7887    var enabled = false;
7888
7889    if (IS_IE8) {
7890      for (var prop in AudioTrack.prototype) {
7891        if (prop !== 'constructor') {
7892          track[prop] = AudioTrack.prototype[prop];
7893        }
7894      }
7895    }
7896    /**
7897     * @memberof AudioTrack
7898     * @member {boolean} enabled
7899     *         If this `AudioTrack` is enabled or not. When setting this will
7900     *         fire {@link AudioTrack#enabledchange} if the state of enabled is changed.
7901     * @instance
7902     *
7903     * @fires VideoTrack#selectedchange
7904     */
7905    Object.defineProperty(track, 'enabled', {
7906      get: function get$$1() {
7907        return enabled;
7908      },
7909      set: function set$$1(newEnabled) {
7910        // an invalid or unchanged value
7911        if (typeof newEnabled !== 'boolean' || newEnabled === enabled) {
7912          return;
7913        }
7914        enabled = newEnabled;
7915
7916        /**
7917         * An event that fires when enabled changes on this track. This allows
7918         * the AudioTrackList that holds this track to act accordingly.
7919         *
7920         * > Note: This is not part of the spec! Native tracks will do
7921         *         this internally without an event.
7922         *
7923         * @event AudioTrack#enabledchange
7924         * @type {EventTarget~Event}
7925         */
7926        this.trigger('enabledchange');
7927      }
7928    });
7929
7930    // if the user sets this track to selected then
7931    // set selected to that true value otherwise
7932    // we keep it false
7933    if (settings.enabled) {
7934      track.enabled = settings.enabled;
7935    }
7936    track.loaded_ = true;
7937
7938    return _ret = track, possibleConstructorReturn(_this, _ret);
7939  }
7940
7941  return AudioTrack;
7942}(Track);
7943
7944/**
7945 * A representation of a single `VideoTrack`.
7946 *
7947 * @see [Spec]{@link https://html.spec.whatwg.org/multipage/embedded-content.html#videotrack}
7948 * @extends Track
7949 */
7950
7951var VideoTrack = function (_Track) {
7952  inherits(VideoTrack, _Track);
7953
7954  /**
7955   * Create an instance of this class.
7956   *
7957   * @param {Object} [options={}]
7958   *        Object of option names and values
7959   *
7960   * @param {string} [options.kind='']
7961   *        A valid {@link VideoTrack~Kind}
7962   *
7963   * @param {string} [options.id='vjs_track_' + Guid.newGUID()]
7964   *        A unique id for this AudioTrack.
7965   *
7966   * @param {string} [options.label='']
7967   *        The menu label for this track.
7968   *
7969   * @param {string} [options.language='']
7970   *        A valid two character language code.
7971   *
7972   * @param {boolean} [options.selected]
7973   *        If this track is the one that is currently playing.
7974   */
7975  function VideoTrack() {
7976    var _this, _ret;
7977
7978    var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
7979    classCallCheck(this, VideoTrack);
7980
7981    var settings = mergeOptions(options, {
7982      kind: VideoTrackKind[options.kind] || ''
7983    });
7984
7985    // on IE8 this will be a document element
7986    // for every other browser this will be a normal object
7987    var track = (_this = possibleConstructorReturn(this, _Track.call(this, settings)), _this);
7988    var selected = false;
7989
7990    if (IS_IE8) {
7991      for (var prop in VideoTrack.prototype) {
7992        if (prop !== 'constructor') {
7993          track[prop] = VideoTrack.prototype[prop];
7994        }
7995      }
7996    }
7997
7998    /**
7999     * @memberof VideoTrack
8000     * @member {boolean} selected
8001     *         If this `VideoTrack` is selected or not. When setting this will
8002     *         fire {@link VideoTrack#selectedchange} if the state of selected changed.
8003     * @instance
8004     *
8005     * @fires VideoTrack#selectedchange
8006     */
8007    Object.defineProperty(track, 'selected', {
8008      get: function get$$1() {
8009        return selected;
8010      },
8011      set: function set$$1(newSelected) {
8012        // an invalid or unchanged value
8013        if (typeof newSelected !== 'boolean' || newSelected === selected) {
8014          return;
8015        }
8016        selected = newSelected;
8017
8018        /**
8019         * An event that fires when selected changes on this track. This allows
8020         * the VideoTrackList that holds this track to act accordingly.
8021         *
8022         * > Note: This is not part of the spec! Native tracks will do
8023         *         this internally without an event.
8024         *
8025         * @event VideoTrack#selectedchange
8026         * @type {EventTarget~Event}
8027         */
8028        this.trigger('selectedchange');
8029      }
8030    });
8031
8032    // if the user sets this track to selected then
8033    // set selected to that true value otherwise
8034    // we keep it false
8035    if (settings.selected) {
8036      track.selected = settings.selected;
8037    }
8038
8039    return _ret = track, possibleConstructorReturn(_this, _ret);
8040  }
8041
8042  return VideoTrack;
8043}(Track);
8044
8045/**
8046 * @file html-track-element.js
8047 */
8048
8049/**
8050 * @memberof HTMLTrackElement
8051 * @typedef {HTMLTrackElement~ReadyState}
8052 * @enum {number}
8053 */
8054var NONE = 0;
8055var LOADING = 1;
8056var LOADED = 2;
8057var ERROR = 3;
8058
8059/**
8060 * A single track represented in the DOM.
8061 *
8062 * @see [Spec]{@link https://html.spec.whatwg.org/multipage/embedded-content.html#htmltrackelement}
8063 * @extends EventTarget
8064 */
8065
8066var HTMLTrackElement = function (_EventTarget) {
8067  inherits(HTMLTrackElement, _EventTarget);
8068
8069  /**
8070   * Create an instance of this class.
8071   *
8072   * @param {Object} options={}
8073   *        Object of option names and values
8074   *
8075   * @param {Tech} options.tech
8076   *        A reference to the tech that owns this HTMLTrackElement.
8077   *
8078   * @param {TextTrack~Kind} [options.kind='subtitles']
8079   *        A valid text track kind.
8080   *
8081   * @param {TextTrack~Mode} [options.mode='disabled']
8082   *        A valid text track mode.
8083   *
8084   * @param {string} [options.id='vjs_track_' + Guid.newGUID()]
8085   *        A unique id for this TextTrack.
8086   *
8087   * @param {string} [options.label='']
8088   *        The menu label for this track.
8089   *
8090   * @param {string} [options.language='']
8091   *        A valid two character language code.
8092   *
8093   * @param {string} [options.srclang='']
8094   *        A valid two character language code. An alternative, but deprioritized
8095   *        vesion of `options.language`
8096   *
8097   * @param {string} [options.src]
8098   *        A url to TextTrack cues.
8099   *
8100   * @param {boolean} [options.default]
8101   *        If this track should default to on or off.
8102   */
8103  function HTMLTrackElement() {
8104    var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
8105    classCallCheck(this, HTMLTrackElement);
8106
8107    var _this = possibleConstructorReturn(this, _EventTarget.call(this));
8108
8109    var readyState = void 0;
8110    var trackElement = _this; // eslint-disable-line
8111
8112    if (IS_IE8) {
8113      trackElement = document_1.createElement('custom');
8114
8115      for (var prop in HTMLTrackElement.prototype) {
8116        if (prop !== 'constructor') {
8117          trackElement[prop] = HTMLTrackElement.prototype[prop];
8118        }
8119      }
8120    }
8121
8122    var track = new TextTrack(options);
8123
8124    trackElement.kind = track.kind;
8125    trackElement.src = track.src;
8126    trackElement.srclang = track.language;
8127    trackElement.label = track.label;
8128    trackElement['default'] = track['default'];
8129
8130    /**
8131     * @memberof HTMLTrackElement
8132     * @member {HTMLTrackElement~ReadyState} readyState
8133     *         The current ready state of the track element.
8134     * @instance
8135     */
8136    Object.defineProperty(trackElement, 'readyState', {
8137      get: function get$$1() {
8138        return readyState;
8139      }
8140    });
8141
8142    /**
8143     * @memberof HTMLTrackElement
8144     * @member {TextTrack} track
8145     *         The underlying TextTrack object.
8146     * @instance
8147     *
8148     */
8149    Object.defineProperty(trackElement, 'track', {
8150      get: function get$$1() {
8151        return track;
8152      }
8153    });
8154
8155    readyState = NONE;
8156
8157    /**
8158     * @listens TextTrack#loadeddata
8159     * @fires HTMLTrackElement#load
8160     */
8161    track.addEventListener('loadeddata', function () {
8162      readyState = LOADED;
8163
8164      trackElement.trigger({
8165        type: 'load',
8166        target: trackElement
8167      });
8168    });
8169
8170    if (IS_IE8) {
8171      var _ret;
8172
8173      return _ret = trackElement, possibleConstructorReturn(_this, _ret);
8174    }
8175    return _this;
8176  }
8177
8178  return HTMLTrackElement;
8179}(EventTarget);
8180
8181HTMLTrackElement.prototype.allowedEvents_ = {
8182  load: 'load'
8183};
8184
8185HTMLTrackElement.NONE = NONE;
8186HTMLTrackElement.LOADING = LOADING;
8187HTMLTrackElement.LOADED = LOADED;
8188HTMLTrackElement.ERROR = ERROR;
8189
8190/*
8191 * This file contains all track properties that are used in
8192 * player.js, tech.js, html5.js and possibly other techs in the future.
8193 */
8194
8195var NORMAL = {
8196  audio: {
8197    ListClass: AudioTrackList,
8198    TrackClass: AudioTrack,
8199    capitalName: 'Audio'
8200  },
8201  video: {
8202    ListClass: VideoTrackList,
8203    TrackClass: VideoTrack,
8204    capitalName: 'Video'
8205  },
8206  text: {
8207    ListClass: TextTrackList,
8208    TrackClass: TextTrack,
8209    capitalName: 'Text'
8210  }
8211};
8212
8213Object.keys(NORMAL).forEach(function (type) {
8214  NORMAL[type].getterName = type + 'Tracks';
8215  NORMAL[type].privateName = type + 'Tracks_';
8216});
8217
8218var REMOTE = {
8219  remoteText: {
8220    ListClass: TextTrackList,
8221    TrackClass: TextTrack,
8222    capitalName: 'RemoteText',
8223    getterName: 'remoteTextTracks',
8224    privateName: 'remoteTextTracks_'
8225  },
8226  remoteTextEl: {
8227    ListClass: HtmlTrackElementList,
8228    TrackClass: HTMLTrackElement,
8229    capitalName: 'RemoteTextTrackEls',
8230    getterName: 'remoteTextTrackEls',
8231    privateName: 'remoteTextTrackEls_'
8232  }
8233};
8234
8235var ALL = mergeOptions(NORMAL, REMOTE);
8236
8237REMOTE.names = Object.keys(REMOTE);
8238NORMAL.names = Object.keys(NORMAL);
8239ALL.names = [].concat(REMOTE.names).concat(NORMAL.names);
8240
8241/**
8242 * Copyright 2013 vtt.js Contributors
8243 *
8244 * Licensed under the Apache License, Version 2.0 (the "License");
vendor: 16,438 bytes, lines 8245-8782
8245 * you may not use this file except in compliance with the License.
8246 * You may obtain a copy of the License at
8247 *
8248 *   http://www.apache.org/licenses/LICENSE-2.0
8249 *
8250 * Unless required by applicable law or agreed to in writing, software
8251 * distributed under the License is distributed on an "AS IS" BASIS,
8252 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
8253 * See the License for the specific language governing permissions and
8254 * limitations under the License.
8255 */
8256
8257/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
8258/* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */
8259var _objCreate = Object.create || (function() {
8260  function F() {}
8261  return function(o) {
8262    if (arguments.length !== 1) {
8263      throw new Error('Object.create shim only accepts one parameter.');
8264    }
8265    F.prototype = o;
8266    return new F();
8267  };
8268})();
8269
8270// Creates a new ParserError object from an errorData object. The errorData
8271// object should have default code and message properties. The default message
8272// property can be overriden by passing in a message parameter.
8273// See ParsingError.Errors below for acceptable errors.
8274function ParsingError(errorData, message) {
8275  this.name = "ParsingError";
8276  this.code = errorData.code;
8277  this.message = message || errorData.message;
8278}
8279ParsingError.prototype = _objCreate(Error.prototype);
8280ParsingError.prototype.constructor = ParsingError;
8281
8282// ParsingError metadata for acceptable ParsingErrors.
8283ParsingError.Errors = {
8284  BadSignature: {
8285    code: 0,
8286    message: "Malformed WebVTT signature."
8287  },
8288  BadTimeStamp: {
8289    code: 1,
8290    message: "Malformed time stamp."
8291  }
8292};
8293
8294// Try to parse input as a time stamp.
8295function parseTimeStamp(input) {
8296
8297  function computeSeconds(h, m, s, f) {
8298    return (h | 0) * 3600 + (m | 0) * 60 + (s | 0) + (f | 0) / 1000;
8299  }
8300
8301  var m = input.match(/^(\d+):(\d{2})(:\d{2})?\.(\d{3})/);
8302  if (!m) {
8303    return null;
8304  }
8305
8306  if (m[3]) {
8307    // Timestamp takes the form of [hours]:[minutes]:[seconds].[milliseconds]
8308    return computeSeconds(m[1], m[2], m[3].replace(":", ""), m[4]);
8309  } else if (m[1] > 59) {
8310    // Timestamp takes the form of [hours]:[minutes].[milliseconds]
8311    // First position is hours as it's over 59.
8312    return computeSeconds(m[1], m[2], 0,  m[4]);
8313  } else {
8314    // Timestamp takes the form of [minutes]:[seconds].[milliseconds]
8315    return computeSeconds(0, m[1], m[2], m[4]);
8316  }
8317}
8318
8319// A settings object holds key/value pairs and will ignore anything but the first
8320// assignment to a specific key.
8321function Settings() {
8322  this.values = _objCreate(null);
8323}
8324
8325Settings.prototype = {
8326  // Only accept the first assignment to any key.
8327  set: function(k, v) {
8328    if (!this.get(k) && v !== "") {
8329      this.values[k] = v;
8330    }
8331  },
8332  // Return the value for a key, or a default value.
8333  // If 'defaultKey' is passed then 'dflt' is assumed to be an object with
8334  // a number of possible default values as properties where 'defaultKey' is
8335  // the key of the property that will be chosen; otherwise it's assumed to be
8336  // a single value.
8337  get: function(k, dflt, defaultKey) {
8338    if (defaultKey) {
8339      return this.has(k) ? this.values[k] : dflt[defaultKey];
8340    }
8341    return this.has(k) ? this.values[k] : dflt;
8342  },
8343  // Check whether we have a value for a key.
8344  has: function(k) {
8345    return k in this.values;
8346  },
8347  // Accept a setting if its one of the given alternatives.
8348  alt: function(k, v, a) {
8349    for (var n = 0; n < a.length; ++n) {
8350      if (v === a[n]) {
8351        this.set(k, v);
8352        break;
8353      }
8354    }
8355  },
8356  // Accept a setting if its a valid (signed) integer.
8357  integer: function(k, v) {
8358    if (/^-?\d+$/.test(v)) { // integer
8359      this.set(k, parseInt(v, 10));
8360    }
8361  },
8362  // Accept a setting if its a valid percentage.
8363  percent: function(k, v) {
8364    var m;
8365    if ((m = v.match(/^([\d]{1,3})(\.[\d]*)?%$/))) {
8366      v = parseFloat(v);
8367      if (v >= 0 && v <= 100) {
8368        this.set(k, v);
8369        return true;
8370      }
8371    }
8372    return false;
8373  }
8374};
8375
8376// Helper function to parse input into groups separated by 'groupDelim', and
8377// interprete each group as a key/value pair separated by 'keyValueDelim'.
8378function parseOptions(input, callback, keyValueDelim, groupDelim) {
8379  var groups = groupDelim ? input.split(groupDelim) : [input];
8380  for (var i in groups) {
8381    if (typeof groups[i] !== "string") {
8382      continue;
8383    }
8384    var kv = groups[i].split(keyValueDelim);
8385    if (kv.length !== 2) {
8386      continue;
8387    }
8388    var k = kv[0];
8389    var v = kv[1];
8390    callback(k, v);
8391  }
8392}
8393
8394function parseCue(input, cue, regionList) {
8395  // Remember the original input if we need to throw an error.
8396  var oInput = input;
8397  // 4.1 WebVTT timestamp
8398  function consumeTimeStamp() {
8399    var ts = parseTimeStamp(input);
8400    if (ts === null) {
8401      throw new ParsingError(ParsingError.Errors.BadTimeStamp,
8402                            "Malformed timestamp: " + oInput);
8403    }
8404    // Remove time stamp from input.
8405    input = input.replace(/^[^\sa-zA-Z-]+/, "");
8406    return ts;
8407  }
8408
8409  // 4.4.2 WebVTT cue settings
8410  function consumeCueSettings(input, cue) {
8411    var settings = new Settings();
8412
8413    parseOptions(input, function (k, v) {
8414      switch (k) {
8415      case "region":
8416        // Find the last region we parsed with the same region id.
8417        for (var i = regionList.length - 1; i >= 0; i--) {
8418          if (regionList[i].id === v) {
8419            settings.set(k, regionList[i].region);
8420            break;
8421          }
8422        }
8423        break;
8424      case "vertical":
8425        settings.alt(k, v, ["rl", "lr"]);
8426        break;
8427      case "line":
8428        var vals = v.split(","),
8429            vals0 = vals[0];
8430        settings.integer(k, vals0);
8431        settings.percent(k, vals0) ? settings.set("snapToLines", false) : null;
8432        settings.alt(k, vals0, ["auto"]);
8433        if (vals.length === 2) {
8434          settings.alt("lineAlign", vals[1], ["start", "middle", "end"]);
8435        }
8436        break;
8437      case "position":
8438        vals = v.split(",");
8439        settings.percent(k, vals[0]);
8440        if (vals.length === 2) {
8441          settings.alt("positionAlign", vals[1], ["start", "middle", "end"]);
8442        }
8443        break;
8444      case "size":
8445        settings.percent(k, v);
8446        break;
8447      case "align":
8448        settings.alt(k, v, ["start", "middle", "end", "left", "right"]);
8449        break;
8450      }
8451    }, /:/, /\s/);
8452
8453    // Apply default values for any missing fields.
8454    cue.region = settings.get("region", null);
8455    cue.vertical = settings.get("vertical", "");
8456    cue.line = settings.get("line", "auto");
8457    cue.lineAlign = settings.get("lineAlign", "start");
8458    cue.snapToLines = settings.get("snapToLines", true);
8459    cue.size = settings.get("size", 100);
8460    cue.align = settings.get("align", "middle");
8461    cue.position = settings.get("position", {
8462      start: 0,
8463      left: 0,
8464      middle: 50,
8465      end: 100,
8466      right: 100
8467    }, cue.align);
8468    cue.positionAlign = settings.get("positionAlign", {
8469      start: "start",
8470      left: "start",
8471      middle: "middle",
8472      end: "end",
8473      right: "end"
8474    }, cue.align);
8475  }
8476
8477  function skipWhitespace() {
8478    input = input.replace(/^\s+/, "");
8479  }
8480
8481  // 4.1 WebVTT cue timings.
8482  skipWhitespace();
8483  cue.startTime = consumeTimeStamp();   // (1) collect cue start time
8484  skipWhitespace();
8485  if (input.substr(0, 3) !== "-->") {     // (3) next characters must match "-->"
8486    throw new ParsingError(ParsingError.Errors.BadTimeStamp,
8487                           "Malformed time stamp (time stamps must be separated by '-->'): " +
8488                           oInput);
8489  }
8490  input = input.substr(3);
8491  skipWhitespace();
8492  cue.endTime = consumeTimeStamp();     // (5) collect cue end time
8493
8494  // 4.1 WebVTT cue settings list.
8495  skipWhitespace();
8496  consumeCueSettings(input, cue);
8497}
8498
8499var ESCAPE = {
8500  "&amp;": "&",
8501  "&lt;": "<",
8502  "&gt;": ">",
8503  "&lrm;": "\u200e",
8504  "&rlm;": "\u200f",
8505  "&nbsp;": "\u00a0"
8506};
8507
8508var TAG_NAME = {
8509  c: "span",
8510  i: "i",
8511  b: "b",
8512  u: "u",
8513  ruby: "ruby",
8514  rt: "rt",
8515  v: "span",
8516  lang: "span"
8517};
8518
8519var TAG_ANNOTATION = {
8520  v: "title",
8521  lang: "lang"
8522};
8523
8524var NEEDS_PARENT = {
8525  rt: "ruby"
8526};
8527
8528// Parse content into a document fragment.
8529function parseContent(window, input) {
8530  function nextToken() {
8531    // Check for end-of-string.
8532    if (!input) {
8533      return null;
8534    }
8535
8536    // Consume 'n' characters from the input.
8537    function consume(result) {
8538      input = input.substr(result.length);
8539      return result;
8540    }
8541
8542    var m = input.match(/^([^<]*)(<[^>]*>?)?/);
8543    // If there is some text before the next tag, return it, otherwise return
8544    // the tag.
8545    return consume(m[1] ? m[1] : m[2]);
8546  }
8547
8548  // Unescape a string 's'.
8549  function unescape1(e) {
8550    return ESCAPE[e];
8551  }
8552  function unescape(s) {
8553    while ((m = s.match(/&(amp|lt|gt|lrm|rlm|nbsp);/))) {
8554      s = s.replace(m[0], unescape1);
8555    }
8556    return s;
8557  }
8558
8559  function shouldAdd(current, element) {
8560    return !NEEDS_PARENT[element.localName] ||
8561           NEEDS_PARENT[element.localName] === current.localName;
8562  }
8563
8564  // Create an element for this tag.
8565  function createElement(type, annotation) {
8566    var tagName = TAG_NAME[type];
8567    if (!tagName) {
8568      return null;
8569    }
8570    var element = window.document.createElement(tagName);
8571    element.localName = tagName;
8572    var name = TAG_ANNOTATION[type];
8573    if (name && annotation) {
8574      element[name] = annotation.trim();
8575    }
8576    return element;
8577  }
8578
8579  var rootDiv = window.document.createElement("div"),
8580      current = rootDiv,
8581      t,
8582      tagStack = [];
8583
8584  while ((t = nextToken()) !== null) {
8585    if (t[0] === '<') {
8586      if (t[1] === "/") {
8587        // If the closing tag matches, move back up to the parent node.
8588        if (tagStack.length &&
8589            tagStack[tagStack.length - 1] === t.substr(2).replace(">", "")) {
8590          tagStack.pop();
8591          current = current.parentNode;
8592        }
8593        // Otherwise just ignore the end tag.
8594        continue;
8595      }
8596      var ts = parseTimeStamp(t.substr(1, t.length - 2));
8597      var node;
8598      if (ts) {
8599        // Timestamps are lead nodes as well.
8600        node = window.document.createProcessingInstruction("timestamp", ts);
8601        current.appendChild(node);
8602        continue;
8603      }
8604      var m = t.match(/^<([^.\s/0-9>]+)(\.[^\s\\>]+)?([^>\\]+)?(\\?)>?$/);
8605      // If we can't parse the tag, skip to the next tag.
8606      if (!m) {
8607        continue;
8608      }
8609      // Try to construct an element, and ignore the tag if we couldn't.
8610      node = createElement(m[1], m[3]);
8611      if (!node) {
8612        continue;
8613      }
8614      // Determine if the tag should be added based on the context of where it
8615      // is placed in the cuetext.
8616      if (!shouldAdd(current, node)) {
8617        continue;
8618      }
8619      // Set the class list (as a list of classes, separated by space).
8620      if (m[2]) {
8621        node.className = m[2].substr(1).replace('.', ' ');
8622      }
8623      // Append the node to the current node, and enter the scope of the new
8624      // node.
8625      tagStack.push(m[1]);
8626      current.appendChild(node);
8627      current = node;
8628      continue;
8629    }
8630
8631    // Text nodes are leaf nodes.
8632    current.appendChild(window.document.createTextNode(unescape(t)));
8633  }
8634
8635  return rootDiv;
8636}
8637
8638// This is a list of all the Unicode characters that have a strong
8639// right-to-left category. What this means is that these characters are
8640// written right-to-left for sure. It was generated by pulling all the strong
8641// right-to-left characters out of the Unicode data table. That table can
8642// found at: http://www.unicode.org/Public/UNIDATA/UnicodeData.txt
8643var strongRTLRanges = [[0x5be, 0x5be], [0x5c0, 0x5c0], [0x5c3, 0x5c3], [0x5c6, 0x5c6],
8644 [0x5d0, 0x5ea], [0x5f0, 0x5f4], [0x608, 0x608], [0x60b, 0x60b], [0x60d, 0x60d],
8645 [0x61b, 0x61b], [0x61e, 0x64a], [0x66d, 0x66f], [0x671, 0x6d5], [0x6e5, 0x6e6],
8646 [0x6ee, 0x6ef], [0x6fa, 0x70d], [0x70f, 0x710], [0x712, 0x72f], [0x74d, 0x7a5],
8647 [0x7b1, 0x7b1], [0x7c0, 0x7ea], [0x7f4, 0x7f5], [0x7fa, 0x7fa], [0x800, 0x815],
8648 [0x81a, 0x81a], [0x824, 0x824], [0x828, 0x828], [0x830, 0x83e], [0x840, 0x858],
8649 [0x85e, 0x85e], [0x8a0, 0x8a0], [0x8a2, 0x8ac], [0x200f, 0x200f],
8650 [0xfb1d, 0xfb1d], [0xfb1f, 0xfb28], [0xfb2a, 0xfb36], [0xfb38, 0xfb3c],
8651 [0xfb3e, 0xfb3e], [0xfb40, 0xfb41], [0xfb43, 0xfb44], [0xfb46, 0xfbc1],
8652 [0xfbd3, 0xfd3d], [0xfd50, 0xfd8f], [0xfd92, 0xfdc7], [0xfdf0, 0xfdfc],
8653 [0xfe70, 0xfe74], [0xfe76, 0xfefc], [0x10800, 0x10805], [0x10808, 0x10808],
8654 [0x1080a, 0x10835], [0x10837, 0x10838], [0x1083c, 0x1083c], [0x1083f, 0x10855],
8655 [0x10857, 0x1085f], [0x10900, 0x1091b], [0x10920, 0x10939], [0x1093f, 0x1093f],
8656 [0x10980, 0x109b7], [0x109be, 0x109bf], [0x10a00, 0x10a00], [0x10a10, 0x10a13],
8657 [0x10a15, 0x10a17], [0x10a19, 0x10a33], [0x10a40, 0x10a47], [0x10a50, 0x10a58],
8658 [0x10a60, 0x10a7f], [0x10b00, 0x10b35], [0x10b40, 0x10b55], [0x10b58, 0x10b72],
8659 [0x10b78, 0x10b7f], [0x10c00, 0x10c48], [0x1ee00, 0x1ee03], [0x1ee05, 0x1ee1f],
8660 [0x1ee21, 0x1ee22], [0x1ee24, 0x1ee24], [0x1ee27, 0x1ee27], [0x1ee29, 0x1ee32],
8661 [0x1ee34, 0x1ee37], [0x1ee39, 0x1ee39], [0x1ee3b, 0x1ee3b], [0x1ee42, 0x1ee42],
8662 [0x1ee47, 0x1ee47], [0x1ee49, 0x1ee49], [0x1ee4b, 0x1ee4b], [0x1ee4d, 0x1ee4f],
8663 [0x1ee51, 0x1ee52], [0x1ee54, 0x1ee54], [0x1ee57, 0x1ee57], [0x1ee59, 0x1ee59],
8664 [0x1ee5b, 0x1ee5b], [0x1ee5d, 0x1ee5d], [0x1ee5f, 0x1ee5f], [0x1ee61, 0x1ee62],
8665 [0x1ee64, 0x1ee64], [0x1ee67, 0x1ee6a], [0x1ee6c, 0x1ee72], [0x1ee74, 0x1ee77],
8666 [0x1ee79, 0x1ee7c], [0x1ee7e, 0x1ee7e], [0x1ee80, 0x1ee89], [0x1ee8b, 0x1ee9b],
8667 [0x1eea1, 0x1eea3], [0x1eea5, 0x1eea9], [0x1eeab, 0x1eebb], [0x10fffd, 0x10fffd]];
8668
8669function isStrongRTLChar(charCode) {
8670  for (var i = 0; i < strongRTLRanges.length; i++) {
8671    var currentRange = strongRTLRanges[i];
8672    if (charCode >= currentRange[0] && charCode <= currentRange[1]) {
8673      return true;
8674    }
8675  }
8676
8677  return false;
8678}
8679
8680function determineBidi(cueDiv) {
8681  var nodeStack = [],
8682      text = "",
8683      charCode;
8684
8685  if (!cueDiv || !cueDiv.childNodes) {
8686    return "ltr";
8687  }
8688
8689  function pushNodes(nodeStack, node) {
8690    for (var i = node.childNodes.length - 1; i >= 0; i--) {
8691      nodeStack.push(node.childNodes[i]);
8692    }
8693  }
8694
8695  function nextTextNode(nodeStack) {
8696    if (!nodeStack || !nodeStack.length) {
8697      return null;
8698    }
8699
8700    var node = nodeStack.pop(),
8701        text = node.textContent || node.innerText;
8702    if (text) {
8703      // TODO: This should match all unicode type B characters (paragraph
8704      // separator characters). See issue #115.
8705      var m = text.match(/^.*(\n|\r)/);
8706      if (m) {
8707        nodeStack.length = 0;
8708        return m[0];
8709      }
8710      return text;
8711    }
8712    if (node.tagName === "ruby") {
8713      return nextTextNode(nodeStack);
8714    }
8715    if (node.childNodes) {
8716      pushNodes(nodeStack, node);
8717      return nextTextNode(nodeStack);
8718    }
8719  }
8720
8721  pushNodes(nodeStack, cueDiv);
8722  while ((text = nextTextNode(nodeStack))) {
8723    for (var i = 0; i < text.length; i++) {
8724      charCode = text.charCodeAt(i);
8725      if (isStrongRTLChar(charCode)) {
8726        return "rtl";
8727      }
8728    }
8729  }
8730  return "ltr";
8731}
8732
8733function computeLinePos(cue) {
8734  if (typeof cue.line === "number" &&
8735      (cue.snapToLines || (cue.line >= 0 && cue.line <= 100))) {
8736    return cue.line;
8737  }
8738  if (!cue.track || !cue.track.textTrackList ||
8739      !cue.track.textTrackList.mediaElement) {
8740    return -1;
8741  }
8742  var track = cue.track,
8743      trackList = track.textTrackList,
8744      count = 0;
8745  for (var i = 0; i < trackList.length && trackList[i] !== track; i++) {
8746    if (trackList[i].mode === "showing") {
8747      count++;
8748    }
8749  }
8750  return ++count * -1;
8751}
8752
8753function StyleBox() {
8754}
8755
8756// Apply styles to a div. If there is no div passed then it defaults to the
8757// div on 'this'.
8758StyleBox.prototype.applyStyles = function(styles, div) {
8759  div = div || this.div;
8760  for (var prop in styles) {
8761    if (styles.hasOwnProperty(prop)) {
8762      div.style[prop] = styles[prop];
8763    }
8764  }
8765};
8766
8767StyleBox.prototype.formatStyle = function(val, unit) {
8768  return val === 0 ? 0 : val + unit;
8769};
8770
8771// Constructs the computed display state of the cue (a div). Places the div
8772// into the overlay which should be a block level element (usually a div).
8773function CueStyleBox(window, cue, styleOptions) {
8774  var isIE8 = (/MSIE\s8\.0/).test(navigator.userAgent);
8775  var color = "rgba(255, 255, 255, 1)";
8776  var backgroundColor = "rgba(0, 0, 0, 0.8)";
8777
8778  if (isIE8) {
8779    color = "rgb(255, 255, 255)";
8780    backgroundColor = "rgb(0, 0, 0)";
8781  }
8782
vendor: 5,880 bytes, lines 8783-8959
8783  StyleBox.call(this);
8784  this.cue = cue;
8785
8786  // Parse our cue's text into a DOM tree rooted at 'cueDiv'. This div will
8787  // have inline positioning and will function as the cue background box.
8788  this.cueDiv = parseContent(window, cue.text);
8789  var styles = {
8790    color: color,
8791    backgroundColor: backgroundColor,
8792    position: "relative",
8793    left: 0,
8794    right: 0,
8795    top: 0,
8796    bottom: 0,
8797    display: "inline"
8798  };
8799
8800  if (!isIE8) {
8801    styles.writingMode = cue.vertical === "" ? "horizontal-tb"
8802                                             : cue.vertical === "lr" ? "vertical-lr"
8803                                                                     : "vertical-rl";
8804    styles.unicodeBidi = "plaintext";
8805  }
8806  this.applyStyles(styles, this.cueDiv);
8807
8808  // Create an absolutely positioned div that will be used to position the cue
8809  // div. Note, all WebVTT cue-setting alignments are equivalent to the CSS
8810  // mirrors of them except "middle" which is "center" in CSS.
8811  this.div = window.document.createElement("div");
8812  styles = {
8813    textAlign: cue.align === "middle" ? "center" : cue.align,
8814    font: styleOptions.font,
8815    whiteSpace: "pre-line",
8816    position: "absolute"
8817  };
8818
8819  if (!isIE8) {
8820    styles.direction = determineBidi(this.cueDiv);
8821    styles.writingMode = cue.vertical === "" ? "horizontal-tb"
8822                                             : cue.vertical === "lr" ? "vertical-lr"
8823                                                                     : "vertical-rl".
8824    stylesunicodeBidi =  "plaintext";
8825  }
8826
8827  this.applyStyles(styles);
8828
8829  this.div.appendChild(this.cueDiv);
8830
8831  // Calculate the distance from the reference edge of the viewport to the text
8832  // position of the cue box. The reference edge will be resolved later when
8833  // the box orientation styles are applied.
8834  var textPos = 0;
8835  switch (cue.positionAlign) {
8836  case "start":
8837    textPos = cue.position;
8838    break;
8839  case "middle":
8840    textPos = cue.position - (cue.size / 2);
8841    break;
8842  case "end":
8843    textPos = cue.position - cue.size;
8844    break;
8845  }
8846
8847  // Horizontal box orientation; textPos is the distance from the left edge of the
8848  // area to the left edge of the box and cue.size is the distance extending to
8849  // the right from there.
8850  if (cue.vertical === "") {
8851    this.applyStyles({
8852      left:  this.formatStyle(textPos, "%"),
8853      width: this.formatStyle(cue.size, "%")
8854    });
8855  // Vertical box orientation; textPos is the distance from the top edge of the
8856  // area to the top edge of the box and cue.size is the height extending
8857  // downwards from there.
8858  } else {
8859    this.applyStyles({
8860      top: this.formatStyle(textPos, "%"),
8861      height: this.formatStyle(cue.size, "%")
8862    });
8863  }
8864
8865  this.move = function(box) {
8866    this.applyStyles({
8867      top: this.formatStyle(box.top, "px"),
8868      bottom: this.formatStyle(box.bottom, "px"),
8869      left: this.formatStyle(box.left, "px"),
8870      right: this.formatStyle(box.right, "px"),
8871      height: this.formatStyle(box.height, "px"),
8872      width: this.formatStyle(box.width, "px")
8873    });
8874  };
8875}
8876CueStyleBox.prototype = _objCreate(StyleBox.prototype);
8877CueStyleBox.prototype.constructor = CueStyleBox;
8878
8879// Represents the co-ordinates of an Element in a way that we can easily
8880// compute things with such as if it overlaps or intersects with another Element.
8881// Can initialize it with either a StyleBox or another BoxPosition.
8882function BoxPosition(obj) {
8883  var isIE8 = (/MSIE\s8\.0/).test(navigator.userAgent);
8884
8885  // Either a BoxPosition was passed in and we need to copy it, or a StyleBox
8886  // was passed in and we need to copy the results of 'getBoundingClientRect'
8887  // as the object returned is readonly. All co-ordinate values are in reference
8888  // to the viewport origin (top left).
8889  var lh, height, width, top;
8890  if (obj.div) {
8891    height = obj.div.offsetHeight;
8892    width = obj.div.offsetWidth;
8893    top = obj.div.offsetTop;
8894
8895    var rects = (rects = obj.div.childNodes) && (rects = rects[0]) &&
8896                rects.getClientRects && rects.getClientRects();
8897    obj = obj.div.getBoundingClientRect();
8898    // In certain cases the outter div will be slightly larger then the sum of
8899    // the inner div's lines. This could be due to bold text, etc, on some platforms.
8900    // In this case we should get the average line height and use that. This will
8901    // result in the desired behaviour.
8902    lh = rects ? Math.max((rects[0] && rects[0].height) || 0, obj.height / rects.length)
8903               : 0;
8904
8905  }
8906  this.left = obj.left;
8907  this.right = obj.right;
8908  this.top = obj.top || top;
8909  this.height = obj.height || height;
8910  this.bottom = obj.bottom || (top + (obj.height || height));
8911  this.width = obj.width || width;
8912  this.lineHeight = lh !== undefined ? lh : obj.lineHeight;
8913
8914  if (isIE8 && !this.lineHeight) {
8915    this.lineHeight = 13;
8916  }
8917}
8918
8919// Move the box along a particular axis. Optionally pass in an amount to move
8920// the box. If no amount is passed then the default is the line height of the
8921// box.
8922BoxPosition.prototype.move = function(axis, toMove) {
8923  toMove = toMove !== undefined ? toMove : this.lineHeight;
8924  switch (axis) {
8925  case "+x":
8926    this.left += toMove;
8927    this.right += toMove;
8928    break;
8929  case "-x":
8930    this.left -= toMove;
8931    this.right -= toMove;
8932    break;
8933  case "+y":
8934    this.top += toMove;
8935    this.bottom += toMove;
8936    break;
8937  case "-y":
8938    this.top -= toMove;
8939    this.bottom -= toMove;
8940    break;
8941  }
8942};
8943
8944// Check if this box overlaps another box, b2.
8945BoxPosition.prototype.overlaps = function(b2) {
8946  return this.left < b2.right &&
8947         this.right > b2.left &&
8948         this.top < b2.bottom &&
8949         this.bottom > b2.top;
8950};
8951
8952// Check if this box overlaps any other boxes in boxes.
8953BoxPosition.prototype.overlapsAny = function(boxes) {
8954  for (var i = 0; i < boxes.length; i++) {
8955    if (this.overlaps(boxes[i])) {
8956      return true;
8957    }
8958  }
8959  return false;
vendor: 6,016 bytes, lines 8960-9123
8960};
8961
8962// Check if this box is within another box.
8963BoxPosition.prototype.within = function(container) {
8964  return this.top >= container.top &&
8965         this.bottom <= container.bottom &&
8966         this.left >= container.left &&
8967         this.right <= container.right;
8968};
8969
8970// Check if this box is entirely within the container or it is overlapping
8971// on the edge opposite of the axis direction passed. For example, if "+x" is
8972// passed and the box is overlapping on the left edge of the container, then
8973// return true.
8974BoxPosition.prototype.overlapsOppositeAxis = function(container, axis) {
8975  switch (axis) {
8976  case "+x":
8977    return this.left < container.left;
8978  case "-x":
8979    return this.right > container.right;
8980  case "+y":
8981    return this.top < container.top;
8982  case "-y":
8983    return this.bottom > container.bottom;
8984  }
8985};
8986
8987// Find the percentage of the area that this box is overlapping with another
8988// box.
8989BoxPosition.prototype.intersectPercentage = function(b2) {
8990  var x = Math.max(0, Math.min(this.right, b2.right) - Math.max(this.left, b2.left)),
8991      y = Math.max(0, Math.min(this.bottom, b2.bottom) - Math.max(this.top, b2.top)),
8992      intersectArea = x * y;
8993  return intersectArea / (this.height * this.width);
8994};
8995
8996// Convert the positions from this box to CSS compatible positions using
8997// the reference container's positions. This has to be done because this
8998// box's positions are in reference to the viewport origin, whereas, CSS
8999// values are in referecne to their respective edges.
9000BoxPosition.prototype.toCSSCompatValues = function(reference) {
9001  return {
9002    top: this.top - reference.top,
9003    bottom: reference.bottom - this.bottom,
9004    left: this.left - reference.left,
9005    right: reference.right - this.right,
9006    height: this.height,
9007    width: this.width
9008  };
9009};
9010
9011// Get an object that represents the box's position without anything extra.
9012// Can pass a StyleBox, HTMLElement, or another BoxPositon.
9013BoxPosition.getSimpleBoxPosition = function(obj) {
9014  var height = obj.div ? obj.div.offsetHeight : obj.tagName ? obj.offsetHeight : 0;
9015  var width = obj.div ? obj.div.offsetWidth : obj.tagName ? obj.offsetWidth : 0;
9016  var top = obj.div ? obj.div.offsetTop : obj.tagName ? obj.offsetTop : 0;
9017
9018  obj = obj.div ? obj.div.getBoundingClientRect() :
9019                obj.tagName ? obj.getBoundingClientRect() : obj;
9020  var ret = {
9021    left: obj.left,
9022    right: obj.right,
9023    top: obj.top || top,
9024    height: obj.height || height,
9025    bottom: obj.bottom || (top + (obj.height || height)),
9026    width: obj.width || width
9027  };
9028  return ret;
9029};
9030
9031// Move a StyleBox to its specified, or next best, position. The containerBox
9032// is the box that contains the StyleBox, such as a div. boxPositions are
9033// a list of other boxes that the styleBox can't overlap with.
9034function moveBoxToLinePosition(window, styleBox, containerBox, boxPositions) {
9035
9036  // Find the best position for a cue box, b, on the video. The axis parameter
9037  // is a list of axis, the order of which, it will move the box along. For example:
9038  // Passing ["+x", "-x"] will move the box first along the x axis in the positive
9039  // direction. If it doesn't find a good position for it there it will then move
9040  // it along the x axis in the negative direction.
9041  function findBestPosition(b, axis) {
9042    var bestPosition,
9043        specifiedPosition = new BoxPosition(b),
9044        percentage = 1; // Highest possible so the first thing we get is better.
9045
9046    for (var i = 0; i < axis.length; i++) {
9047      while (b.overlapsOppositeAxis(containerBox, axis[i]) ||
9048             (b.within(containerBox) && b.overlapsAny(boxPositions))) {
9049        b.move(axis[i]);
9050      }
9051      // We found a spot where we aren't overlapping anything. This is our
9052      // best position.
9053      if (b.within(containerBox)) {
9054        return b;
9055      }
9056      var p = b.intersectPercentage(containerBox);
9057      // If we're outside the container box less then we were on our last try
9058      // then remember this position as the best position.
9059      if (percentage > p) {
9060        bestPosition = new BoxPosition(b);
9061        percentage = p;
9062      }
9063      // Reset the box position to the specified position.
9064      b = new BoxPosition(specifiedPosition);
9065    }
9066    return bestPosition || specifiedPosition;
9067  }
9068
9069  var boxPosition = new BoxPosition(styleBox),
9070      cue = styleBox.cue,
9071      linePos = computeLinePos(cue),
9072      axis = [];
9073
9074  // If we have a line number to align the cue to.
9075  if (cue.snapToLines) {
9076    var size;
9077    switch (cue.vertical) {
9078    case "":
9079      axis = [ "+y", "-y" ];
9080      size = "height";
9081      break;
9082    case "rl":
9083      axis = [ "+x", "-x" ];
9084      size = "width";
9085      break;
9086    case "lr":
9087      axis = [ "-x", "+x" ];
9088      size = "width";
9089      break;
9090    }
9091
9092    var step = boxPosition.lineHeight,
9093        position = step * Math.round(linePos),
9094        maxPosition = containerBox[size] + step,
9095        initialAxis = axis[0];
9096
9097    // If the specified intial position is greater then the max position then
9098    // clamp the box to the amount of steps it would take for the box to
9099    // reach the max position.
9100    if (Math.abs(position) > maxPosition) {
9101      position = position < 0 ? -1 : 1;
9102      position *= Math.ceil(maxPosition / step) * step;
9103    }
9104
9105    // If computed line position returns negative then line numbers are
9106    // relative to the bottom of the video instead of the top. Therefore, we
9107    // need to increase our initial position by the length or width of the
9108    // video, depending on the writing direction, and reverse our axis directions.
9109    if (linePos < 0) {
9110      position += cue.vertical === "" ? containerBox.height : containerBox.width;
9111      axis = axis.reverse();
9112    }
9113
9114    // Move the box to the specified position. This may not be its best
9115    // position.
9116    boxPosition.move(initialAxis, position);
9117
9118  } else {
9119    // If we have a percentage line value for the cue.
9120    var calculatedPercentage = (boxPosition.lineHeight / containerBox.height) * 100;
9121
9122    switch (cue.lineAlign) {
9123    case "middle
vendor: 10,353 bytes, lines 9123-9484
9123":
9124      linePos -= (calculatedPercentage / 2);
9125      break;
9126    case "end":
9127      linePos -= calculatedPercentage;
9128      break;
9129    }
9130
9131    // Apply initial line position to the cue box.
9132    switch (cue.vertical) {
9133    case "":
9134      styleBox.applyStyles({
9135        top: styleBox.formatStyle(linePos, "%")
9136      });
9137      break;
9138    case "rl":
9139      styleBox.applyStyles({
9140        left: styleBox.formatStyle(linePos, "%")
9141      });
9142      break;
9143    case "lr":
9144      styleBox.applyStyles({
9145        right: styleBox.formatStyle(linePos, "%")
9146      });
9147      break;
9148    }
9149
9150    axis = [ "+y", "-x", "+x", "-y" ];
9151
9152    // Get the box position again after we've applied the specified positioning
9153    // to it.
9154    boxPosition = new BoxPosition(styleBox);
9155  }
9156
9157  var bestPosition = findBestPosition(boxPosition, axis);
9158  styleBox.move(bestPosition.toCSSCompatValues(containerBox));
9159}
9160
9161function WebVTT$1() {
9162  // Nothing
9163}
9164
9165// Helper to allow strings to be decoded instead of the default binary utf8 data.
9166WebVTT$1.StringDecoder = function() {
9167  return {
9168    decode: function(data) {
9169      if (!data) {
9170        return "";
9171      }
9172      if (typeof data !== "string") {
9173        throw new Error("Error - expected string data.");
9174      }
9175      return decodeURIComponent(encodeURIComponent(data));
9176    }
9177  };
9178};
9179
9180WebVTT$1.convertCueToDOMTree = function(window, cuetext) {
9181  if (!window || !cuetext) {
9182    return null;
9183  }
9184  return parseContent(window, cuetext);
9185};
9186
9187var FONT_SIZE_PERCENT = 0.05;
9188var FONT_STYLE = "sans-serif";
9189var CUE_BACKGROUND_PADDING = "1.5%";
9190
9191// Runs the processing model over the cues and regions passed to it.
9192// @param overlay A block level element (usually a div) that the computed cues
9193//                and regions will be placed into.
9194WebVTT$1.processCues = function(window, cues, overlay) {
9195  if (!window || !cues || !overlay) {
9196    return null;
9197  }
9198
9199  // Remove all previous children.
9200  while (overlay.firstChild) {
9201    overlay.removeChild(overlay.firstChild);
9202  }
9203
9204  var paddedOverlay = window.document.createElement("div");
9205  paddedOverlay.style.position = "absolute";
9206  paddedOverlay.style.left = "0";
9207  paddedOverlay.style.right = "0";
9208  paddedOverlay.style.top = "0";
9209  paddedOverlay.style.bottom = "0";
9210  paddedOverlay.style.margin = CUE_BACKGROUND_PADDING;
9211  overlay.appendChild(paddedOverlay);
9212
9213  // Determine if we need to compute the display states of the cues. This could
9214  // be the case if a cue's state has been changed since the last computation or
9215  // if it has not been computed yet.
9216  function shouldCompute(cues) {
9217    for (var i = 0; i < cues.length; i++) {
9218      if (cues[i].hasBeenReset || !cues[i].displayState) {
9219        return true;
9220      }
9221    }
9222    return false;
9223  }
9224
9225  // We don't need to recompute the cues' display states. Just reuse them.
9226  if (!shouldCompute(cues)) {
9227    for (var i = 0; i < cues.length; i++) {
9228      paddedOverlay.appendChild(cues[i].displayState);
9229    }
9230    return;
9231  }
9232
9233  var boxPositions = [],
9234      containerBox = BoxPosition.getSimpleBoxPosition(paddedOverlay),
9235      fontSize = Math.round(containerBox.height * FONT_SIZE_PERCENT * 100) / 100;
9236  var styleOptions = {
9237    font: fontSize + "px " + FONT_STYLE
9238  };
9239
9240  (function() {
9241    var styleBox, cue;
9242
9243    for (var i = 0; i < cues.length; i++) {
9244      cue = cues[i];
9245
9246      // Compute the intial position and styles of the cue div.
9247      styleBox = new CueStyleBox(window, cue, styleOptions);
9248      paddedOverlay.appendChild(styleBox.div);
9249
9250      // Move the cue div to it's correct line position.
9251      moveBoxToLinePosition(window, styleBox, containerBox, boxPositions);
9252
9253      // Remember the computed div so that we don't have to recompute it later
9254      // if we don't have too.
9255      cue.displayState = styleBox.div;
9256
9257      boxPositions.push(BoxPosition.getSimpleBoxPosition(styleBox));
9258    }
9259  })();
9260};
9261
9262WebVTT$1.Parser = function(window, vttjs, decoder) {
9263  if (!decoder) {
9264    decoder = vttjs;
9265    vttjs = {};
9266  }
9267  if (!vttjs) {
9268    vttjs = {};
9269  }
9270
9271  this.window = window;
9272  this.vttjs = vttjs;
9273  this.state = "INITIAL";
9274  this.buffer = "";
9275  this.decoder = decoder || new TextDecoder("utf8");
9276  this.regionList = [];
9277};
9278
9279WebVTT$1.Parser.prototype = {
9280  // If the error is a ParsingError then report it to the consumer if
9281  // possible. If it's not a ParsingError then throw it like normal.
9282  reportOrThrowError: function(e) {
9283    if (e instanceof ParsingError) {
9284      this.onparsingerror && this.onparsingerror(e);
9285    } else {
9286      throw e;
9287    }
9288  },
9289  parse: function (data) {
9290    var self = this;
9291
9292    // If there is no data then we won't decode it, but will just try to parse
9293    // whatever is in buffer already. This may occur in circumstances, for
9294    // example when flush() is called.
9295    if (data) {
9296      // Try to decode the data that we received.
9297      self.buffer += self.decoder.decode(data, {stream: true});
9298    }
9299
9300    function collectNextLine() {
9301      var buffer = self.buffer;
9302      var pos = 0;
9303      while (pos < buffer.length && buffer[pos] !== '\r' && buffer[pos] !== '\n') {
9304        ++pos;
9305      }
9306      var line = buffer.substr(0, pos);
9307      // Advance the buffer early in case we fail below.
9308      if (buffer[pos] === '\r') {
9309        ++pos;
9310      }
9311      if (buffer[pos] === '\n') {
9312        ++pos;
9313      }
9314      self.buffer = buffer.substr(pos);
9315      return line;
9316    }
9317
9318    // 3.4 WebVTT region and WebVTT region settings syntax
9319    function parseRegion(input) {
9320      var settings = new Settings();
9321
9322      parseOptions(input, function (k, v) {
9323        switch (k) {
9324        case "id":
9325          settings.set(k, v);
9326          break;
9327        case "width":
9328          settings.percent(k, v);
9329          break;
9330        case "lines":
9331          settings.integer(k, v);
9332          break;
9333        case "regionanchor":
9334        case "viewportanchor":
9335          var xy = v.split(',');
9336          if (xy.length !== 2) {
9337            break;
9338          }
9339          // We have to make sure both x and y parse, so use a temporary
9340          // settings object here.
9341          var anchor = new Settings();
9342          anchor.percent("x", xy[0]);
9343          anchor.percent("y", xy[1]);
9344          if (!anchor.has("x") || !anchor.has("y")) {
9345            break;
9346          }
9347          settings.set(k + "X", anchor.get("x"));
9348          settings.set(k + "Y", anchor.get("y"));
9349          break;
9350        case "scroll":
9351          settings.alt(k, v, ["up"]);
9352          break;
9353        }
9354      }, /=/, /\s/);
9355
9356      // Create the region, using default values for any values that were not
9357      // specified.
9358      if (settings.has("id")) {
9359        var region = new (self.vttjs.VTTRegion || self.window.VTTRegion)();
9360        region.width = settings.get("width", 100);
9361        region.lines = settings.get("lines", 3);
9362        region.regionAnchorX = settings.get("regionanchorX", 0);
9363        region.regionAnchorY = settings.get("regionanchorY", 100);
9364        region.viewportAnchorX = settings.get("viewportanchorX", 0);
9365        region.viewportAnchorY = settings.get("viewportanchorY", 100);
9366        region.scroll = settings.get("scroll", "");
9367        // Register the region.
9368        self.onregion && self.onregion(region);
9369        // Remember the VTTRegion for later in case we parse any VTTCues that
9370        // reference it.
9371        self.regionList.push({
9372          id: settings.get("id"),
9373          region: region
9374        });
9375      }
9376    }
9377
9378    // draft-pantos-http-live-streaming-20
9379    // https://tools.ietf.org/html/draft-pantos-http-live-streaming-20#section-3.5
9380    // 3.5 WebVTT
9381    function parseTimestampMap(input) {
9382      var settings = new Settings();
9383
9384      parseOptions(input, function(k, v) {
9385        switch(k) {
9386        case "MPEGT":
9387          settings.integer(k + 'S', v);
9388          break;
9389        case "LOCA":
9390          settings.set(k + 'L', parseTimeStamp(v));
9391          break;
9392        }
9393      }, /[^\d]:/, /,/);
9394
9395      self.ontimestampmap && self.ontimestampmap({
9396        "MPEGTS": settings.get("MPEGTS"),
9397        "LOCAL": settings.get("LOCAL")
9398      });
9399    }
9400
9401    // 3.2 WebVTT metadata header syntax
9402    function parseHeader(input) {
9403      if (input.match(/X-TIMESTAMP-MAP/)) {
9404        // This line contains HLS X-TIMESTAMP-MAP metadata
9405        parseOptions(input, function(k, v) {
9406          switch(k) {
9407          case "X-TIMESTAMP-MAP":
9408            parseTimestampMap(v);
9409            break;
9410          }
9411        }, /=/);
9412      } else {
9413        parseOptions(input, function (k, v) {
9414          switch (k) {
9415          case "Region":
9416            // 3.3 WebVTT region metadata header syntax
9417            parseRegion(v);
9418            break;
9419          }
9420        }, /:/);
9421      }
9422
9423    }
9424
9425    // 5.1 WebVTT file parsing.
9426    try {
9427      var line;
9428      if (self.state === "INITIAL") {
9429        // We can't start parsing until we have the first line.
9430        if (!/\r\n|\n/.test(self.buffer)) {
9431          return this;
9432        }
9433
9434        line = collectNextLine();
9435
9436        var m = line.match(/^WEBVTT([ \t].*)?$/);
9437        if (!m || !m[0]) {
9438          throw new ParsingError(ParsingError.Errors.BadSignature);
9439        }
9440
9441        self.state = "HEADER";
9442      }
9443
9444      var alreadyCollectedLine = false;
9445      while (self.buffer) {
9446        // We can't parse a line until we have the full line.
9447        if (!/\r\n|\n/.test(self.buffer)) {
9448          return this;
9449        }
9450
9451        if (!alreadyCollectedLine) {
9452          line = collectNextLine();
9453        } else {
9454          alreadyCollectedLine = false;
9455        }
9456
9457        switch (self.state) {
9458        case "HEADER":
9459          // 13-18 - Allow a header (metadata) under the WEBVTT line.
9460          if (/:/.test(line)) {
9461            parseHeader(line);
9462          } else if (!line) {
9463            // An empty line terminates the header and starts the body (cues).
9464            self.state = "ID";
9465          }
9466          continue;
9467        case "NOTE":
9468          // Ignore NOTE blocks.
9469          if (!line) {
9470            self.state = "ID";
9471          }
9472          continue;
9473        case "ID":
9474          // Check for the start of NOTE blocks.
9475          if (/^NOTE($|[ \t])/.test(line)) {
9476            self.state = "NOTE";
9477            break;
9478          }
9479          // 19-29 - Allow any number of line terminators, then initialize new cue values.
9480          if (!line) {
9481            continue;
9482          }
9483          self.cue = new (self.vttjs.VTTCue || self.window.VTTCue)(0, 0, "");
9484          
vendor: 2,935 bytes, lines 9484-9574
9484self.state = "CUE";
9485          // 30-39 - Check if self line contains an optional identifier or timing data.
9486          if (line.indexOf("-->") === -1) {
9487            self.cue.id = line;
9488            continue;
9489          }
9490          // Process line as start of a cue.
9491          /*falls through*/
9492        case "CUE":
9493          // 40 - Collect cue timings and settings.
9494          try {
9495            parseCue(line, self.cue, self.regionList);
9496          } catch (e) {
9497            self.reportOrThrowError(e);
9498            // In case of an error ignore rest of the cue.
9499            self.cue = null;
9500            self.state = "BADCUE";
9501            continue;
9502          }
9503          self.state = "CUETEXT";
9504          continue;
9505        case "CUETEXT":
9506          var hasSubstring = line.indexOf("-->") !== -1;
9507          // 34 - If we have an empty line then report the cue.
9508          // 35 - If we have the special substring '-->' then report the cue,
9509          // but do not collect the line as we need to process the current
9510          // one as a new cue.
9511          if (!line || hasSubstring && (alreadyCollectedLine = true)) {
9512            // We are done parsing self cue.
9513            self.oncue && self.oncue(self.cue);
9514            self.cue = null;
9515            self.state = "ID";
9516            continue;
9517          }
9518          if (self.cue.text) {
9519            self.cue.text += "\n";
9520          }
9521          self.cue.text += line;
9522          continue;
9523        case "BADCUE": // BADCUE
9524          // 54-62 - Collect and discard the remaining cue.
9525          if (!line) {
9526            self.state = "ID";
9527          }
9528          continue;
9529        }
9530      }
9531    } catch (e) {
9532      self.reportOrThrowError(e);
9533
9534      // If we are currently parsing a cue, report what we have.
9535      if (self.state === "CUETEXT" && self.cue && self.oncue) {
9536        self.oncue(self.cue);
9537      }
9538      self.cue = null;
9539      // Enter BADWEBVTT state if header was not parsed correctly otherwise
9540      // another exception occurred so enter BADCUE state.
9541      self.state = self.state === "INITIAL" ? "BADWEBVTT" : "BADCUE";
9542    }
9543    return this;
9544  },
9545  flush: function () {
9546    var self = this;
9547    try {
9548      // Finish decoding the stream.
9549      self.buffer += self.decoder.decode();
9550      // Synthesize the end of the current cue or region.
9551      if (self.cue || self.state === "HEADER") {
9552        self.buffer += "\n\n";
9553        self.parse();
9554      }
9555      // If we've flushed, parsed, and we're still on the INITIAL state then
9556      // that means we don't have enough of the stream to parse the first
9557      // line.
9558      if (self.state === "INITIAL") {
9559        throw new ParsingError(ParsingError.Errors.BadSignature);
9560      }
9561    } catch(e) {
9562      self.reportOrThrowError(e);
9563    }
9564    self.onflush && self.onflush();
9565    return this;
9566  }
9567};
9568
9569var vtt$1 = WebVTT$1;
9570
9571/**
9572 * Copyright 2013 vtt.js Contributors
9573 *
9574 * Licensed under the Apache License, Version 2.0 (the "License");
9575 * you may not use this file except in compliance with the License.
9576 * You may obtain a copy of the License at
9577 *
9578 *   http://www.apache.org/licenses/LICENSE-2.0
9579 *
9580 * Unless required by applicable law or agreed to in writing, software
9581 * distributed under the License is distributed on an "AS IS" BASIS,
9582 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
9583 * See the License for the specific language governing permissions and
9584 * limitations under the License.
9585 */
9586
9587var autoKeyword = "auto";
9588var directionSetting = {
9589  "": true,
9590  "lr": true,
9591  "rl": true
9592};
9593var alignSetting = {
9594  "start": true,
9595  "middle": true,
9596  "end": true,
9597  "left": true,
9598  "right": true
9599};
9600
9601function findDirectionSetting(value) {
9602  if (typeof value !== "string") {
9603    return false;
9604  }
9605  var dir = directionSetting[value.toLowerCase()];
9606  return dir ? value.toLowerCase() : false;
9607}
9608
9609function findAlignSetting(value) {
9610  if (typeof value !== "string") {
9611    return false;
9612  }
9613  var align = alignSetting[value.toLowerCase()];
9614  return align ? value.toLowerCase() : false;
9615}
9616
9617function extend$1(obj) {
9618  var i = 1;
9619  for (; i < arguments.length; i++) {
9620    var cobj = arguments[i];
9621    for (var p in cobj) {
9622      obj[p] = cobj[p];
9623    }
9624  }
9625
9626  return obj;
9627}
9628
9629function VTTCue(startTime, endTime, text) {
9630  var cue = this;
9631  var isIE8 = (/MSIE\s8\.0/).test(navigator.userAgent);
9632  var baseObj = {};
9633
9634  if (isIE8) {
9635    cue = document.createElement('custom');
9636  } else {
9637    baseObj.enumerable = true;
9638  }
9639
9640  /**
9641   * Shim implementation specific properties. These properties are not in
9642   * the spec.
9643   */
9644
9645  // Lets us know when the VTTCue's data has changed in such a way that we need
9646  // to recompute its display state. This lets us compute its display state
9647  // lazily.
9648  cue.hasBeenReset = false;
9649
9650  /**
9651   * VTTCue and TextTrackCue properties
9652   * http://dev.w3.org/html5/webvtt/#vttcue-interface
9653   */
9654
9655  var _id = "";
9656  var _pauseOnExit = false;
9657  var _startTime = startTime;
9658  var _endTime = endTime;
9659  var _text = text;
9660  var _region = null;
9661  var _vertical = "";
9662  var _snapToLines = true;
9663  var _line = "auto";
9664  var _lineAlign = "start";
9665  var _position = 50;
9666  var _positionAlign = "middle";
9667  var _size = 50;
9668  var _align = "middle";
9669
9670  Object.defineProperty(cue,
9671    "id", extend$1({}, baseObj, {
9672      get: function() {
9673        return _id;
9674      },
9675      set: function(value) {
9676        _id = "" + value;
9677      }
9678    }));
9679
9680  Object.defineProperty(cue,
9681    "pauseOnExit", extend$1({}, baseObj, {
9682      get: function() {
9683        return _pauseOnExit;
9684      },
9685      set: function(value) {
9686        _pauseOnExit = !!value;
9687      }
9688    }));
9689
9690  Object.defineProperty(cue,
9691    "startTime", extend$1({}, baseObj, {
9692      get: function() {
9693        return _startTime;
9694      },
9695      set: function(value) {
9696        if (typeof value !== "number") {
9697          throw new TypeError("Start time must be set to a number.");
9698        }
9699        _startTime = value;
9700        this.hasBeenReset = true;
9701      }
9702    }));
9703
9704  Object.defineProperty(cue,
9705    "endTime", extend$1({}, baseObj, {
9706      get: function() {
9707        return _endTime;
9708      },
9709      set: function(value) {
9710        if (typeof value !== "number") {
9711          throw new TypeError("End time must be set to a number.");
9712        }
9713        _endTime = value;
9714        this.hasBeenReset = true;
9715      }
9716    }));
9717
9718  Object.defineProperty(cue,
9719    "text", extend$1({}, baseObj, {
9720      get: function() {
9721        return _text;
9722      },
9723      set: function(value) {
9724        _text = "" + value;
9725        this.hasBeenReset = true;
9726      }
9727    }));
9728
9729  Object.defineProperty(cue,
9730    "region", extend$1({}, baseObj, {
9731      get: function() {
9732        return _region;
9733      },
9734      set: function(value) {
9735        _region = value;
9736        this.hasBeenReset = true;
9737      }
9738    }));
9739
9740  Object.defineProperty(cue,
9741    "vertical", extend$1({}, baseObj, {
9742      get: function() {
9743        return _vertical;
9744      },
9745      set: function(value) {
9746        var setting = findDirectionSetting(value);
9747        // Have to check for false because the setting an be an empty string.
9748        if (setting === false) {
9749          throw new SyntaxError("An invalid or illegal string was specified.");
9750        }
9751        _vertical = setting;
9752        this.hasBeenReset = true;
9753      }
9754    }));
9755
9756  Object.defineProperty(cue,
9757    "snapToLines", extend$1({}, baseObj, {
9758      get: function() {
9759        return _snapToLines;
9760      },
9761      set: function(value) {
9762        _snapToLines = !!value;
9763        this.hasBeenReset = true;
9764      }
9765    }));
9766
9767  Object.defineProperty(cue,
9768    "line", extend$1({}, baseObj, {
9769      get: function() {
9770        return _line;
9771      },
9772      set: function(value) {
vendor: 4,245 bytes, lines 9773-9938
9773        if (typeof value !== "number" && value !== autoKeyword) {
9774          throw new SyntaxError("An invalid number or illegal string was specified.");
9775        }
9776        _line = value;
9777        this.hasBeenReset = true;
9778      }
9779    }));
9780
9781  Object.defineProperty(cue,
9782    "lineAlign", extend$1({}, baseObj, {
9783      get: function() {
9784        return _lineAlign;
9785      },
9786      set: function(value) {
9787        var setting = findAlignSetting(value);
9788        if (!setting) {
9789          throw new SyntaxError("An invalid or illegal string was specified.");
9790        }
9791        _lineAlign = setting;
9792        this.hasBeenReset = true;
9793      }
9794    }));
9795
9796  Object.defineProperty(cue,
9797    "position", extend$1({}, baseObj, {
9798      get: function() {
9799        return _position;
9800      },
9801      set: function(value) {
9802        if (value < 0 || value > 100) {
9803          throw new Error("Position must be between 0 and 100.");
9804        }
9805        _position = value;
9806        this.hasBeenReset = true;
9807      }
9808    }));
9809
9810  Object.defineProperty(cue,
9811    "positionAlign", extend$1({}, baseObj, {
9812      get: function() {
9813        return _positionAlign;
9814      },
9815      set: function(value) {
9816        var setting = findAlignSetting(value);
9817        if (!setting) {
9818          throw new SyntaxError("An invalid or illegal string was specified.");
9819        }
9820        _positionAlign = setting;
9821        this.hasBeenReset = true;
9822      }
9823    }));
9824
9825  Object.defineProperty(cue,
9826    "size", extend$1({}, baseObj, {
9827      get: function() {
9828        return _size;
9829      },
9830      set: function(value) {
9831        if (value < 0 || value > 100) {
9832          throw new Error("Size must be between 0 and 100.");
9833        }
9834        _size = value;
9835        this.hasBeenReset = true;
9836      }
9837    }));
9838
9839  Object.defineProperty(cue,
9840    "align", extend$1({}, baseObj, {
9841      get: function() {
9842        return _align;
9843      },
9844      set: function(value) {
9845        var setting = findAlignSetting(value);
9846        if (!setting) {
9847          throw new SyntaxError("An invalid or illegal string was specified.");
9848        }
9849        _align = setting;
9850        this.hasBeenReset = true;
9851      }
9852    }));
9853
9854  /**
9855   * Other <track> spec defined properties
9856   */
9857
9858  // http://www.whatwg.org/specs/web-apps/current-work/multipage/the-video-element.html#text-track-cue-display-state
9859  cue.displayState = undefined;
9860
9861  if (isIE8) {
9862    return cue;
9863  }
9864}
9865
9866/**
9867 * VTTCue methods
9868 */
9869
9870VTTCue.prototype.getCueAsHTML = function() {
9871  // Assume WebVTT.convertCueToDOMTree is on the global.
9872  return WebVTT.convertCueToDOMTree(window, this.text);
9873};
9874
9875var vttcue = VTTCue;
9876
9877/**
9878 * Copyright 2013 vtt.js Contributors
9879 *
9880 * Licensed under the Apache License, Version 2.0 (the "License");
9881 * you may not use this file except in compliance with the License.
9882 * You may obtain a copy of the License at
9883 *
9884 *   http://www.apache.org/licenses/LICENSE-2.0
9885 *
9886 * Unless required by applicable law or agreed to in writing, software
9887 * distributed under the License is distributed on an "AS IS" BASIS,
9888 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
9889 * See the License for the specific language governing permissions and
9890 * limitations under the License.
9891 */
9892
9893var scrollSetting = {
9894  "": true,
9895  "up": true
9896};
9897
9898function findScrollSetting(value) {
9899  if (typeof value !== "string") {
9900    return false;
9901  }
9902  var scroll = scrollSetting[value.toLowerCase()];
9903  return scroll ? value.toLowerCase() : false;
9904}
9905
9906function isValidPercentValue(value) {
9907  return typeof value === "number" && (value >= 0 && value <= 100);
9908}
9909
9910// VTTRegion shim http://dev.w3.org/html5/webvtt/#vttregion-interface
9911function VTTRegion() {
9912  var _width = 100;
9913  var _lines = 3;
9914  var _regionAnchorX = 0;
9915  var _regionAnchorY = 100;
9916  var _viewportAnchorX = 0;
9917  var _viewportAnchorY = 100;
9918  var _scroll = "";
9919
9920  Object.defineProperties(this, {
9921    "width": {
9922      enumerable: true,
9923      get: function() {
9924        return _width;
9925      },
9926      set: function(value) {
9927        if (!isValidPercentValue(value)) {
9928          throw new Error("Width must be between 0 and 100.");
9929        }
9930        _width = value;
9931      }
9932    },
9933    "lines": {
9934      enumerable: true,
9935      get: function() {
9936        return _lines;
9937      },
9938      set: function(value) {
vendor: 3,307 bytes, lines 9939-10063
9939        if (typeof value !== "number") {
9940          throw new TypeError("Lines must be set to a number.");
9941        }
9942        _lines = value;
9943      }
9944    },
9945    "regionAnchorY": {
9946      enumerable: true,
9947      get: function() {
9948        return _regionAnchorY;
9949      },
9950      set: function(value) {
9951        if (!isValidPercentValue(value)) {
9952          throw new Error("RegionAnchorX must be between 0 and 100.");
9953        }
9954        _regionAnchorY = value;
9955      }
9956    },
9957    "regionAnchorX": {
9958      enumerable: true,
9959      get: function() {
9960        return _regionAnchorX;
9961      },
9962      set: function(value) {
9963        if(!isValidPercentValue(value)) {
9964          throw new Error("RegionAnchorY must be between 0 and 100.");
9965        }
9966        _regionAnchorX = value;
9967      }
9968    },
9969    "viewportAnchorY": {
9970      enumerable: true,
9971      get: function() {
9972        return _viewportAnchorY;
9973      },
9974      set: function(value) {
9975        if (!isValidPercentValue(value)) {
9976          throw new Error("ViewportAnchorY must be between 0 and 100.");
9977        }
9978        _viewportAnchorY = value;
9979      }
9980    },
9981    "viewportAnchorX": {
9982      enumerable: true,
9983      get: function() {
9984        return _viewportAnchorX;
9985      },
9986      set: function(value) {
9987        if (!isValidPercentValue(value)) {
9988          throw new Error("ViewportAnchorX must be between 0 and 100.");
9989        }
9990        _viewportAnchorX = value;
9991      }
9992    },
9993    "scroll": {
9994      enumerable: true,
9995      get: function() {
9996        return _scroll;
9997      },
9998      set: function(value) {
9999        var setting = findScrollSetting(value);
10000        // Have to check for false as an empty string is a legal value.
10001        if (setting === false) {
10002          throw new SyntaxError("An invalid or illegal string was specified.");
10003        }
10004        _scroll = setting;
10005      }
10006    }
10007  });
10008}
10009
10010var vttregion = VTTRegion;
10011
10012var browserIndex = createCommonjsModule(function (module) {
10013/**
10014 * Copyright 2013 vtt.js Contributors
10015 *
10016 * Licensed under the Apache License, Version 2.0 (the "License");
10017 * you may not use this file except in compliance with the License.
10018 * You may obtain a copy of the License at
10019 *
10020 *   http://www.apache.org/licenses/LICENSE-2.0
10021 *
10022 * Unless required by applicable law or agreed to in writing, software
10023 * distributed under the License is distributed on an "AS IS" BASIS,
10024 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
10025 * See the License for the specific language governing permissions and
10026 * limitations under the License.
10027 */
10028
10029// Default exports for Node. Export the extended versions of VTTCue and
10030// VTTRegion in Node since we likely want the capability to convert back and
10031// forth between JSON. If we don't then it's not that big of a deal since we're
10032// off browser.
10033
10034
10035
10036var vttjs = module.exports = {
10037  WebVTT: vtt$1,
10038  VTTCue: vttcue,
10039  VTTRegion: vttregion
10040};
10041
10042window_1.vttjs = vttjs;
10043window_1.WebVTT = vttjs.WebVTT;
10044
10045var cueShim = vttjs.VTTCue;
10046var regionShim = vttjs.VTTRegion;
10047var nativeVTTCue = window_1.VTTCue;
10048var nativeVTTRegion = window_1.VTTRegion;
10049
10050vttjs.shim = function() {
10051  window_1.VTTCue = cueShim;
10052  window_1.VTTRegion = regionShim;
10053};
10054
10055vttjs.restore = function() {
10056  window_1.VTTCue = nativeVTTCue;
10057  window_1.VTTRegion = nativeVTTRegion;
10058};
10059
10060if (!window_1.VTTCue) {
10061  vttjs.shim();
10062}
10063}
vendor: 13,208 bytes, lines 10063-10554
10063);
10064
10065/**
10066 * @file tech.js
10067 */
10068
10069/**
10070 * An Object containing a structure like: `{src: 'url', type: 'mimetype'}` or string
10071 * that just contains the src url alone.
10072 * * `var SourceObject = {src: 'http://ex.com/video.mp4', type: 'video/mp4'};`
10073   * `var SourceString = 'http://example.com/some-video.mp4';`
10074 *
10075 * @typedef {Object|string} Tech~SourceObject
10076 *
10077 * @property {string} src
10078 *           The url to the source
10079 *
10080 * @property {string} type
10081 *           The mime type of the source
10082 */
10083
10084/**
10085 * A function used by {@link Tech} to create a new {@link TextTrack}.
10086 *
10087 * @private
10088 *
10089 * @param {Tech} self
10090 *        An instance of the Tech class.
10091 *
10092 * @param {string} kind
10093 *        `TextTrack` kind (subtitles, captions, descriptions, chapters, or metadata)
10094 *
10095 * @param {string} [label]
10096 *        Label to identify the text track
10097 *
10098 * @param {string} [language]
10099 *        Two letter language abbreviation
10100 *
10101 * @param {Object} [options={}]
10102 *        An object with additional text track options
10103 *
10104 * @return {TextTrack}
10105 *          The text track that was created.
10106 */
10107function createTrackHelper(self, kind, label, language) {
10108  var options = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : {};
10109
10110  var tracks = self.textTracks();
10111
10112  options.kind = kind;
10113
10114  if (label) {
10115    options.label = label;
10116  }
10117  if (language) {
10118    options.language = language;
10119  }
10120  options.tech = self;
10121
10122  var track = new ALL.text.TrackClass(options);
10123
10124  tracks.addTrack(track);
10125
10126  return track;
10127}
10128
10129/**
10130 * This is the base class for media playback technology controllers, such as
10131 * {@link Flash} and {@link HTML5}
10132 *
10133 * @extends Component
10134 */
10135
10136var Tech = function (_Component) {
10137  inherits(Tech, _Component);
10138
10139  /**
10140   * Create an instance of this Tech.
10141   *
10142   * @param {Object} [options]
10143   *        The key/value store of player options.
10144   *
10145   * @param {Component~ReadyCallback} ready
10146   *        Callback function to call when the `HTML5` Tech is ready.
10147   */
10148  function Tech() {
10149    var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
10150    var ready = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : function () {};
10151    classCallCheck(this, Tech);
10152
10153    // we don't want the tech to report user activity automatically.
10154    // This is done manually in addControlsListeners
10155    options.reportTouchActivity = false;
10156
10157    // keep track of whether the current source has played at all to
10158    // implement a very limited played()
10159    var _this = possibleConstructorReturn(this, _Component.call(this, null, options, ready));
10160
10161    _this.hasStarted_ = false;
10162    _this.on('playing', function () {
10163      this.hasStarted_ = true;
10164    });
10165    _this.on('loadstart', function () {
10166      this.hasStarted_ = false;
10167    });
10168
10169    ALL.names.forEach(function (name) {
10170      var props = ALL[name];
10171
10172      if (options && options[props.getterName]) {
10173        _this[props.privateName] = options[props.getterName];
10174      }
10175    });
10176
10177    // Manually track progress in cases where the browser/flash player doesn't report it.
10178    if (!_this.featuresProgressEvents) {
10179      _this.manualProgressOn();
10180    }
10181
10182    // Manually track timeupdates in cases where the browser/flash player doesn't report it.
10183    if (!_this.featuresTimeupdateEvents) {
10184      _this.manualTimeUpdatesOn();
10185    }
10186
10187    ['Text', 'Audio', 'Video'].forEach(function (track) {
10188      if (options['native' + track + 'Tracks'] === false) {
10189        _this['featuresNative' + track + 'Tracks'] = false;
10190      }
10191    });
10192
10193    if (options.nativeCaptions === false || options.nativeTextTracks === false) {
10194      _this.featuresNativeTextTracks = false;
10195    } else if (options.nativeCaptions === true || options.nativeTextTracks === true) {
10196      _this.featuresNativeTextTracks = true;
10197    }
10198
10199    if (!_this.featuresNativeTextTracks) {
10200      _this.emulateTextTracks();
10201    }
10202
10203    _this.autoRemoteTextTracks_ = new ALL.text.ListClass();
10204
10205    _this.initTrackListeners();
10206
10207    // Turn on component tap events only if not using native controls
10208    if (!options.nativeControlsForTouch) {
10209      _this.emitTapEvents();
10210    }
10211
10212    if (_this.constructor) {
10213      _this.name_ = _this.constructor.name || 'Unknown Tech';
10214    }
10215    return _this;
10216  }
10217
10218  /**
10219   * A special function to trigger source set in a way that will allow player
10220   * to re-trigger if the player or tech are not ready yet.
10221   *
10222   * @fires Tech#sourceset
10223   * @param {string} src The source string at the time of the source changing.
10224   */
10225
10226
10227  Tech.prototype.triggerSourceset = function triggerSourceset(src) {
10228    var _this2 = this;
10229
10230    if (!this.isReady_) {
10231      // on initial ready we have to trigger source set
10232      // 1ms after ready so that player can watch for it.
10233      this.one('ready', function () {
10234        return _this2.setTimeout(function () {
10235          return _this2.triggerSourceset(src);
10236        }, 1);
10237      });
10238    }
10239
10240    /**
10241     * Fired when the source is set on the tech causing the media element
10242     * to reload.
10243     *
10244     * @see {@link Player#event:sourceset}
10245     * @event Tech#sourceset
10246     * @type {EventTarget~Event}
10247     */
10248    this.trigger({
10249      src: src,
10250      type: 'sourceset'
10251    });
10252  };
10253
10254  /* Fallbacks for unsupported event types
10255  ================================================================================ */
10256
10257  /**
10258   * Polyfill the `progress` event for browsers that don't support it natively.
10259   *
10260   * @see {@link Tech#trackProgress}
10261   */
10262
10263
10264  Tech.prototype.manualProgressOn = function manualProgressOn() {
10265    this.on('durationchange', this.onDurationChange);
10266
10267    this.manualProgress = true;
10268
10269    // Trigger progress watching when a source begins loading
10270    this.one('ready', this.trackProgress);
10271  };
10272
10273  /**
10274   * Turn off the polyfill for `progress` events that was created in
10275   * {@link Tech#manualProgressOn}
10276   */
10277
10278
10279  Tech.prototype.manualProgressOff = function manualProgressOff() {
10280    this.manualProgress = false;
10281    this.stopTrackingProgress();
10282
10283    this.off('durationchange', this.onDurationChange);
10284  };
10285
10286  /**
10287   * This is used to trigger a `progress` event when the buffered percent changes. It
10288   * sets an interval function that will be called every 500 milliseconds to check if the
10289   * buffer end percent has changed.
10290   *
10291   * > This function is called by {@link Tech#manualProgressOn}
10292   *
10293   * @param {EventTarget~Event} event
10294   *        The `ready` event that caused this to run.
10295   *
10296   * @listens Tech#ready
10297   * @fires Tech#progress
10298   */
10299
10300
10301  Tech.prototype.trackProgress = function trackProgress(event) {
10302    this.stopTrackingProgress();
10303    this.progressInterval = this.setInterval(bind(this, function () {
10304      // Don't trigger unless buffered amount is greater than last time
10305
10306      var numBufferedPercent = this.bufferedPercent();
10307
10308      if (this.bufferedPercent_ !== numBufferedPercent) {
10309        /**
10310         * See {@link Player#progress}
10311         *
10312         * @event Tech#progress
10313         * @type {EventTarget~Event}
10314         */
10315        this.trigger('progress');
10316      }
10317
10318      this.bufferedPercent_ = numBufferedPercent;
10319
10320      if (numBufferedPercent === 1) {
10321        this.stopTrackingProgress();
10322      }
10323    }), 500);
10324  };
10325
10326  /**
10327   * Update our internal duration on a `durationchange` event by calling
10328   * {@link Tech#duration}.
10329   *
10330   * @param {EventTarget~Event} event
10331   *        The `durationchange` event that caused this to run.
10332   *
10333   * @listens Tech#durationchange
10334   */
10335
10336
10337  Tech.prototype.onDurationChange = function onDurationChange(event) {
10338    this.duration_ = this.duration();
10339  };
10340
10341  /**
10342   * Get and create a `TimeRange` object for buffering.
10343   *
10344   * @return {TimeRange}
10345   *         The time range object that was created.
10346   */
10347
10348
10349  Tech.prototype.buffered = function buffered() {
10350    return createTimeRanges(0, 0);
10351  };
10352
10353  /**
10354   * Get the percentage of the current video that is currently buffered.
10355   *
10356   * @return {number}
10357   *         A number from 0 to 1 that represents the decimal percentage of the
10358   *         video that is buffered.
10359   *
10360   */
10361
10362
10363  Tech.prototype.bufferedPercent = function bufferedPercent$$1() {
10364    return bufferedPercent(this.buffered(), this.duration_);
10365  };
10366
10367  /**
10368   * Turn off the polyfill for `progress` events that was created in
10369   * {@link Tech#manualProgressOn}
10370   * Stop manually tracking progress events by clearing the interval that was set in
10371   * {@link Tech#trackProgress}.
10372   */
10373
10374
10375  Tech.prototype.stopTrackingProgress = function stopTrackingProgress() {
10376    this.clearInterval(this.progressInterval);
10377  };
10378
10379  /**
10380   * Polyfill the `timeupdate` event for browsers that don't support it.
10381   *
10382   * @see {@link Tech#trackCurrentTime}
10383   */
10384
10385
10386  Tech.prototype.manualTimeUpdatesOn = function manualTimeUpdatesOn() {
10387    this.manualTimeUpdates = true;
10388
10389    this.on('play', this.trackCurrentTime);
10390    this.on('pause', this.stopTrackingCurrentTime);
10391  };
10392
10393  /**
10394   * Turn off the polyfill for `timeupdate` events that was created in
10395   * {@link Tech#manualTimeUpdatesOn}
10396   */
10397
10398
10399  Tech.prototype.manualTimeUpdatesOff = function manualTimeUpdatesOff() {
10400    this.manualTimeUpdates = false;
10401    this.stopTrackingCurrentTime();
10402    this.off('play', this.trackCurrentTime);
10403    this.off('pause', this.stopTrackingCurrentTime);
10404  };
10405
10406  /**
10407   * Sets up an interval function to track current time and trigger `timeupdate` every
10408   * 250 milliseconds.
10409   *
10410   * @listens Tech#play
10411   * @triggers Tech#timeupdate
10412   */
10413
10414
10415  Tech.prototype.trackCurrentTime = function trackCurrentTime() {
10416    if (this.currentTimeInterval) {
10417      this.stopTrackingCurrentTime();
10418    }
10419    this.currentTimeInterval = this.setInterval(function () {
10420      /**
10421       * Triggered at an interval of 250ms to indicated that time is passing in the video.
10422       *
10423       * @event Tech#timeupdate
10424       * @type {EventTarget~Event}
10425       */
10426      this.trigger({ type: 'timeupdate', target: this, manuallyTriggered: true });
10427
10428      // 42 = 24 fps // 250 is what Webkit uses // FF uses 15
10429    }, 250);
10430  };
10431
10432  /**
10433   * Stop the interval function created in {@link Tech#trackCurrentTime} so that the
10434   * `timeupdate` event is no longer triggered.
10435   *
10436   * @listens {Tech#pause}
10437   */
10438
10439
10440  Tech.prototype.stopTrackingCurrentTime = function stopTrackingCurrentTime() {
10441    this.clearInterval(this.currentTimeInterval);
10442
10443    // #1002 - if the video ends right before the next timeupdate would happen,
10444    // the progress bar won't make it all the way to the end
10445    this.trigger({ type: 'timeupdate', target: this, manuallyTriggered: true });
10446  };
10447
10448  /**
10449   * Turn off all event polyfills, clear the `Tech`s {@link AudioTrackList},
10450   * {@link VideoTrackList}, and {@link TextTrackList}, and dispose of this Tech.
10451   *
10452   * @fires Component#dispose
10453   */
10454
10455
10456  Tech.prototype.dispose = function dispose() {
10457
10458    // clear out all tracks because we can't reuse them between techs
10459    this.clearTracks(NORMAL.names);
10460
10461    // Turn off any manual progress or timeupdate tracking
10462    if (this.manualProgress) {
10463      this.manualProgressOff();
10464    }
10465
10466    if (this.manualTimeUpdates) {
10467      this.manualTimeUpdatesOff();
10468    }
10469
10470    _Component.prototype.dispose.call(this);
10471  };
10472
10473  /**
10474   * Clear out a single `TrackList` or an array of `TrackLists` given their names.
10475   *
10476   * > Note: Techs without source handlers should call this between sources for `video`
10477   *         & `audio` tracks. You don't want to use them between tracks!
10478   *
10479   * @param {string[]|string} types
10480   *        TrackList names to clear, valid names are `video`, `audio`, and
10481   *        `text`.
10482   */
10483
10484
10485  Tech.prototype.clearTracks = function clearTracks(types) {
10486    var _this3 = this;
10487
10488    types = [].concat(types);
10489    // clear out all tracks because we can't reuse them between techs
10490    types.forEach(function (type) {
10491      var list = _this3[type + 'Tracks']() || [];
10492      var i = list.length;
10493
10494      while (i--) {
10495        var track = list[i];
10496
10497        if (type === 'text') {
10498          _this3.removeRemoteTextTrack(track);
10499        }
10500        list.removeTrack(track);
10501      }
10502    });
10503  };
10504
10505  /**
10506   * Remove any TextTracks added via addRemoteTextTrack that are
10507   * flagged for automatic garbage collection
10508   */
10509
10510
10511  Tech.prototype.cleanupAutoTextTracks = function cleanupAutoTextTracks() {
10512    var list = this.autoRemoteTextTracks_ || [];
10513    var i = list.length;
10514
10515    while (i--) {
10516      var track = list[i];
10517
10518      this.removeRemoteTextTrack(track);
10519    }
10520  };
10521
10522  /**
10523   * Reset the tech, which will removes all sources and reset the internal readyState.
10524   *
10525   * @abstract
10526   */
10527
10528
10529  Tech.prototype.reset = function reset() {};
10530
10531  /**
10532   * Get or set an error on the Tech.
10533   *
10534   * @param {MediaError} [err]
10535   *        Error to set on the Tech
10536   *
10537   * @return {MediaError|null}
10538   *         The current error object on the tech, or null if there isn't one.
10539   */
10540
10541
10542  Tech.prototype.error = function error(err) {
10543    if (err !== undefined) {
10544      this.error_ = new MediaError(err);
10545      this.trigger('error');
10546    }
10547    return this.error_;
10548  };
10549
10550  /**
10551   * Returns the `TimeRange`s that have been played through for the current source.
10552   *
10553   * > NOTE: This implementation is incomplete. It does not track the played `TimeRange`.
10554   *         It only checks w
10554ether the source has played at all or not.
10555   *
10556   * @return {TimeRange}
10557   *         - A single time range if this video has played
10558   *         - An empty set of ranges if not.
10559   */
10560
10561
10562  Tech.prototype.played = function played() {
10563    if (this.hasStarted_) {
10564      return createTimeRanges(0, 0);
10565    }
10566    return createTimeRanges();
10567  };
10568
10569  /**
10570   * Causes a manual time update to occur if {@link Tech#manualTimeUpdatesOn} was
10571   * previously called.
10572   *
10573   * @fires Tech#timeupdate
10574   */
10575
10576
10577  Tech.prototype.setCurrentTime = function setCurrentTime() {
10578    // improve the accuracy of manual timeupdates
10579    if (this.manualTimeUpdates) {
10580      /**
10581       * A manual `timeupdate` event.
10582       *
10583       * @event Tech#timeupdate
10584       * @type {EventTarget~Event}
10585       */
10586      this.trigger({ type: 'timeupdate', target: this, manuallyTriggered: true });
10587    }
10588  };
10589
10590  /**
10591   * Turn on listeners for {@link VideoTrackList}, {@link {AudioTrackList}, and
10592   * {@link TextTrackList} events.
10593   *
10594   * This adds {@link EventTarget~EventListeners} for `addtrack`, and  `removetrack`.
10595   *
10596   * @fires Tech#audiotrackchange
10597   * @fires Tech#videotrackchange
10598   * @fires Tech#texttrackchange
10599   */
10600
10601
10602  Tech.prototype.initTrackListeners = function initTrackListeners() {
10603    var _this4 = this;
10604
10605    /**
10606     * Triggered when tracks are added or removed on the Tech {@link AudioTrackList}
10607     *
10608     * @event Tech#audiotrackchange
10609     * @type {EventTarget~Event}
10610     */
10611
10612    /**
10613     * Triggered when tracks are added or removed on the Tech {@link VideoTrackList}
10614     *
10615     * @event Tech#videotrackchange
10616     * @type {EventTarget~Event}
10617     */
10618
10619    /**
10620     * Triggered when tracks are added or removed on the Tech {@link TextTrackList}
10621     *
10622     * @event Tech#texttrackchange
10623     * @type {EventTarget~Event}
10624     */
vendor: 213,864 bytes, lines 10625-18753
10625    NORMAL.names.forEach(function (name) {
10626      var props = NORMAL[name];
10627      var trackListChanges = function trackListChanges() {
10628        _this4.trigger(name + 'trackchange');
10629      };
10630
10631      var tracks = _this4[props.getterName]();
10632
10633      tracks.addEventListener('removetrack', trackListChanges);
10634      tracks.addEventListener('addtrack', trackListChanges);
10635
10636      _this4.on('dispose', function () {
10637        tracks.removeEventListener('removetrack', trackListChanges);
10638        tracks.removeEventListener('addtrack', trackListChanges);
10639      });
10640    });
10641  };
10642
10643  /**
10644   * Emulate TextTracks using vtt.js if necessary
10645   *
10646   * @fires Tech#vttjsloaded
10647   * @fires Tech#vttjserror
10648   */
10649
10650
10651  Tech.prototype.addWebVttScript_ = function addWebVttScript_() {
10652    var _this5 = this;
10653
10654    if (window_1.WebVTT) {
10655      return;
10656    }
10657
10658    // Initially, Tech.el_ is a child of a dummy-div wait until the Component system
10659    // signals that the Tech is ready at which point Tech.el_ is part of the DOM
10660    // before inserting the WebVTT script
10661    if (document_1.body.contains(this.el())) {
10662
10663      // load via require if available and vtt.js script location was not passed in
10664      // as an option. novtt builds will turn the above require call into an empty object
10665      // which will cause this if check to always fail.
10666      if (!this.options_['vtt.js'] && isPlain(browserIndex) && Object.keys(browserIndex).length > 0) {
10667        this.trigger('vttjsloaded');
10668        return;
10669      }
10670
10671      // load vtt.js via the script location option or the cdn of no location was
10672      // passed in
10673      var script = document_1.createElement('script');
10674
10675      script.src = this.options_['vtt.js'] || 'https://vjs.zencdn.net/vttjs/0.12.4/vtt.min.js';
10676      script.onload = function () {
10677        /**
10678         * Fired when vtt.js is loaded.
10679         *
10680         * @event Tech#vttjsloaded
10681         * @type {EventTarget~Event}
10682         */
10683        _this5.trigger('vttjsloaded');
10684      };
10685      script.onerror = function () {
10686        /**
10687         * Fired when vtt.js was not loaded due to an error
10688         *
10689         * @event Tech#vttjsloaded
10690         * @type {EventTarget~Event}
10691         */
10692        _this5.trigger('vttjserror');
10693      };
10694      this.on('dispose', function () {
10695        script.onload = null;
10696        script.onerror = null;
10697      });
10698      // but have not loaded yet and we set it to true before the inject so that
10699      // we don't overwrite the injected window.WebVTT if it loads right away
10700      window_1.WebVTT = true;
10701      this.el().parentNode.appendChild(script);
10702    } else {
10703      this.ready(this.addWebVttScript_);
10704    }
10705  };
10706
10707  /**
10708   * Emulate texttracks
10709   *
10710   */
10711
10712
10713  Tech.prototype.emulateTextTracks = function emulateTextTracks() {
10714    var _this6 = this;
10715
10716    var tracks = this.textTracks();
10717    var remoteTracks = this.remoteTextTracks();
10718    var handleAddTrack = function handleAddTrack(e) {
10719      return tracks.addTrack(e.track);
10720    };
10721    var handleRemoveTrack = function handleRemoveTrack(e) {
10722      return tracks.removeTrack(e.track);
10723    };
10724
10725    remoteTracks.on('addtrack', handleAddTrack);
10726    remoteTracks.on('removetrack', handleRemoveTrack);
10727
10728    this.addWebVttScript_();
10729
10730    var updateDisplay = function updateDisplay() {
10731      return _this6.trigger('texttrackchange');
10732    };
10733
10734    var textTracksChanges = function textTracksChanges() {
10735      updateDisplay();
10736
10737      for (var i = 0; i < tracks.length; i++) {
10738        var track = tracks[i];
10739
10740        track.removeEventListener('cuechange', updateDisplay);
10741        if (track.mode === 'showing') {
10742          track.addEventListener('cuechange', updateDisplay);
10743        }
10744      }
10745    };
10746
10747    textTracksChanges();
10748    tracks.addEventListener('change', textTracksChanges);
10749    tracks.addEventListener('addtrack', textTracksChanges);
10750    tracks.addEventListener('removetrack', textTracksChanges);
10751
10752    this.on('dispose', function () {
10753      remoteTracks.off('addtrack', handleAddTrack);
10754      remoteTracks.off('removetrack', handleRemoveTrack);
10755      tracks.removeEventListener('change', textTracksChanges);
10756      tracks.removeEventListener('addtrack', textTracksChanges);
10757      tracks.removeEventListener('removetrack', textTracksChanges);
10758
10759      for (var i = 0; i < tracks.length; i++) {
10760        var track = tracks[i];
10761
10762        track.removeEventListener('cuechange', updateDisplay);
10763      }
10764    });
10765  };
10766
10767  /**
10768   * Create and returns a remote {@link TextTrack} object.
10769   *
10770   * @param {string} kind
10771   *        `TextTrack` kind (subtitles, captions, descriptions, chapters, or metadata)
10772   *
10773   * @param {string} [label]
10774   *        Label to identify the text track
10775   *
10776   * @param {string} [language]
10777   *        Two letter language abbreviation
10778   *
10779   * @return {TextTrack}
10780   *         The TextTrack that gets created.
10781   */
10782
10783
10784  Tech.prototype.addTextTrack = function addTextTrack(kind, label, language) {
10785    if (!kind) {
10786      throw new Error('TextTrack kind is required but was not provided');
10787    }
10788
10789    return createTrackHelper(this, kind, label, language);
10790  };
10791
10792  /**
10793   * Create an emulated TextTrack for use by addRemoteTextTrack
10794   *
10795   * This is intended to be overridden by classes that inherit from
10796   * Tech in order to create native or custom TextTracks.
10797   *
10798   * @param {Object} options
10799   *        The object should contain the options to initialize the TextTrack with.
10800   *
10801   * @param {string} [options.kind]
10802   *        `TextTrack` kind (subtitles, captions, descriptions, chapters, or metadata).
10803   *
10804   * @param {string} [options.label].
10805   *        Label to identify the text track
10806   *
10807   * @param {string} [options.language]
10808   *        Two letter language abbreviation.
10809   *
10810   * @return {HTMLTrackElement}
10811   *         The track element that gets created.
10812   */
10813
10814
10815  Tech.prototype.createRemoteTextTrack = function createRemoteTextTrack(options) {
10816    var track = mergeOptions(options, {
10817      tech: this
10818    });
10819
10820    return new REMOTE.remoteTextEl.TrackClass(track);
10821  };
10822
10823  /**
10824   * Creates a remote text track object and returns an html track element.
10825   *
10826   * > Note: This can be an emulated {@link HTMLTrackElement} or a native one.
10827   *
10828   * @param {Object} options
10829   *        See {@link Tech#createRemoteTextTrack} for more detailed properties.
10830   *
10831   * @param {boolean} [manualCleanup=true]
10832   *        - When false: the TextTrack will be automatically removed from the video
10833   *          element whenever the source changes
10834   *        - When True: The TextTrack will have to be cleaned up manually
10835   *
10836   * @return {HTMLTrackElement}
10837   *         An Html Track Element.
10838   *
10839   * @deprecated The default functionality for this function will be equivalent
10840   *             to "manualCleanup=false" in the future. The manualCleanup parameter will
10841   *             also be removed.
10842   */
10843
10844
10845  Tech.prototype.addRemoteTextTrack = function addRemoteTextTrack() {
10846    var _this7 = this;
10847
10848    var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
10849    var manualCleanup = arguments[1];
10850
10851    var htmlTrackElement = this.createRemoteTextTrack(options);
10852
10853    if (manualCleanup !== true && manualCleanup !== false) {
10854      // deprecation warning
10855      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');
10856      manualCleanup = true;
10857    }
10858
10859    // store HTMLTrackElement and TextTrack to remote list
10860    this.remoteTextTrackEls().addTrackElement_(htmlTrackElement);
10861    this.remoteTextTracks().addTrack(htmlTrackElement.track);
10862
10863    if (manualCleanup !== true) {
10864      // create the TextTrackList if it doesn't exist
10865      this.ready(function () {
10866        return _this7.autoRemoteTextTracks_.addTrack(htmlTrackElement.track);
10867      });
10868    }
10869
10870    return htmlTrackElement;
10871  };
10872
10873  /**
10874   * Remove a remote text track from the remote `TextTrackList`.
10875   *
10876   * @param {TextTrack} track
10877   *        `TextTrack` to remove from the `TextTrackList`
10878   */
10879
10880
10881  Tech.prototype.removeRemoteTextTrack = function removeRemoteTextTrack(track) {
10882    var trackElement = this.remoteTextTrackEls().getTrackElementByTrack_(track);
10883
10884    // remove HTMLTrackElement and TextTrack from remote list
10885    this.remoteTextTrackEls().removeTrackElement_(trackElement);
10886    this.remoteTextTracks().removeTrack(track);
10887    this.autoRemoteTextTracks_.removeTrack(track);
10888  };
10889
10890  /**
10891   * Gets available media playback quality metrics as specified by the W3C's Media
10892   * Playback Quality API.
10893   *
10894   * @see [Spec]{@link https://wicg.github.io/media-playback-quality}
10895   *
10896   * @return {Object}
10897   *         An object with supported media playback quality metrics
10898   *
10899   * @abstract
10900   */
10901
10902
10903  Tech.prototype.getVideoPlaybackQuality = function getVideoPlaybackQuality() {
10904    return {};
10905  };
10906
10907  /**
10908   * A method to set a poster from a `Tech`.
10909   *
10910   * @abstract
10911   */
10912
10913
10914  Tech.prototype.setPoster = function setPoster() {};
10915
10916  /**
10917   * A method to check for the presence of the 'playsinine' <video> attribute.
10918   *
10919   * @abstract
10920   */
10921
10922
10923  Tech.prototype.playsinline = function playsinline() {};
10924
10925  /**
10926   * A method to set or unset the 'playsinine' <video> attribute.
10927   *
10928   * @abstract
10929   */
10930
10931
10932  Tech.prototype.setPlaysinline = function setPlaysinline() {};
10933
10934  /*
10935   * Check if the tech can support the given mime-type.
10936   *
10937   * The base tech does not support any type, but source handlers might
10938   * overwrite this.
10939   *
10940   * @param  {string} type
10941   *         The mimetype to check for support
10942   *
10943   * @return {string}
10944   *         'probably', 'maybe', or empty string
10945   *
10946   * @see [Spec]{@link https://developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement/canPlayType}
10947   *
10948   * @abstract
10949   */
10950
10951
10952  Tech.prototype.canPlayType = function canPlayType() {
10953    return '';
10954  };
10955
10956  /**
10957   * Check if the type is supported by this tech.
10958   *
10959   * The base tech does not support any type, but source handlers might
10960   * overwrite this.
10961   *
10962   * @param {string} type
10963   *        The media type to check
10964   * @return {string} Returns the native video element's response
10965   */
10966
10967
10968  Tech.canPlayType = function canPlayType() {
10969    return '';
10970  };
10971
10972  /**
10973   * Check if the tech can support the given source
10974   * @param {Object} srcObj
10975   *        The source object
10976   * @param {Object} options
10977   *        The options passed to the tech
10978   * @return {string} 'probably', 'maybe', or '' (empty string)
10979   */
10980
10981
10982  Tech.canPlaySource = function canPlaySource(srcObj, options) {
10983    return Tech.canPlayType(srcObj.type);
10984  };
10985
10986  /*
10987   * Return whether the argument is a Tech or not.
10988   * Can be passed either a Class like `Html5` or a instance like `player.tech_`
10989   *
10990   * @param {Object} component
10991   *        The item to check
10992   *
10993   * @return {boolean}
10994   *         Whether it is a tech or not
10995   *         - True if it is a tech
10996   *         - False if it is not
10997   */
10998
10999
11000  Tech.isTech = function isTech(component) {
11001    return component.prototype instanceof Tech || component instanceof Tech || component === Tech;
11002  };
11003
11004  /**
11005   * Registers a `Tech` into a shared list for videojs.
11006   *
11007   * @param {string} name
11008   *        Name of the `Tech` to register.
11009   *
11010   * @param {Object} tech
11011   *        The `Tech` class to register.
11012   */
11013
11014
11015  Tech.registerTech = function registerTech(name, tech) {
11016    if (!Tech.techs_) {
11017      Tech.techs_ = {};
11018    }
11019
11020    if (!Tech.isTech(tech)) {
11021      throw new Error('Tech ' + name + ' must be a Tech');
11022    }
11023
11024    if (!Tech.canPlayType) {
11025      throw new Error('Techs must have a static canPlayType method on them');
11026    }
11027    if (!Tech.canPlaySource) {
11028      throw new Error('Techs must have a static canPlaySource method on them');
11029    }
11030
11031    name = toTitleCase(name);
11032
11033    Tech.techs_[name] = tech;
11034    if (name !== 'Tech') {
11035      // camel case the techName for use in techOrder
11036      Tech.defaultTechOrder_.push(name);
11037    }
11038    return tech;
11039  };
11040
11041  /**
11042   * Get a `Tech` from the shared list by name.
11043   *
11044   * @param {string} name
11045   *        `camelCase` or `TitleCase` name of the Tech to get
11046   *
11047   * @return {Tech|undefined}
11048   *         The `Tech` or undefined if there was no tech with the name requsted.
11049   */
11050
11051
11052  Tech.getTech = function getTech(name) {
11053    if (!name) {
11054      return;
11055    }
11056
11057    name = toTitleCase(name);
11058
11059    if (Tech.techs_ && Tech.techs_[name]) {
11060      return Tech.techs_[name];
11061    }
11062
11063    if (window_1 && window_1.videojs && window_1.videojs[name]) {
11064      log$1.warn('The ' + name + ' tech was added to the videojs object when it should be registered using videojs.registerTech(name, tech)');
11065      return window_1.videojs[name];
11066    }
11067  };
11068
11069  return Tech;
11070}(Component);
11071
11072/**
11073 * Get the {@link VideoTrackList}
11074 *
11075 * @returns {VideoTrackList}
11076 * @method Tech.prototype.videoTracks
11077 */
11078
11079/**
11080 * Get the {@link AudioTrackList}
11081 *
11082 * @returns {AudioTrackList}
11083 * @method Tech.prototype.audioTracks
11084 */
11085
11086/**
11087 * Get the {@link TextTrackList}
11088 *
11089 * @returns {TextTrackList}
11090 * @method Tech.prototype.textTracks
11091 */
11092
11093/**
11094 * Get the remote element {@link TextTrackList}
11095 *
11096 * @returns {TextTrackList}
11097 * @method Tech.prototype.remoteTextTracks
11098 */
11099
11100/**
11101 * Get the remote element {@link HtmlTrackElementList}
11102 *
11103 * @returns {HtmlTrackElementList}
11104 * @method Tech.prototype.remoteTextTrackEls
11105 */
11106
11107ALL.names.forEach(function (name) {
11108  var props = ALL[name];
11109
11110  Tech.prototype[props.getterName] = function () {
11111    this[props.privateName] = this[props.privateName] || new props.ListClass();
11112    return this[props.privateName];
11113  };
11114});
11115
11116/**
11117 * List of associated text tracks
11118 *
11119 * @type {TextTrackList}
11120 * @private
11121 * @property Tech#textTracks_
11122 */
11123
11124/**
11125 * List of associated audio tracks.
11126 *
11127 * @type {AudioTrackList}
11128 * @private
11129 * @property Tech#audioTracks_
11130 */
11131
11132/**
11133 * List of associated video tracks.
11134 *
11135 * @type {VideoTrackList}
11136 * @private
11137 * @property Tech#videoTracks_
11138 */
11139
11140/**
11141 * Boolean indicating wether the `Tech` supports volume control.
11142 *
11143 * @type {boolean}
11144 * @default
11145 */
11146Tech.prototype.featuresVolumeControl = true;
11147
11148/**
11149 * Boolean indicating whether the `Tech` supports muting volume.
11150 *
11151 * @type {bolean}
11152 * @default
11153 */
11154Tech.prototype.featuresMuteControl = true;
11155
11156/**
11157 * Boolean indicating whether the `Tech` supports fullscreen resize control.
11158 * Resizing plugins using request fullscreen reloads the plugin
11159 *
11160 * @type {boolean}
11161 * @default
11162 */
11163Tech.prototype.featuresFullscreenResize = false;
11164
11165/**
11166 * Boolean indicating wether the `Tech` supports changing the speed at which the video
11167 * plays. Examples:
11168 *   - Set player to play 2x (twice) as fast
11169 *   - Set player to play 0.5x (half) as fast
11170 *
11171 * @type {boolean}
11172 * @default
11173 */
11174Tech.prototype.featuresPlaybackRate = false;
11175
11176/**
11177 * Boolean indicating wether the `Tech` supports the `progress` event. This is currently
11178 * not triggered by video-js-swf. This will be used to determine if
11179 * {@link Tech#manualProgressOn} should be called.
11180 *
11181 * @type {boolean}
11182 * @default
11183 */
11184Tech.prototype.featuresProgressEvents = false;
11185
11186/**
11187 * Boolean indicating wether the `Tech` supports the `sourceset` event.
11188 *
11189 * A tech should set this to `true` and then use {@link Tech#triggerSourceset}
11190 * to trigger a {@link Tech#event:sourceset} at the earliest time after getting
11191 * a new source.
11192 *
11193 * @type {boolean}
11194 * @default
11195 */
11196Tech.prototype.featuresSourceset = false;
11197
11198/**
11199 * Boolean indicating wether the `Tech` supports the `timeupdate` event. This is currently
11200 * not triggered by video-js-swf. This will be used to determine if
11201 * {@link Tech#manualTimeUpdates} should be called.
11202 *
11203 * @type {boolean}
11204 * @default
11205 */
11206Tech.prototype.featuresTimeupdateEvents = false;
11207
11208/**
11209 * Boolean indicating wether the `Tech` supports the native `TextTrack`s.
11210 * This will help us integrate with native `TextTrack`s if the browser supports them.
11211 *
11212 * @type {boolean}
11213 * @default
11214 */
11215Tech.prototype.featuresNativeTextTracks = false;
11216
11217/**
11218 * A functional mixin for techs that want to use the Source Handler pattern.
11219 * Source handlers are scripts for handling specific formats.
11220 * The source handler pattern is used for adaptive formats (HLS, DASH) that
11221 * manually load video data and feed it into a Source Buffer (Media Source Extensions)
11222 * Example: `Tech.withSourceHandlers.call(MyTech);`
11223 *
11224 * @param {Tech} _Tech
11225 *        The tech to add source handler functions to.
11226 *
11227 * @mixes Tech~SourceHandlerAdditions
11228 */
11229Tech.withSourceHandlers = function (_Tech) {
11230
11231  /**
11232   * Register a source handler
11233   *
11234   * @param {Function} handler
11235   *        The source handler class
11236   *
11237   * @param {number} [index]
11238   *        Register it at the following index
11239   */
11240  _Tech.registerSourceHandler = function (handler, index) {
11241    var handlers = _Tech.sourceHandlers;
11242
11243    if (!handlers) {
11244      handlers = _Tech.sourceHandlers = [];
11245    }
11246
11247    if (index === undefined) {
11248      // add to the end of the list
11249      index = handlers.length;
11250    }
11251
11252    handlers.splice(index, 0, handler);
11253  };
11254
11255  /**
11256   * Check if the tech can support the given type. Also checks the
11257   * Techs sourceHandlers.
11258   *
11259   * @param {string} type
11260   *         The mimetype to check.
11261   *
11262   * @return {string}
11263   *         'probably', 'maybe', or '' (empty string)
11264   */
11265  _Tech.canPlayType = function (type) {
11266    var handlers = _Tech.sourceHandlers || [];
11267    var can = void 0;
11268
11269    for (var i = 0; i < handlers.length; i++) {
11270      can = handlers[i].canPlayType(type);
11271
11272      if (can) {
11273        return can;
11274      }
11275    }
11276
11277    return '';
11278  };
11279
11280  /**
11281   * Returns the first source handler that supports the source.
11282   *
11283   * TODO: Answer question: should 'probably' be prioritized over 'maybe'
11284   *
11285   * @param {Tech~SourceObject} source
11286   *        The source object
11287   *
11288   * @param {Object} options
11289   *        The options passed to the tech
11290   *
11291   * @return {SourceHandler|null}
11292   *          The first source handler that supports the source or null if
11293   *          no SourceHandler supports the source
11294   */
11295  _Tech.selectSourceHandler = function (source, options) {
11296    var handlers = _Tech.sourceHandlers || [];
11297    var can = void 0;
11298
11299    for (var i = 0; i < handlers.length; i++) {
11300      can = handlers[i].canHandleSource(source, options);
11301
11302      if (can) {
11303        return handlers[i];
11304      }
11305    }
11306
11307    return null;
11308  };
11309
11310  /**
11311   * Check if the tech can support the given source.
11312   *
11313   * @param {Tech~SourceObject} srcObj
11314   *        The source object
11315   *
11316   * @param {Object} options
11317   *        The options passed to the tech
11318   *
11319   * @return {string}
11320   *         'probably', 'maybe', or '' (empty string)
11321   */
11322  _Tech.canPlaySource = function (srcObj, options) {
11323    var sh = _Tech.selectSourceHandler(srcObj, options);
11324
11325    if (sh) {
11326      return sh.canHandleSource(srcObj, options);
11327    }
11328
11329    return '';
11330  };
11331
11332  /**
11333   * When using a source handler, prefer its implementation of
11334   * any function normally provided by the tech.
11335   */
11336  var deferrable = ['seekable', 'seeking', 'duration'];
11337
11338  /**
11339   * A wrapper around {@link Tech#seekable} that will call a `SourceHandler`s seekable
11340   * function if it exists, with a fallback to the Techs seekable function.
11341   *
11342   * @method _Tech.seekable
11343   */
11344
11345  /**
11346   * A wrapper around {@link Tech#duration} that will call a `SourceHandler`s duration
11347   * function if it exists, otherwise it will fallback to the techs duration function.
11348   *
11349   * @method _Tech.duration
11350   */
11351
11352  deferrable.forEach(function (fnName) {
11353    var originalFn = this[fnName];
11354
11355    if (typeof originalFn !== 'function') {
11356      return;
11357    }
11358
11359    this[fnName] = function () {
11360      if (this.sourceHandler_ && this.sourceHandler_[fnName]) {
11361        return this.sourceHandler_[fnName].apply(this.sourceHandler_, arguments);
11362      }
11363      return originalFn.apply(this, arguments);
11364    };
11365  }, _Tech.prototype);
11366
11367  /**
11368   * Create a function for setting the source using a source object
11369   * and source handlers.
11370   * Should never be called unless a source handler was found.
11371   *
11372   * @param {Tech~SourceObject} source
11373   *        A source object with src and type keys
11374   */
11375  _Tech.prototype.setSource = function (source) {
11376    var sh = _Tech.selectSourceHandler(source, this.options_);
11377
11378    if (!sh) {
11379      // Fall back to a native source hander when unsupported sources are
11380      // deliberately set
11381      if (_Tech.nativeSourceHandler) {
11382        sh = _Tech.nativeSourceHandler;
11383      } else {
11384        log$1.error('No source hander found for the current source.');
11385      }
11386    }
11387
11388    // Dispose any existing source handler
11389    this.disposeSourceHandler();
11390    this.off('dispose', this.disposeSourceHandler);
11391
11392    if (sh !== _Tech.nativeSourceHandler) {
11393      this.currentSource_ = source;
11394    }
11395
11396    this.sourceHandler_ = sh.handleSource(source, this, this.options_);
11397    this.on('dispose', this.disposeSourceHandler);
11398  };
11399
11400  /**
11401   * Clean up any existing SourceHandlers and listeners when the Tech is disposed.
11402   *
11403   * @listens Tech#dispose
11404   */
11405  _Tech.prototype.disposeSourceHandler = function () {
11406    // if we have a source and get another one
11407    // then we are loading something new
11408    // than clear all of our current tracks
11409    if (this.currentSource_) {
11410      this.clearTracks(['audio', 'video']);
11411      this.currentSource_ = null;
11412    }
11413
11414    // always clean up auto-text tracks
11415    this.cleanupAutoTextTracks();
11416
11417    if (this.sourceHandler_) {
11418
11419      if (this.sourceHandler_.dispose) {
11420        this.sourceHandler_.dispose();
11421      }
11422
11423      this.sourceHandler_ = null;
11424    }
11425  };
11426};
11427
11428// The base Tech class needs to be registered as a Component. It is the only
11429// Tech that can be registered as a Component.
11430Component.registerComponent('Tech', Tech);
11431Tech.registerTech('Tech', Tech);
11432
11433/**
11434 * A list of techs that should be added to techOrder on Players
11435 *
11436 * @private
11437 */
11438Tech.defaultTechOrder_ = [];
11439
11440var middlewares = {};
11441var middlewareInstances = {};
11442
11443var TERMINATOR = {};
11444
11445function use(type, middleware) {
11446  middlewares[type] = middlewares[type] || [];
11447  middlewares[type].push(middleware);
11448}
11449
11450
11451
11452function setSource(player, src, next) {
11453  player.setTimeout(function () {
11454    return setSourceHelper(src, middlewares[src.type], next, player);
11455  }, 1);
11456}
11457
11458function setTech(middleware, tech) {
11459  middleware.forEach(function (mw) {
11460    return mw.setTech && mw.setTech(tech);
11461  });
11462}
11463
11464/**
11465 * Calls a getter on the tech first, through each middleware
11466 * from right to left to the player.
11467 */
11468function get$1(middleware, tech, method) {
11469  return middleware.reduceRight(middlewareIterator(method), tech[method]());
11470}
11471
11472/**
11473 * Takes the argument given to the player and calls the setter method on each
11474 * middlware from left to right to the tech.
11475 */
11476function set$1(middleware, tech, method, arg) {
11477  return tech[method](middleware.reduce(middlewareIterator(method), arg));
11478}
11479
11480/**
11481 * Takes the argument given to the player and calls the `call` version of the method
11482 * on each middleware from left to right.
11483 * Then, call the passed in method on the tech and return the result unchanged
11484 * back to the player, through middleware, this time from right to left.
11485 */
11486function mediate(middleware, tech, method) {
11487  var arg = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : null;
11488
11489  var callMethod = 'call' + toTitleCase(method);
11490  var middlewareValue = middleware.reduce(middlewareIterator(callMethod), arg);
11491  var terminated = middlewareValue === TERMINATOR;
11492  var returnValue = terminated ? null : tech[method](middlewareValue);
11493
11494  executeRight(middleware, method, returnValue, terminated);
11495
11496  return returnValue;
11497}
11498
11499var allowedGetters = {
11500  buffered: 1,
11501  currentTime: 1,
11502  duration: 1,
11503  seekable: 1,
11504  played: 1,
11505  paused: 1
11506};
11507
11508var allowedSetters = {
11509  setCurrentTime: 1
11510};
11511
11512var allowedMediators = {
11513  play: 1,
11514  pause: 1
11515};
11516
11517function middlewareIterator(method) {
11518  return function (value, mw) {
11519    // if the previous middleware terminated, pass along the termination
11520    if (value === TERMINATOR) {
11521      return TERMINATOR;
11522    }
11523
11524    if (mw[method]) {
11525      return mw[method](value);
11526    }
11527
11528    return value;
11529  };
11530}
11531
11532function executeRight(mws, method, value, terminated) {
11533  for (var i = mws.length - 1; i >= 0; i--) {
11534    var mw = mws[i];
11535
11536    if (mw[method]) {
11537      mw[method](terminated, value);
11538    }
11539  }
11540}
11541
11542function clearCacheForPlayer(player) {
11543  middlewareInstances[player.id()] = null;
11544}
11545
11546/**
11547 * {
11548 *  [playerId]: [[mwFactory, mwInstance], ...]
11549 * }
11550 */
11551function getOrCreateFactory(player, mwFactory) {
11552  var mws = middlewareInstances[player.id()];
11553  var mw = null;
11554
11555  if (mws === undefined || mws === null) {
11556    mw = mwFactory(player);
11557    middlewareInstances[player.id()] = [[mwFactory, mw]];
11558    return mw;
11559  }
11560
11561  for (var i = 0; i < mws.length; i++) {
11562    var _mws$i = mws[i],
11563        mwf = _mws$i[0],
11564        mwi = _mws$i[1];
11565
11566
11567    if (mwf !== mwFactory) {
11568      continue;
11569    }
11570
11571    mw = mwi;
11572  }
11573
11574  if (mw === null) {
11575    mw = mwFactory(player);
11576    mws.push([mwFactory, mw]);
11577  }
11578
11579  return mw;
11580}
11581
11582function setSourceHelper() {
11583  var src = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
11584  var middleware = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : [];
11585  var next = arguments[2];
11586  var player = arguments[3];
11587  var acc = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : [];
11588  var lastRun = arguments.length > 5 && arguments[5] !== undefined ? arguments[5] : false;
11589  var mwFactory = middleware[0],
11590      mwrest = middleware.slice(1);
11591
11592  // if mwFactory is a string, then we're at a fork in the road
11593
11594  if (typeof mwFactory === 'string') {
11595    setSourceHelper(src, middlewares[mwFactory], next, player, acc, lastRun);
11596
11597    // if we have an mwFactory, call it with the player to get the mw,
11598    // then call the mw's setSource method
11599  } else if (mwFactory) {
11600    var mw = getOrCreateFactory(player, mwFactory);
11601
11602    // if setSource isn't present, implicitly select this middleware
11603    if (!mw.setSource) {
11604      acc.push(mw);
11605      return setSourceHelper(src, mwrest, next, player, acc, lastRun);
11606    }
11607
11608    mw.setSource(assign({}, src), function (err, _src) {
11609
11610      // something happened, try the next middleware on the current level
11611      // make sure to use the old src
11612      if (err) {
11613        return setSourceHelper(src, mwrest, next, player, acc, lastRun);
11614      }
11615
11616      // we've succeeded, now we need to go deeper
11617      acc.push(mw);
11618
11619      // if it's the same type, continue down the current chain
11620      // otherwise, we want to go down the new chain
11621      setSourceHelper(_src, src.type === _src.type ? mwrest : middlewares[_src.type], next, player, acc, lastRun);
11622    });
11623  } else if (mwrest.length) {
11624    setSourceHelper(src, mwrest, next, player, acc, lastRun);
11625  } else if (lastRun) {
11626    next(src, acc);
11627  } else {
11628    setSourceHelper(src, middlewares['*'], next, player, acc, true);
11629  }
11630}
11631
11632/**
11633 * Mimetypes
11634 *
11635 * @see http://hul.harvard.edu/ois/////systems/wax/wax-public-help/mimetypes.htm
11636 * @typedef Mimetypes~Kind
11637 * @enum
11638 */
11639var MimetypesKind = {
11640  opus: 'video/ogg',
11641  ogv: 'video/ogg',
11642  mp4: 'video/mp4',
11643  mov: 'video/mp4',
11644  m4v: 'video/mp4',
11645  mkv: 'video/x-matroska',
11646  mp3: 'audio/mpeg',
11647  aac: 'audio/aac',
11648  oga: 'audio/ogg',
11649  m3u8: 'application/x-mpegURL'
11650};
11651
11652/**
11653 * Get the mimetype of a given src url if possible
11654 *
11655 * @param {string} src
11656 *        The url to the src
11657 *
11658 * @return {string}
11659 *         return the mimetype if it was known or empty string otherwise
11660 */
11661var getMimetype = function getMimetype() {
11662  var src = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : '';
11663
11664  var ext = getFileExtension(src);
11665  var mimetype = MimetypesKind[ext.toLowerCase()];
11666
11667  return mimetype || '';
11668};
11669
11670/**
11671 * Find the mime type of a given source string if possible. Uses the player
11672 * source cache.
11673 *
11674 * @param {Player} player
11675 *        The player object
11676 *
11677 * @param {string} src
11678 *        The source string
11679 *
11680 * @return {string}
11681 *         The type that was found
11682 */
11683var findMimetype = function findMimetype(player, src) {
11684  if (!src) {
11685    return '';
11686  }
11687
11688  // 1. check for the type in the `source` cache
11689  if (player.cache_.source.src === src && player.cache_.source.type) {
11690    return player.cache_.source.type;
11691  }
11692
11693  // 2. see if we have this source in our `currentSources` cache
11694  var matchingSources = player.cache_.sources.filter(function (s) {
11695    return s.src === src;
11696  });
11697
11698  if (matchingSources.length) {
11699    return matchingSources[0].type;
11700  }
11701
11702  // 3. look for the src url in source elements and use the type there
11703  var sources = player.$$('source');
11704
11705  for (var i = 0; i < sources.length; i++) {
11706    var s = sources[i];
11707
11708    if (s.type && s.src && s.src === src) {
11709      return s.type;
11710    }
11711  }
11712
11713  // 4. finally fallback to our list of mime types based on src url extension
11714  return getMimetype(src);
11715};
11716
11717/**
11718 * @module filter-source
11719 */
11720/**
11721 * Filter out single bad source objects or multiple source objects in an
11722 * array. Also flattens nested source object arrays into a 1 dimensional
11723 * array of source objects.
11724 *
11725 * @param {Tech~SourceObject|Tech~SourceObject[]} src
11726 *        The src object to filter
11727 *
11728 * @return {Tech~SourceObject[]}
11729 *         An array of sourceobjects containing only valid sources
11730 *
11731 * @private
11732 */
11733var filterSource = function filterSource(src) {
11734  // traverse array
11735  if (Array.isArray(src)) {
11736    var newsrc = [];
11737
11738    src.forEach(function (srcobj) {
11739      srcobj = filterSource(srcobj);
11740
11741      if (Array.isArray(srcobj)) {
11742        newsrc = newsrc.concat(srcobj);
11743      } else if (isObject(srcobj)) {
11744        newsrc.push(srcobj);
11745      }
11746    });
11747
11748    src = newsrc;
11749  } else if (typeof src === 'string' && src.trim()) {
11750    // convert string into object
11751    src = [fixSource({ src: src })];
11752  } else if (isObject(src) && typeof src.src === 'string' && src.src && src.src.trim()) {
11753    // src is already valid
11754    src = [fixSource(src)];
11755  } else {
11756    // invalid source, turn it into an empty array
11757    src = [];
11758  }
11759
11760  return src;
11761};
11762
11763/**
11764 * Checks src mimetype, adding it when possible
11765 *
11766 * @param {Tech~SourceObject} src
11767 *        The src object to check
11768 * @return {Tech~SourceObject}
11769 *        src Object with known type
11770 */
11771function fixSource(src) {
11772  var mimetype = getMimetype(src.src);
11773
11774  if (!src.type && mimetype) {
11775    src.type = mimetype;
11776  }
11777
11778  return src;
11779}
11780
11781/**
11782 * @file loader.js
11783 */
11784/**
11785 * The `MediaLoader` is the `Component` that decides which playback technology to load
11786 * when a player is initialized.
11787 *
11788 * @extends Component
11789 */
11790
11791var MediaLoader = function (_Component) {
11792  inherits(MediaLoader, _Component);
11793
11794  /**
11795   * Create an instance of this class.
11796   *
11797   * @param {Player} player
11798   *        The `Player` that this class should attach to.
11799   *
11800   * @param {Object} [options]
11801   *        The key/value stroe of player options.
11802   *
11803   * @param {Component~ReadyCallback} [ready]
11804   *        The function that is run when this component is ready.
11805   */
11806  function MediaLoader(player, options, ready) {
11807    classCallCheck(this, MediaLoader);
11808
11809    // MediaLoader has no element
11810    var options_ = mergeOptions({ createEl: false }, options);
11811
11812    // If there are no sources when the player is initialized,
11813    // load the first supported playback technology.
11814
11815    var _this = possibleConstructorReturn(this, _Component.call(this, player, options_, ready));
11816
11817    if (!options.playerOptions.sources || options.playerOptions.sources.length === 0) {
11818      for (var i = 0, j = options.playerOptions.techOrder; i < j.length; i++) {
11819        var techName = toTitleCase(j[i]);
11820        var tech = Tech.getTech(techName);
11821
11822        // Support old behavior of techs being registered as components.
11823        // Remove once that deprecated behavior is removed.
11824        if (!techName) {
11825          tech = Component.getComponent(techName);
11826        }
11827
11828        // Check if the browser supports this technology
11829        if (tech && tech.isSupported()) {
11830          player.loadTech_(techName);
11831          break;
11832        }
11833      }
11834    } else {
11835      // Loop through playback technologies (HTML5, Flash) and check for support.
11836      // Then load the best source.
11837      // A few assumptions here:
11838      //   All playback technologies respect preload false.
11839      player.src(options.playerOptions.sources);
11840    }
11841    return _this;
11842  }
11843
11844  return MediaLoader;
11845}(Component);
11846
11847Component.registerComponent('MediaLoader', MediaLoader);
11848
11849/**
11850 * @file button.js
11851 */
11852/**
11853 * Clickable Component which is clickable or keyboard actionable,
11854 * but is not a native HTML button.
11855 *
11856 * @extends Component
11857 */
11858
11859var ClickableComponent = function (_Component) {
11860  inherits(ClickableComponent, _Component);
11861
11862  /**
11863   * Creates an instance of this class.
11864   *
11865   * @param  {Player} player
11866   *         The `Player` that this class should be attached to.
11867   *
11868   * @param  {Object} [options]
11869   *         The key/value store of player options.
11870   */
11871  function ClickableComponent(player, options) {
11872    classCallCheck(this, ClickableComponent);
11873
11874    var _this = possibleConstructorReturn(this, _Component.call(this, player, options));
11875
11876    _this.emitTapEvents();
11877
11878    _this.enable();
11879    return _this;
11880  }
11881
11882  /**
11883   * Create the `Component`s DOM element.
11884   *
11885   * @param {string} [tag=div]
11886   *        The element's node type.
11887   *
11888   * @param {Object} [props={}]
11889   *        An object of properties that should be set on the element.
11890   *
11891   * @param {Object} [attributes={}]
11892   *        An object of attributes that should be set on the element.
11893   *
11894   * @return {Element}
11895   *         The element that gets created.
11896   */
11897
11898
11899  ClickableComponent.prototype.createEl = function createEl$$1() {
11900    var tag = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 'div';
11901    var props = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
11902    var attributes = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
11903
11904    props = assign({
11905      innerHTML: '<span aria-hidden="true" class="vjs-icon-placeholder"></span>',
11906      className: this.buildCSSClass(),
11907      tabIndex: 0
11908    }, props);
11909
11910    if (tag === 'button') {
11911      log$1.error('Creating a ClickableComponent with an HTML element of ' + tag + ' is not supported; use a Button instead.');
11912    }
11913
11914    // Add ARIA attributes for clickable element which is not a native HTML button
11915    attributes = assign({
11916      role: 'button'
11917    }, attributes);
11918
11919    this.tabIndex_ = props.tabIndex;
11920
11921    var el = _Component.prototype.createEl.call(this, tag, props, attributes);
11922
11923    this.createControlTextEl(el);
11924
11925    return el;
11926  };
11927
11928  ClickableComponent.prototype.dispose = function dispose() {
11929    // remove controlTextEl_ on dipose
11930    this.controlTextEl_ = null;
11931
11932    _Component.prototype.dispose.call(this);
11933  };
11934
11935  /**
11936   * Create a control text element on this `Component`
11937   *
11938   * @param {Element} [el]
11939   *        Parent element for the control text.
11940   *
11941   * @return {Element}
11942   *         The control text element that gets created.
11943   */
11944
11945
11946  ClickableComponent.prototype.createControlTextEl = function createControlTextEl(el) {
11947    this.controlTextEl_ = createEl('span', {
11948      className: 'vjs-control-text'
11949    }, {
11950      // let the screen reader user know that the text of the element may change
11951      'aria-live': 'polite'
11952    });
11953
11954    if (el) {
11955      el.appendChild(this.controlTextEl_);
11956    }
11957
11958    this.controlText(this.controlText_, el);
11959
11960    return this.controlTextEl_;
11961  };
11962
11963  /**
11964   * Get or set the localize text to use for the controls on the `Component`.
11965   *
11966   * @param {string} [text]
11967   *        Control text for element.
11968   *
11969   * @param {Element} [el=this.el()]
11970   *        Element to set the title on.
11971   *
11972   * @return {string}
11973   *         - The control text when getting
11974   */
11975
11976
11977  ClickableComponent.prototype.controlText = function controlText(text) {
11978    var el = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : this.el();
11979
11980    if (text === undefined) {
11981      return this.controlText_ || 'Need Text';
11982    }
11983
11984    var localizedText = this.localize(text);
11985
11986    this.controlText_ = text;
11987    textContent(this.controlTextEl_, localizedText);
11988    if (!this.nonIconControl) {
11989      // Set title attribute if only an icon is shown
11990      el.setAttribute('title', localizedText);
11991    }
11992  };
11993
11994  /**
11995   * Builds the default DOM `className`.
11996   *
11997   * @return {string}
11998   *         The DOM `className` for this object.
11999   */
12000
12001
12002  ClickableComponent.prototype.buildCSSClass = function buildCSSClass() {
12003    return 'vjs-control vjs-button ' + _Component.prototype.buildCSSClass.call(this);
12004  };
12005
12006  /**
12007   * Enable this `Component`s element.
12008   */
12009
12010
12011  ClickableComponent.prototype.enable = function enable() {
12012    if (!this.enabled_) {
12013      this.enabled_ = true;
12014      this.removeClass('vjs-disabled');
12015      this.el_.setAttribute('aria-disabled', 'false');
12016      if (typeof this.tabIndex_ !== 'undefined') {
12017        this.el_.setAttribute('tabIndex', this.tabIndex_);
12018      }
12019      this.on(['tap', 'click'], this.handleClick);
12020      this.on('focus', this.handleFocus);
12021      this.on('blur', this.handleBlur);
12022    }
12023  };
12024
12025  /**
12026   * Disable this `Component`s element.
12027   */
12028
12029
12030  ClickableComponent.prototype.disable = function disable() {
12031    this.enabled_ = false;
12032    this.addClass('vjs-disabled');
12033    this.el_.setAttribute('aria-disabled', 'true');
12034    if (typeof this.tabIndex_ !== 'undefined') {
12035      this.el_.removeAttribute('tabIndex');
12036    }
12037    this.off(['tap', 'click'], this.handleClick);
12038    this.off('focus', this.handleFocus);
12039    this.off('blur', this.handleBlur);
12040  };
12041
12042  /**
12043   * This gets called when a `ClickableComponent` gets:
12044   * - Clicked (via the `click` event, listening starts in the constructor)
12045   * - Tapped (via the `tap` event, listening starts in the constructor)
12046   * - The following things happen in order:
12047   *   1. {@link ClickableComponent#handleFocus} is called via a `focus` event on the
12048   *      `ClickableComponent`.
12049   *   2. {@link ClickableComponent#handleFocus} adds a listener for `keydown` on using
12050   *      {@link ClickableComponent#handleKeyPress}.
12051   *   3. `ClickableComponent` has not had a `blur` event (`blur` means that focus was lost). The user presses
12052   *      the space or enter key.
12053   *   4. {@link ClickableComponent#handleKeyPress} calls this function with the `keydown`
12054   *      event as a parameter.
12055   *
12056   * @param {EventTarget~Event} event
12057   *        The `keydown`, `tap`, or `click` event that caused this function to be
12058   *        called.
12059   *
12060   * @listens tap
12061   * @listens click
12062   * @abstract
12063   */
12064
12065
12066  ClickableComponent.prototype.handleClick = function handleClick(event) {};
12067
12068  /**
12069   * This gets called when a `ClickableComponent` gains focus via a `focus` event.
12070   * Turns on listening for `keydown` events. When they happen it
12071   * calls `this.handleKeyPress`.
12072   *
12073   * @param {EventTarget~Event} event
12074   *        The `focus` event that caused this function to be called.
12075   *
12076   * @listens focus
12077   */
12078
12079
12080  ClickableComponent.prototype.handleFocus = function handleFocus(event) {
12081    on(document_1, 'keydown', bind(this, this.handleKeyPress));
12082  };
12083
12084  /**
12085   * Called when this ClickableComponent has focus and a key gets pressed down. By
12086   * default it will call `this.handleClick` when the key is space or enter.
12087   *
12088   * @param {EventTarget~Event} event
12089   *        The `keydown` event that caused this function to be called.
12090   *
12091   * @listens keydown
12092   */
12093
12094
12095  ClickableComponent.prototype.handleKeyPress = function handleKeyPress(event) {
12096
12097    // Support Space (32) or Enter (13) key operation to fire a click event
12098    if (event.which === 32 || event.which === 13) {
12099      event.preventDefault();
12100      this.trigger('click');
12101    } else if (_Component.prototype.handleKeyPress) {
12102
12103      // Pass keypress handling up for unsupported keys
12104      _Component.prototype.handleKeyPress.call(this, event);
12105    }
12106  };
12107
12108  /**
12109   * Called when a `ClickableComponent` loses focus. Turns off the listener for
12110   * `keydown` events. Which Stops `this.handleKeyPress` from getting called.
12111   *
12112   * @param {EventTarget~Event} event
12113   *        The `blur` event that caused this function to be called.
12114   *
12115   * @listens blur
12116   */
12117
12118
12119  ClickableComponent.prototype.handleBlur = function handleBlur(event) {
12120    off(document_1, 'keydown', bind(this, this.handleKeyPress));
12121  };
12122
12123  return ClickableComponent;
12124}(Component);
12125
12126Component.registerComponent('ClickableComponent', ClickableComponent);
12127
12128/**
12129 * @file poster-image.js
12130 */
12131/**
12132 * A `ClickableComponent` that handles showing the poster image for the player.
12133 *
12134 * @extends ClickableComponent
12135 */
12136
12137var PosterImage = function (_ClickableComponent) {
12138  inherits(PosterImage, _ClickableComponent);
12139
12140  /**
12141   * Create an instance of this class.
12142   *
12143   * @param {Player} player
12144   *        The `Player` that this class should attach to.
12145   *
12146   * @param {Object} [options]
12147   *        The key/value store of player options.
12148   */
12149  function PosterImage(player, options) {
12150    classCallCheck(this, PosterImage);
12151
12152    var _this = possibleConstructorReturn(this, _ClickableComponent.call(this, player, options));
12153
12154    _this.update();
12155    player.on('posterchange', bind(_this, _this.update));
12156    return _this;
12157  }
12158
12159  /**
12160   * Clean up and dispose of the `PosterImage`.
12161   */
12162
12163
12164  PosterImage.prototype.dispose = function dispose() {
12165    this.player().off('posterchange', this.update);
12166    _ClickableComponent.prototype.dispose.call(this);
12167  };
12168
12169  /**
12170   * Create the `PosterImage`s DOM element.
12171   *
12172   * @return {Element}
12173   *         The element that gets created.
12174   */
12175
12176
12177  PosterImage.prototype.createEl = function createEl$$1() {
12178    var el = createEl('div', {
12179      className: 'vjs-poster',
12180
12181      // Don't want poster to be tabbable.
12182      tabIndex: -1
12183    });
12184
12185    // To ensure the poster image resizes while maintaining its original aspect
12186    // ratio, use a div with `background-size` when available. For browsers that
12187    // do not support `background-size` (e.g. IE8), fall back on using a regular
12188    // img element.
12189    if (!BACKGROUND_SIZE_SUPPORTED) {
12190      this.fallbackImg_ = createEl('img');
12191      el.appendChild(this.fallbackImg_);
12192    }
12193
12194    return el;
12195  };
12196
12197  /**
12198   * An {@link EventTarget~EventListener} for {@link Player#posterchange} events.
12199   *
12200   * @listens Player#posterchange
12201   *
12202   * @param {EventTarget~Event} [event]
12203   *        The `Player#posterchange` event that triggered this function.
12204   */
12205
12206
12207  PosterImage.prototype.update = function update(event) {
12208    var url = this.player().poster();
12209
12210    this.setSrc(url);
12211
12212    // If there's no poster source we should display:none on this component
12213    // so it's not still clickable or right-clickable
12214    if (url) {
12215      this.show();
12216    } else {
12217      this.hide();
12218    }
12219  };
12220
12221  /**
12222   * Set the source of the `PosterImage` depending on the display method.
12223   *
12224   * @param {string} url
12225   *        The URL to the source for the `PosterImage`.
12226   */
12227
12228
12229  PosterImage.prototype.setSrc = function setSrc(url) {
12230    if (this.fallbackImg_) {
12231      this.fallbackImg_.src = url;
12232    } else {
12233      var backgroundImage = '';
12234
12235      // Any falsey values should stay as an empty string, otherwise
12236      // this will throw an extra error
12237      if (url) {
12238        backgroundImage = 'url("' + url + '")';
12239      }
12240
12241      this.el_.style.backgroundImage = backgroundImage;
12242    }
12243  };
12244
12245  /**
12246   * An {@link EventTarget~EventListener} for clicks on the `PosterImage`. See
12247   * {@link ClickableComponent#handleClick} for instances where this will be triggered.
12248   *
12249   * @listens tap
12250   * @listens click
12251   * @listens keydown
12252   *
12253   * @param {EventTarget~Event} event
12254   +        The `click`, `tap` or `keydown` event that caused this function to be called.
12255   */
12256
12257
12258  PosterImage.prototype.handleClick = function handleClick(event) {
12259    // We don't want a click to trigger playback when controls are disabled
12260    if (!this.player_.controls()) {
12261      return;
12262    }
12263
12264    if (this.player_.paused()) {
12265      silencePromise(this.player_.play());
12266    } else {
12267      this.player_.pause();
12268    }
12269  };
12270
12271  return PosterImage;
12272}(ClickableComponent);
12273
12274Component.registerComponent('PosterImage', PosterImage);
12275
12276/**
12277 * @file text-track-display.js
12278 */
12279var darkGray = '#222';
12280var lightGray = '#ccc';
12281var fontMap = {
12282  monospace: 'monospace',
12283  sansSerif: 'sans-serif',
12284  serif: 'serif',
12285  monospaceSansSerif: '"Andale Mono", "Lucida Console", monospace',
12286  monospaceSerif: '"Courier New", monospace',
12287  proportionalSansSerif: 'sans-serif',
12288  proportionalSerif: 'serif',
12289  casual: '"Comic Sans MS", Impact, fantasy',
12290  script: '"Monotype Corsiva", cursive',
12291  smallcaps: '"Andale Mono", "Lucida Console", monospace, sans-serif'
12292};
12293
12294/**
12295 * Construct an rgba color from a given hex color code.
12296 *
12297 * @param {number} color
12298 *        Hex number for color, like #f0e or #f604e2.
12299 *
12300 * @param {number} opacity
12301 *        Value for opacity, 0.0 - 1.0.
12302 *
12303 * @return {string}
12304 *         The rgba color that was created, like 'rgba(255, 0, 0, 0.3)'.
12305 */
12306function constructColor(color, opacity) {
12307  var hex = void 0;
12308
12309  if (color.length === 4) {
12310    // color looks like "#f0e"
12311    hex = color[1] + color[1] + color[2] + color[2] + color[3] + color[3];
12312  } else if (color.length === 7) {
12313    // color looks like "#f604e2"
12314    hex = color.slice(1);
12315  } else {
12316    throw new Error('Invalid color code provided, ' + color + '; must be formatted as e.g. #f0e or #f604e2.');
12317  }
12318  return 'rgba(' + parseInt(hex.slice(0, 2), 16) + ',' + parseInt(hex.slice(2, 4), 16) + ',' + parseInt(hex.slice(4, 6), 16) + ',' + opacity + ')';
12319}
12320
12321/**
12322 * Try to update the style of a DOM element. Some style changes will throw an error,
12323 * particularly in IE8. Those should be noops.
12324 *
12325 * @param {Element} el
12326 *        The DOM element to be styled.
12327 *
12328 * @param {string} style
12329 *        The CSS property on the element that should be styled.
12330 *
12331 * @param {string} rule
12332 *        The style rule that should be applied to the property.
12333 *
12334 * @private
12335 */
12336function tryUpdateStyle(el, style, rule) {
12337  try {
12338    el.style[style] = rule;
12339  } catch (e) {
12340
12341    // Satisfies linter.
12342    return;
12343  }
12344}
12345
12346/**
12347 * The component for displaying text track cues.
12348 *
12349 * @extends Component
12350 */
12351
12352var TextTrackDisplay = function (_Component) {
12353  inherits(TextTrackDisplay, _Component);
12354
12355  /**
12356   * Creates an instance of this class.
12357   *
12358   * @param {Player} player
12359   *        The `Player` that this class should be attached to.
12360   *
12361   * @param {Object} [options]
12362   *        The key/value store of player options.
12363   *
12364   * @param {Component~ReadyCallback} [ready]
12365   *        The function to call when `TextTrackDisplay` is ready.
12366   */
12367  function TextTrackDisplay(player, options, ready) {
12368    classCallCheck(this, TextTrackDisplay);
12369
12370    var _this = possibleConstructorReturn(this, _Component.call(this, player, options, ready));
12371
12372    player.on('loadstart', bind(_this, _this.toggleDisplay));
12373    player.on('texttrackchange', bind(_this, _this.updateDisplay));
12374    player.on('loadstart', bind(_this, _this.preselectTrack));
12375
12376    // This used to be called during player init, but was causing an error
12377    // if a track should show by default and the display hadn't loaded yet.
12378    // Should probably be moved to an external track loader when we support
12379    // tracks that don't need a display.
12380    player.ready(bind(_this, function () {
12381      if (player.tech_ && player.tech_.featuresNativeTextTracks) {
12382        this.hide();
12383        return;
12384      }
12385
12386      player.on('fullscreenchange', bind(this, this.updateDisplay));
12387
12388      var tracks = this.options_.playerOptions.tracks || [];
12389
12390      for (var i = 0; i < tracks.length; i++) {
12391        this.player_.addRemoteTextTrack(tracks[i], true);
12392      }
12393
12394      this.preselectTrack();
12395    }));
12396    return _this;
12397  }
12398
12399  /**
12400  * Preselect a track following this precedence:
12401  * - matches the previously selected {@link TextTrack}'s language and kind
12402  * - matches the previously selected {@link TextTrack}'s language only
12403  * - is the first default captions track
12404  * - is the first default descriptions track
12405  *
12406  * @listens Player#loadstart
12407  */
12408
12409
12410  TextTrackDisplay.prototype.preselectTrack = function preselectTrack() {
12411    var modes = { captions: 1, subtitles: 1 };
12412    var trackList = this.player_.textTracks();
12413    var userPref = this.player_.cache_.selectedLanguage;
12414    var firstDesc = void 0;
12415    var firstCaptions = void 0;
12416    var preferredTrack = void 0;
12417
12418    for (var i = 0; i < trackList.length; i++) {
12419      var track = trackList[i];
12420
12421      if (userPref && userPref.enabled && userPref.language === track.language) {
12422        // Always choose the track that matches both language and kind
12423        if (track.kind === userPref.kind) {
12424          preferredTrack = track;
12425          // or choose the first track that matches language
12426        } else if (!preferredTrack) {
12427          preferredTrack = track;
12428        }
12429
12430        // clear everything if offTextTrackMenuItem was clicked
12431      } else if (userPref && !userPref.enabled) {
12432        preferredTrack = null;
12433        firstDesc = null;
12434        firstCaptions = null;
12435      } else if (track['default']) {
12436        if (track.kind === 'descriptions' && !firstDesc) {
12437          firstDesc = track;
12438        } else if (track.kind in modes && !firstCaptions) {
12439          firstCaptions = track;
12440        }
12441      }
12442    }
12443
12444    // The preferredTrack matches the user preference and takes
12445    // precendence over all the other tracks.
12446    // So, display the preferredTrack before the first default track
12447    // and the subtitles/captions track before the descriptions track
12448    if (preferredTrack) {
12449      preferredTrack.mode = 'showing';
12450    } else if (firstCaptions) {
12451      firstCaptions.mode = 'showing';
12452    } else if (firstDesc) {
12453      firstDesc.mode = 'showing';
12454    }
12455  };
12456
12457  /**
12458   * Turn display of {@link TextTrack}'s from the current state into the other state.
12459   * There are only two states:
12460   * - 'shown'
12461   * - 'hidden'
12462   *
12463   * @listens Player#loadstart
12464   */
12465
12466
12467  TextTrackDisplay.prototype.toggleDisplay = function toggleDisplay() {
12468    if (this.player_.tech_ && this.player_.tech_.featuresNativeTextTracks) {
12469      this.hide();
12470    } else {
12471      this.show();
12472    }
12473  };
12474
12475  /**
12476   * Create the {@link Component}'s DOM element.
12477   *
12478   * @return {Element}
12479   *         The element that was created.
12480   */
12481
12482
12483  TextTrackDisplay.prototype.createEl = function createEl() {
12484    return _Component.prototype.createEl.call(this, 'div', {
12485      className: 'vjs-text-track-display'
12486    }, {
12487      'aria-live': 'off',
12488      'aria-atomic': 'true'
12489    });
12490  };
12491
12492  /**
12493   * Clear all displayed {@link TextTrack}s.
12494   */
12495
12496
12497  TextTrackDisplay.prototype.clearDisplay = function clearDisplay() {
12498    if (typeof window_1.WebVTT === 'function') {
12499      window_1.WebVTT.processCues(window_1, [], this.el_);
12500    }
12501  };
12502
12503  /**
12504   * Update the displayed TextTrack when a either a {@link Player#texttrackchange} or
12505   * a {@link Player#fullscreenchange} is fired.
12506   *
12507   * @listens Player#texttrackchange
12508   * @listens Player#fullscreenchange
12509   */
12510
12511
12512  TextTrackDisplay.prototype.updateDisplay = function updateDisplay() {
12513    var tracks = this.player_.textTracks();
12514
12515    this.clearDisplay();
12516
12517    // Track display prioritization model: if multiple tracks are 'showing',
12518    //  display the first 'subtitles' or 'captions' track which is 'showing',
12519    //  otherwise display the first 'descriptions' track which is 'showing'
12520
12521    var descriptionsTrack = null;
12522    var captionsSubtitlesTrack = null;
12523    var i = tracks.length;
12524
12525    while (i--) {
12526      var track = tracks[i];
12527
12528      if (track.mode === 'showing') {
12529        if (track.kind === 'descriptions') {
12530          descriptionsTrack = track;
12531        } else {
12532          captionsSubtitlesTrack = track;
12533        }
12534      }
12535    }
12536
12537    if (captionsSubtitlesTrack) {
12538      if (this.getAttribute('aria-live') !== 'off') {
12539        this.setAttribute('aria-live', 'off');
12540      }
12541      this.updateForTrack(captionsSubtitlesTrack);
12542    } else if (descriptionsTrack) {
12543      if (this.getAttribute('aria-live') !== 'assertive') {
12544        this.setAttribute('aria-live', 'assertive');
12545      }
12546      this.updateForTrack(descriptionsTrack);
12547    }
12548  };
12549
12550  /**
12551   * Add an {@link Texttrack} to to the {@link Tech}s {@link TextTrackList}.
12552   *
12553   * @param {TextTrack} track
12554   *        Text track object to be added to the list.
12555   */
12556
12557
12558  TextTrackDisplay.prototype.updateForTrack = function updateForTrack(track) {
12559    if (typeof window_1.WebVTT !== 'function' || !track.activeCues) {
12560      return;
12561    }
12562
12563    var cues = [];
12564
12565    for (var _i = 0; _i < track.activeCues.length; _i++) {
12566      cues.push(track.activeCues[_i]);
12567    }
12568
12569    window_1.WebVTT.processCues(window_1, cues, this.el_);
12570
12571    if (!this.player_.textTrackSettings) {
12572      return;
12573    }
12574
12575    var overrides = this.player_.textTrackSettings.getValues();
12576
12577    var i = cues.length;
12578
12579    while (i--) {
12580      var cue = cues[i];
12581
12582      if (!cue) {
12583        continue;
12584      }
12585
12586      var cueDiv = cue.displayState;
12587
12588      if (overrides.color) {
12589        cueDiv.firstChild.style.color = overrides.color;
12590      }
12591      if (overrides.textOpacity) {
12592        tryUpdateStyle(cueDiv.firstChild, 'color', constructColor(overrides.color || '#fff', overrides.textOpacity));
12593      }
12594      if (overrides.backgroundColor) {
12595        cueDiv.firstChild.style.backgroundColor = overrides.backgroundColor;
12596      }
12597      if (overrides.backgroundOpacity) {
12598        tryUpdateStyle(cueDiv.firstChild, 'backgroundColor', constructColor(overrides.backgroundColor || '#000', overrides.backgroundOpacity));
12599      }
12600      if (overrides.windowColor) {
12601        if (overrides.windowOpacity) {
12602          tryUpdateStyle(cueDiv, 'backgroundColor', constructColor(overrides.windowColor, overrides.windowOpacity));
12603        } else {
12604          cueDiv.style.backgroundColor = overrides.windowColor;
12605        }
12606      }
12607      if (overrides.edgeStyle) {
12608        if (overrides.edgeStyle === 'dropshadow') {
12609          cueDiv.firstChild.style.textShadow = '2px 2px 3px ' + darkGray + ', 2px 2px 4px ' + darkGray + ', 2px 2px 5px ' + darkGray;
12610        } else if (overrides.edgeStyle === 'raised') {
12611          cueDiv.firstChild.style.textShadow = '1px 1px ' + darkGray + ', 2px 2px ' + darkGray + ', 3px 3px ' + darkGray;
12612        } else if (overrides.edgeStyle === 'depressed') {
12613          cueDiv.firstChild.style.textShadow = '1px 1px ' + lightGray + ', 0 1px ' + lightGray + ', -1px -1px ' + darkGray + ', 0 -1px ' + darkGray;
12614        } else if (overrides.edgeStyle === 'uniform') {
12615          cueDiv.firstChild.style.textShadow = '0 0 4px ' + darkGray + ', 0 0 4px ' + darkGray + ', 0 0 4px ' + darkGray + ', 0 0 4px ' + darkGray;
12616        }
12617      }
12618      if (overrides.fontPercent && overrides.fontPercent !== 1) {
12619        var fontSize = window_1.parseFloat(cueDiv.style.fontSize);
12620
12621        cueDiv.style.fontSize = fontSize * overrides.fontPercent + 'px';
12622        cueDiv.style.height = 'auto';
12623        cueDiv.style.top = 'auto';
12624        cueDiv.style.bottom = '2px';
12625      }
12626      if (overrides.fontFamily && overrides.fontFamily !== 'default') {
12627        if (overrides.fontFamily === 'small-caps') {
12628          cueDiv.firstChild.style.fontVariant = 'small-caps';
12629        } else {
12630          cueDiv.firstChild.style.fontFamily = fontMap[overrides.fontFamily];
12631        }
12632      }
12633    }
12634  };
12635
12636  return TextTrackDisplay;
12637}(Component);
12638
12639Component.registerComponent('TextTrackDisplay', TextTrackDisplay);
12640
12641/**
12642 * @file loading-spinner.js
12643 */
12644/**
12645 * A loading spinner for use during waiting/loading events.
12646 *
12647 * @extends Component
12648 */
12649
12650var LoadingSpinner = function (_Component) {
12651  inherits(LoadingSpinner, _Component);
12652
12653  function LoadingSpinner() {
12654    classCallCheck(this, LoadingSpinner);
12655    return possibleConstructorReturn(this, _Component.apply(this, arguments));
12656  }
12657
12658  /**
12659   * Create the `LoadingSpinner`s DOM element.
12660   *
12661   * @return {Element}
12662   *         The dom element that gets created.
12663   */
12664  LoadingSpinner.prototype.createEl = function createEl$$1() {
12665    var isAudio = this.player_.isAudio();
12666    var playerType = this.localize(isAudio ? 'Audio Player' : 'Video Player');
12667    var controlText = createEl('span', {
12668      className: 'vjs-control-text',
12669      innerHTML: this.localize('{1} is loading.', [playerType])
12670    });
12671
12672    var el = _Component.prototype.createEl.call(this, 'div', {
12673      className: 'vjs-loading-spinner',
12674      dir: 'ltr'
12675    });
12676
12677    el.appendChild(controlText);
12678
12679    return el;
12680  };
12681
12682  return LoadingSpinner;
12683}(Component);
12684
12685Component.registerComponent('LoadingSpinner', LoadingSpinner);
12686
12687/**
12688 * @file button.js
12689 */
12690/**
12691 * Base class for all buttons.
12692 *
12693 * @extends ClickableComponent
12694 */
12695
12696var Button = function (_ClickableComponent) {
12697  inherits(Button, _ClickableComponent);
12698
12699  function Button() {
12700    classCallCheck(this, Button);
12701    return possibleConstructorReturn(this, _ClickableComponent.apply(this, arguments));
12702  }
12703
12704  /**
12705   * Create the `Button`s DOM element.
12706   *
12707   * @param {string} [tag="button"]
12708   *        The element's node type. This argument is IGNORED: no matter what
12709   *        is passed, it will always create a `button` element.
12710   *
12711   * @param {Object} [props={}]
12712   *        An object of properties that should be set on the element.
12713   *
12714   * @param {Object} [attributes={}]
12715   *        An object of attributes that should be set on the element.
12716   *
12717   * @return {Element}
12718   *         The element that gets created.
12719   */
12720  Button.prototype.createEl = function createEl(tag) {
12721    var props = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
12722    var attributes = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
12723
12724    tag = 'button';
12725
12726    props = assign({
12727      innerHTML: '<span aria-hidden="true" class="vjs-icon-placeholder"></span>',
12728      className: this.buildCSSClass()
12729    }, props);
12730
12731    // Add attributes for button element
12732    attributes = assign({
12733
12734      // Necessary since the default button type is "submit"
12735      type: 'button'
12736    }, attributes);
12737
12738    var el = Component.prototype.createEl.call(this, tag, props, attributes);
12739
12740    this.createControlTextEl(el);
12741
12742    return el;
12743  };
12744
12745  /**
12746   * Add a child `Component` inside of this `Button`.
12747   *
12748   * @param {string|Component} child
12749   *        The name or instance of a child to add.
12750   *
12751   * @param {Object} [options={}]
12752   *        The key/value store of options that will get passed to children of
12753   *        the child.
12754   *
12755   * @return {Component}
12756   *         The `Component` that gets added as a child. When using a string the
12757   *         `Component` will get created by this process.
12758   *
12759   * @deprecated since version 5
12760   */
12761
12762
12763  Button.prototype.addChild = function addChild(child) {
12764    var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
12765
12766    var className = this.constructor.name;
12767
12768    log$1.warn('Adding an actionable (user controllable) child to a Button (' + className + ') is not supported; use a ClickableComponent instead.');
12769
12770    // Avoid the error message generated by ClickableComponent's addChild method
12771    return Component.prototype.addChild.call(this, child, options);
12772  };
12773
12774  /**
12775   * Enable the `Button` element so that it can be activated or clicked. Use this with
12776   * {@link Button#disable}.
12777   */
12778
12779
12780  Button.prototype.enable = function enable() {
12781    _ClickableComponent.prototype.enable.call(this);
12782    this.el_.removeAttribute('disabled');
12783  };
12784
12785  /**
12786   * Disable the `Button` element so that it cannot be activated or clicked. Use this with
12787   * {@link Button#enable}.
12788   */
12789
12790
12791  Button.prototype.disable = function disable() {
12792    _ClickableComponent.prototype.disable.call(this);
12793    this.el_.setAttribute('disabled', 'disabled');
12794  };
12795
12796  /**
12797   * This gets called when a `Button` has focus and `keydown` is triggered via a key
12798   * press.
12799   *
12800   * @param {EventTarget~Event} event
12801   *        The event that caused this function to get called.
12802   *
12803   * @listens keydown
12804   */
12805
12806
12807  Button.prototype.handleKeyPress = function handleKeyPress(event) {
12808
12809    // Ignore Space (32) or Enter (13) key operation, which is handled by the browser for a button.
12810    if (event.which === 32 || event.which === 13) {
12811      return;
12812    }
12813
12814    // Pass keypress handling up for unsupported keys
12815    _ClickableComponent.prototype.handleKeyPress.call(this, event);
12816  };
12817
12818  return Button;
12819}(ClickableComponent);
12820
12821Component.registerComponent('Button', Button);
12822
12823/**
12824 * @file big-play-button.js
12825 */
12826/**
12827 * The initial play button that shows before the video has played. The hiding of the
12828 * `BigPlayButton` get done via CSS and `Player` states.
12829 *
12830 * @extends Button
12831 */
12832
12833var BigPlayButton = function (_Button) {
12834  inherits(BigPlayButton, _Button);
12835
12836  function BigPlayButton(player, options) {
12837    classCallCheck(this, BigPlayButton);
12838
12839    var _this = possibleConstructorReturn(this, _Button.call(this, player, options));
12840
12841    _this.mouseused_ = false;
12842
12843    _this.on('mousedown', _this.handleMouseDown);
12844    return _this;
12845  }
12846
12847  /**
12848   * Builds the default DOM `className`.
12849   *
12850   * @return {string}
12851   *         The DOM `className` for this object. Always returns 'vjs-big-play-button'.
12852   */
12853
12854
12855  BigPlayButton.prototype.buildCSSClass = function buildCSSClass() {
12856    return 'vjs-big-play-button';
12857  };
12858
12859  /**
12860   * This gets called when a `BigPlayButton` "clicked". See {@link ClickableComponent}
12861   * for more detailed information on what a click can be.
12862   *
12863   * @param {EventTarget~Event} event
12864   *        The `keydown`, `tap`, or `click` event that caused this function to be
12865   *        called.
12866   *
12867   * @listens tap
12868   * @listens click
12869   */
12870
12871
12872  BigPlayButton.prototype.handleClick = function handleClick(event) {
12873    var playPromise = this.player_.play();
12874
12875    // exit early if clicked via the mouse
12876    if (this.mouseused_ && event.clientX && event.clientY) {
12877      silencePromise(playPromise);
12878      return;
12879    }
12880
12881    var cb = this.player_.getChild('controlBar');
12882    var playToggle = cb && cb.getChild('playToggle');
12883
12884    if (!playToggle) {
12885      this.player_.focus();
12886      return;
12887    }
12888
12889    var playFocus = function playFocus() {
12890      return playToggle.focus();
12891    };
12892
12893    if (isPromise(playPromise)) {
12894      playPromise.then(playFocus, function () {});
12895    } else {
12896      this.setTimeout(playFocus, 1);
12897    }
12898  };
12899
12900  BigPlayButton.prototype.handleKeyPress = function handleKeyPress(event) {
12901    this.mouseused_ = false;
12902
12903    _Button.prototype.handleKeyPress.call(this, event);
12904  };
12905
12906  BigPlayButton.prototype.handleMouseDown = function handleMouseDown(event) {
12907    this.mouseused_ = true;
12908  };
12909
12910  return BigPlayButton;
12911}(Button);
12912
12913/**
12914 * The text that should display over the `BigPlayButton`s controls. Added to for localization.
12915 *
12916 * @type {string}
12917 * @private
12918 */
12919
12920
12921BigPlayButton.prototype.controlText_ = 'Play Video';
12922
12923Component.registerComponent('BigPlayButton', BigPlayButton);
12924
12925/**
12926 * @file close-button.js
12927 */
12928/**
12929 * The `CloseButton` is a `{@link Button}` that fires a `close` event when
12930 * it gets clicked.
12931 *
12932 * @extends Button
12933 */
12934
12935var CloseButton = function (_Button) {
12936  inherits(CloseButton, _Button);
12937
12938  /**
12939   * Creates an instance of the this class.
12940   *
12941   * @param  {Player} player
12942   *         The `Player` that this class should be attached to.
12943   *
12944   * @param  {Object} [options]
12945   *         The key/value store of player options.
12946   */
12947  function CloseButton(player, options) {
12948    classCallCheck(this, CloseButton);
12949
12950    var _this = possibleConstructorReturn(this, _Button.call(this, player, options));
12951
12952    _this.controlText(options && options.controlText || _this.localize('Close'));
12953    return _this;
12954  }
12955
12956  /**
12957   * Builds the default DOM `className`.
12958   *
12959   * @return {string}
12960   *         The DOM `className` for this object.
12961   */
12962
12963
12964  CloseButton.prototype.buildCSSClass = function buildCSSClass() {
12965    return 'vjs-close-button ' + _Button.prototype.buildCSSClass.call(this);
12966  };
12967
12968  /**
12969   * This gets called when a `CloseButton` gets clicked. See
12970   * {@link ClickableComponent#handleClick} for more information on when this will be
12971   * triggered
12972   *
12973   * @param {EventTarget~Event} event
12974   *        The `keydown`, `tap`, or `click` event that caused this function to be
12975   *        called.
12976   *
12977   * @listens tap
12978   * @listens click
12979   * @fires CloseButton#close
12980   */
12981
12982
12983  CloseButton.prototype.handleClick = function handleClick(event) {
12984
12985    /**
12986     * Triggered when the a `CloseButton` is clicked.
12987     *
12988     * @event CloseButton#close
12989     * @type {EventTarget~Event}
12990     *
12991     * @property {boolean} [bubbles=false]
12992     *           set to false so that the close event does not
12993     *           bubble up to parents if there is no listener
12994     */
12995    this.trigger({ type: 'close', bubbles: false });
12996  };
12997
12998  return CloseButton;
12999}(Button);
13000
13001Component.registerComponent('CloseButton', CloseButton);
13002
13003/**
13004 * @file play-toggle.js
13005 */
13006/**
13007 * Button to toggle between play and pause.
13008 *
13009 * @extends Button
13010 */
13011
13012var PlayToggle = function (_Button) {
13013  inherits(PlayToggle, _Button);
13014
13015  /**
13016   * Creates an instance of this class.
13017   *
13018   * @param {Player} player
13019   *        The `Player` that this class should be attached to.
13020   *
13021   * @param {Object} [options]
13022   *        The key/value store of player options.
13023   */
13024  function PlayToggle(player, options) {
13025    classCallCheck(this, PlayToggle);
13026
13027    var _this = possibleConstructorReturn(this, _Button.call(this, player, options));
13028
13029    _this.on(player, 'play', _this.handlePlay);
13030    _this.on(player, 'pause', _this.handlePause);
13031    _this.on(player, 'ended', _this.handleEnded);
13032    return _this;
13033  }
13034
13035  /**
13036   * Builds the default DOM `className`.
13037   *
13038   * @return {string}
13039   *         The DOM `className` for this object.
13040   */
13041
13042
13043  PlayToggle.prototype.buildCSSClass = function buildCSSClass() {
13044    return 'vjs-play-control ' + _Button.prototype.buildCSSClass.call(this);
13045  };
13046
13047  /**
13048   * This gets called when an `PlayToggle` is "clicked". See
13049   * {@link ClickableComponent} for more detailed information on what a click can be.
13050   *
13051   * @param {EventTarget~Event} [event]
13052   *        The `keydown`, `tap`, or `click` event that caused this function to be
13053   *        called.
13054   *
13055   * @listens tap
13056   * @listens click
13057   */
13058
13059
13060  PlayToggle.prototype.handleClick = function handleClick(event) {
13061    if (this.player_.paused()) {
13062      this.player_.play();
13063    } else {
13064      this.player_.pause();
13065    }
13066  };
13067
13068  /**
13069   * This gets called once after the video has ended and the user seeks so that
13070   * we can change the replay button back to a play button.
13071   *
13072   * @param {EventTarget~Event} [event]
13073   *        The event that caused this function to run.
13074   *
13075   * @listens Player#seeked
13076   */
13077
13078
13079  PlayToggle.prototype.handleSeeked = function handleSeeked(event) {
13080    this.removeClass('vjs-ended');
13081
13082    if (this.player_.paused()) {
13083      this.handlePause(event);
13084    } else {
13085      this.handlePlay(event);
13086    }
13087  };
13088
13089  /**
13090   * Add the vjs-playing class to the element so it can change appearance.
13091   *
13092   * @param {EventTarget~Event} [event]
13093   *        The event that caused this function to run.
13094   *
13095   * @listens Player#play
13096   */
13097
13098
13099  PlayToggle.prototype.handlePlay = function handlePlay(event) {
13100    this.removeClass('vjs-ended');
13101    this.removeClass('vjs-paused');
13102    this.addClass('vjs-playing');
13103    // change the button text to "Pause"
13104    this.controlText('Pause');
13105  };
13106
13107  /**
13108   * Add the vjs-paused class to the element so it can change appearance.
13109   *
13110   * @param {EventTarget~Event} [event]
13111   *        The event that caused this function to run.
13112   *
13113   * @listens Player#pause
13114   */
13115
13116
13117  PlayToggle.prototype.handlePause = function handlePause(event) {
13118    this.removeClass('vjs-playing');
13119    this.addClass('vjs-paused');
13120    // change the button text to "Play"
13121    this.controlText('Play');
13122  };
13123
13124  /**
13125   * Add the vjs-ended class to the element so it can change appearance
13126   *
13127   * @param {EventTarget~Event} [event]
13128   *        The event that caused this function to run.
13129   *
13130   * @listens Player#ended
13131   */
13132
13133
13134  PlayToggle.prototype.handleEnded = function handleEnded(event) {
13135    this.removeClass('vjs-playing');
13136    this.addClass('vjs-ended');
13137    // change the button text to "Replay"
13138    this.controlText('Replay');
13139
13140    // on the next seek remove the replay button
13141    this.one(this.player_, 'seeked', this.handleSeeked);
13142  };
13143
13144  return PlayToggle;
13145}(Button);
13146
13147/**
13148 * The text that should display over the `PlayToggle`s controls. Added for localization.
13149 *
13150 * @type {string}
13151 * @private
13152 */
13153
13154
13155PlayToggle.prototype.controlText_ = 'Play';
13156
13157Component.registerComponent('PlayToggle', PlayToggle);
13158
13159/**
13160 * @file format-time.js
13161 * @module format-time
13162 */
13163
13164/**
13165* Format seconds as a time string, H:MM:SS or M:SS. Supplying a guide (in seconds)
13166* will force a number of leading zeros to cover the length of the guide.
13167*
13168* @param {number} seconds
13169*        Number of seconds to be turned into a string
13170*
13171* @param {number} guide
13172*        Number (in seconds) to model the string after
13173*
13174* @return {string}
13175*         Time formatted as H:MM:SS or M:SS
13176*/
13177var defaultImplementation = function defaultImplementation(seconds, guide) {
13178  seconds = seconds < 0 ? 0 : seconds;
13179  var s = Math.floor(seconds % 60);
13180  var m = Math.floor(seconds / 60 % 60);
13181  var h = Math.floor(seconds / 3600);
13182  var gm = Math.floor(guide / 60 % 60);
13183  var gh = Math.floor(guide / 3600);
13184
13185  // handle invalid times
13186  if (isNaN(seconds) || seconds === Infinity) {
13187    // '-' is false for all relational operators (e.g. <, >=) so this setting
13188    // will add the minimum number of fields specified by the guide
13189    h = m = s = '-';
13190  }
13191
13192  // Check if we need to show hours
13193  h = h > 0 || gh > 0 ? h + ':' : '';
13194
13195  // If hours are showing, we may need to add a leading zero.
13196  // Always show at least one digit of minutes.
13197  m = ((h || gm >= 10) && m < 10 ? '0' + m : m) + ':';
13198
13199  // Check if leading zero is need for seconds
13200  s = s < 10 ? '0' + s : s;
13201
13202  return h + m + s;
13203};
13204
13205var implementation = defaultImplementation;
13206
13207/**
13208 * Replaces the default formatTime implementation with a custom implementation.
13209 *
13210 * @param {Function} customImplementation
13211 *        A function which will be used in place of the default formatTime implementation.
13212 *        Will receive the current time in seconds and the guide (in seconds) as arguments.
13213 */
13214function setFormatTime(customImplementation) {
13215  implementation = customImplementation;
13216}
13217
13218/**
13219 * Resets formatTime to the default implementation.
13220 */
13221function resetFormatTime() {
13222  implementation = defaultImplementation;
13223}
13224
13225var formatTime = function (seconds) {
13226  var guide = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : seconds;
13227
13228  return implementation(seconds, guide);
13229};
13230
13231/**
13232 * @file time-display.js
13233 */
13234/**
13235 * Displays the time left in the video
13236 *
13237 * @extends Component
13238 */
13239
13240var TimeDisplay = function (_Component) {
13241  inherits(TimeDisplay, _Component);
13242
13243  /**
13244   * Creates an instance of this class.
13245   *
13246   * @param {Player} player
13247   *        The `Player` that this class should be attached to.
13248   *
13249   * @param {Object} [options]
13250   *        The key/value store of player options.
13251   */
13252  function TimeDisplay(player, options) {
13253    classCallCheck(this, TimeDisplay);
13254
13255    var _this = possibleConstructorReturn(this, _Component.call(this, player, options));
13256
13257    _this.throttledUpdateContent = throttle(bind(_this, _this.updateContent), 25);
13258    _this.on(player, 'timeupdate', _this.throttledUpdateContent);
13259    return _this;
13260  }
13261
13262  /**
13263   * Create the `Component`'s DOM element
13264   *
13265   * @return {Element}
13266   *         The element that was created.
13267   */
13268
13269
13270  TimeDisplay.prototype.createEl = function createEl$$1(plainName) {
13271    var className = this.buildCSSClass();
13272    var el = _Component.prototype.createEl.call(this, 'div', {
13273      className: className + ' vjs-time-control vjs-control',
13274      innerHTML: '<span class="vjs-control-text">' + this.localize(this.labelText_) + '\xA0</span>'
13275    });
13276
13277    this.contentEl_ = createEl('span', {
13278      className: className + '-display'
13279    }, {
13280      // tell screen readers not to automatically read the time as it changes
13281      'aria-live': 'off'
13282    });
13283
13284    this.updateTextNode_();
13285    el.appendChild(this.contentEl_);
13286    return el;
13287  };
13288
13289  TimeDisplay.prototype.dispose = function dispose() {
13290    this.contentEl_ = null;
13291    this.textNode_ = null;
13292
13293    _Component.prototype.dispose.call(this);
13294  };
13295
13296  /**
13297   * Updates the "remaining time" text node with new content using the
13298   * contents of the `formattedTime_` property.
13299   *
13300   * @private
13301   */
13302
13303
13304  TimeDisplay.prototype.updateTextNode_ = function updateTextNode_() {
13305    if (!this.contentEl_) {
13306      return;
13307    }
13308
13309    while (this.contentEl_.firstChild) {
13310      this.contentEl_.removeChild(this.contentEl_.firstChild);
13311    }
13312
13313    this.textNode_ = document_1.createTextNode(this.formattedTime_ || this.formatTime_(0));
13314    this.contentEl_.appendChild(this.textNode_);
13315  };
13316
13317  /**
13318   * Generates a formatted time for this component to use in display.
13319   *
13320   * @param  {number} time
13321   *         A numeric time, in seconds.
13322   *
13323   * @return {string}
13324   *         A formatted time
13325   *
13326   * @private
13327   */
13328
13329
13330  TimeDisplay.prototype.formatTime_ = function formatTime_(time) {
13331    return formatTime(time);
13332  };
13333
13334  /**
13335   * Updates the time display text node if it has what was passed in changed
13336   * the formatted time.
13337   *
13338   * @param {number} time
13339   *        The time to update to
13340   *
13341   * @private
13342   */
13343
13344
13345  TimeDisplay.prototype.updateFormattedTime_ = function updateFormattedTime_(time) {
13346    var formattedTime = this.formatTime_(time);
13347
13348    if (formattedTime === this.formattedTime_) {
13349      return;
13350    }
13351
13352    this.formattedTime_ = formattedTime;
13353    this.requestAnimationFrame(this.updateTextNode_);
13354  };
13355
13356  /**
13357   * To be filled out in the child class, should update the displayed time
13358   * in accordance with the fact that the current time has changed.
13359   *
13360   * @param {EventTarget~Event} [event]
13361   *        The `timeupdate`  event that caused this to run.
13362   *
13363   * @listens Player#timeupdate
13364   */
13365
13366
13367  TimeDisplay.prototype.updateContent = function updateContent(event) {};
13368
13369  return TimeDisplay;
13370}(Component);
13371
13372/**
13373 * The text that is added to the `TimeDisplay` for screen reader users.
13374 *
13375 * @type {string}
13376 * @private
13377 */
13378
13379
13380TimeDisplay.prototype.labelText_ = 'Time';
13381
13382/**
13383 * The text that should display over the `TimeDisplay`s controls. Added to for localization.
13384 *
13385 * @type {string}
13386 * @private
13387 *
13388 * @deprecated in v7; controlText_ is not used in non-active display Components
13389 */
13390TimeDisplay.prototype.controlText_ = 'Time';
13391
13392Component.registerComponent('TimeDisplay', TimeDisplay);
13393
13394/**
13395 * @file current-time-display.js
13396 */
13397/**
13398 * Displays the current time
13399 *
13400 * @extends Component
13401 */
13402
13403var CurrentTimeDisplay = function (_TimeDisplay) {
13404  inherits(CurrentTimeDisplay, _TimeDisplay);
13405
13406  /**
13407   * Creates an instance of this class.
13408   *
13409   * @param {Player} player
13410   *        The `Player` that this class should be attached to.
13411   *
13412   * @param {Object} [options]
13413   *        The key/value store of player options.
13414   */
13415  function CurrentTimeDisplay(player, options) {
13416    classCallCheck(this, CurrentTimeDisplay);
13417
13418    var _this = possibleConstructorReturn(this, _TimeDisplay.call(this, player, options));
13419
13420    _this.on(player, 'ended', _this.handleEnded);
13421    return _this;
13422  }
13423
13424  /**
13425   * Builds the default DOM `className`.
13426   *
13427   * @return {string}
13428   *         The DOM `className` for this object.
13429   */
13430
13431
13432  CurrentTimeDisplay.prototype.buildCSSClass = function buildCSSClass() {
13433    return 'vjs-current-time';
13434  };
13435
13436  /**
13437   * Update current time display
13438   *
13439   * @param {EventTarget~Event} [event]
13440   *        The `timeupdate` event that caused this function to run.
13441   *
13442   * @listens Player#timeupdate
13443   */
13444
13445
13446  CurrentTimeDisplay.prototype.updateContent = function updateContent(event) {
13447    // Allows for smooth scrubbing, when player can't keep up.
13448    var time = this.player_.scrubbing() ? this.player_.getCache().currentTime : this.player_.currentTime();
13449
13450    this.updateFormattedTime_(time);
13451  };
13452
13453  /**
13454   * When the player fires ended there should be no time left. Sadly
13455   * this is not always the case, lets make it seem like that is the case
13456   * for users.
13457   *
13458   * @param {EventTarget~Event} [event]
13459   *        The `ended` event that caused this to run.
13460   *
13461   * @listens Player#ended
13462   */
13463
13464
13465  CurrentTimeDisplay.prototype.handleEnded = function handleEnded(event) {
13466    if (!this.player_.duration()) {
13467      return;
13468    }
13469    this.updateFormattedTime_(this.player_.duration());
13470  };
13471
13472  return CurrentTimeDisplay;
13473}(TimeDisplay);
13474
13475/**
13476 * The text that is added to the `CurrentTimeDisplay` for screen reader users.
13477 *
13478 * @type {string}
13479 * @private
13480 */
13481
13482
13483CurrentTimeDisplay.prototype.labelText_ = 'Current Time';
13484
13485/**
13486 * The text that should display over the `CurrentTimeDisplay`s controls. Added to for localization.
13487 *
13488 * @type {string}
13489 * @private
13490 *
13491 * @deprecated in v7; controlText_ is not used in non-active display Components
13492 */
13493CurrentTimeDisplay.prototype.controlText_ = 'Current Time';
13494
13495Component.registerComponent('CurrentTimeDisplay', CurrentTimeDisplay);
13496
13497/**
13498 * @file duration-display.js
13499 */
13500/**
13501 * Displays the duration
13502 *
13503 * @extends Component
13504 */
13505
13506var DurationDisplay = function (_TimeDisplay) {
13507  inherits(DurationDisplay, _TimeDisplay);
13508
13509  /**
13510   * Creates an instance of this class.
13511   *
13512   * @param {Player} player
13513   *        The `Player` that this class should be attached to.
13514   *
13515   * @param {Object} [options]
13516   *        The key/value store of player options.
13517   */
13518  function DurationDisplay(player, options) {
13519    classCallCheck(this, DurationDisplay);
13520
13521    // we do not want to/need to throttle duration changes,
13522    // as they should always display the changed duration as
13523    // it has changed
13524    var _this = possibleConstructorReturn(this, _TimeDisplay.call(this, player, options));
13525
13526    _this.on(player, 'durationchange', _this.updateContent);
13527
13528    // Also listen for timeupdate (in the parent) and loadedmetadata because removing those
13529    // listeners could have broken dependent applications/libraries. These
13530    // can likely be removed for 7.0.
13531    _this.on(player, 'loadedmetadata', _this.throttledUpdateContent);
13532    return _this;
13533  }
13534
13535  /**
13536   * Builds the default DOM `className`.
13537   *
13538   * @return {string}
13539   *         The DOM `className` for this object.
13540   */
13541
13542
13543  DurationDisplay.prototype.buildCSSClass = function buildCSSClass() {
13544    return 'vjs-duration';
13545  };
13546
13547  /**
13548   * Update duration time display.
13549   *
13550   * @param {EventTarget~Event} [event]
13551   *        The `durationchange`, `timeupdate`, or `loadedmetadata` event that caused
13552   *        this function to be called.
13553   *
13554   * @listens Player#durationchange
13555   * @listens Player#timeupdate
13556   * @listens Player#loadedmetadata
13557   */
13558
13559
13560  DurationDisplay.prototype.updateContent = function updateContent(event) {
13561    var duration = this.player_.duration();
13562
13563    if (duration && this.duration_ !== duration) {
13564      this.duration_ = duration;
13565      this.updateFormattedTime_(duration);
13566    }
13567  };
13568
13569  return DurationDisplay;
13570}(TimeDisplay);
13571
13572/**
13573 * The text that is added to the `DurationDisplay` for screen reader users.
13574 *
13575 * @type {string}
13576 * @private
13577 */
13578
13579
13580DurationDisplay.prototype.labelText_ = 'Duration';
13581
13582/**
13583 * The text that should display over the `DurationDisplay`s controls. Added to for localization.
13584 *
13585 * @type {string}
13586 * @private
13587 *
13588 * @deprecated in v7; controlText_ is not used in non-active display Components
13589 */
13590DurationDisplay.prototype.controlText_ = 'Duration';
13591
13592Component.registerComponent('DurationDisplay', DurationDisplay);
13593
13594/**
13595 * @file time-divider.js
13596 */
13597/**
13598 * The separator between the current time and duration.
13599 * Can be hidden if it's not needed in the design.
13600 *
13601 * @extends Component
13602 */
13603
13604var TimeDivider = function (_Component) {
13605  inherits(TimeDivider, _Component);
13606
13607  function TimeDivider() {
13608    classCallCheck(this, TimeDivider);
13609    return possibleConstructorReturn(this, _Component.apply(this, arguments));
13610  }
13611
13612  /**
13613   * Create the component's DOM element
13614   *
13615   * @return {Element}
13616   *         The element that was created.
13617   */
13618  TimeDivider.prototype.createEl = function createEl() {
13619    return _Component.prototype.createEl.call(this, 'div', {
13620      className: 'vjs-time-control vjs-time-divider',
13621      innerHTML: '<div><span>/</span></div>'
13622    });
13623  };
13624
13625  return TimeDivider;
13626}(Component);
13627
13628Component.registerComponent('TimeDivider', TimeDivider);
13629
13630/**
13631 * @file remaining-time-display.js
13632 */
13633/**
13634 * Displays the time left in the video
13635 *
13636 * @extends Component
13637 */
13638
13639var RemainingTimeDisplay = function (_TimeDisplay) {
13640  inherits(RemainingTimeDisplay, _TimeDisplay);
13641
13642  /**
13643   * Creates an instance of this class.
13644   *
13645   * @param {Player} player
13646   *        The `Player` that this class should be attached to.
13647   *
13648   * @param {Object} [options]
13649   *        The key/value store of player options.
13650   */
13651  function RemainingTimeDisplay(player, options) {
13652    classCallCheck(this, RemainingTimeDisplay);
13653
13654    var _this = possibleConstructorReturn(this, _TimeDisplay.call(this, player, options));
13655
13656    _this.on(player, 'durationchange', _this.throttledUpdateContent);
13657    _this.on(player, 'ended', _this.handleEnded);
13658    return _this;
13659  }
13660
13661  /**
13662   * Builds the default DOM `className`.
13663   *
13664   * @return {string}
13665   *         The DOM `className` for this object.
13666   */
13667
13668
13669  RemainingTimeDisplay.prototype.buildCSSClass = function buildCSSClass() {
13670    return 'vjs-remaining-time';
13671  };
13672
13673  /**
13674   * The remaining time display prefixes numbers with a "minus" character.
13675   *
13676   * @param  {number} time
13677   *         A numeric time, in seconds.
13678   *
13679   * @return {string}
13680   *         A formatted time
13681   *
13682   * @private
13683   */
13684
13685
13686  RemainingTimeDisplay.prototype.formatTime_ = function formatTime_(time) {
13687    // TODO: The "-" should be decorative, and not announced by a screen reader
13688    return '-' + _TimeDisplay.prototype.formatTime_.call(this, time);
13689  };
13690
13691  /**
13692   * Update remaining time display.
13693   *
13694   * @param {EventTarget~Event} [event]
13695   *        The `timeupdate` or `durationchange` event that caused this to run.
13696   *
13697   * @listens Player#timeupdate
13698   * @listens Player#durationchange
13699   */
13700
13701
13702  RemainingTimeDisplay.prototype.updateContent = function updateContent(event) {
13703    if (!this.player_.duration()) {
13704      return;
13705    }
13706
13707    // @deprecated We should only use remainingTimeDisplay
13708    // as of video.js 7
13709    if (this.player_.remainingTimeDisplay) {
13710      this.updateFormattedTime_(this.player_.remainingTimeDisplay());
13711    } else {
13712      this.updateFormattedTime_(this.player_.remainingTime());
13713    }
13714  };
13715
13716  /**
13717   * When the player fires ended there should be no time left. Sadly
13718   * this is not always the case, lets make it seem like that is the case
13719   * for users.
13720   *
13721   * @param {EventTarget~Event} [event]
13722   *        The `ended` event that caused this to run.
13723   *
13724   * @listens Player#ended
13725   */
13726
13727
13728  RemainingTimeDisplay.prototype.handleEnded = function handleEnded(event) {
13729    if (!this.player_.duration()) {
13730      return;
13731    }
13732    this.updateFormattedTime_(0);
13733  };
13734
13735  return RemainingTimeDisplay;
13736}(TimeDisplay);
13737
13738/**
13739 * The text that is added to the `RemainingTimeDisplay` for screen reader users.
13740 *
13741 * @type {string}
13742 * @private
13743 */
13744
13745
13746RemainingTimeDisplay.prototype.labelText_ = 'Remaining Time';
13747
13748/**
13749 * The text that should display over the `RemainingTimeDisplay`s controls. Added to for localization.
13750 *
13751 * @type {string}
13752 * @private
13753 *
13754 * @deprecated in v7; controlText_ is not used in non-active display Components
13755 */
13756RemainingTimeDisplay.prototype.controlText_ = 'Remaining Time';
13757
13758Component.registerComponent('RemainingTimeDisplay', RemainingTimeDisplay);
13759
13760/**
13761 * @file live-display.js
13762 */
13763// TODO - Future make it click to snap to live
13764
13765/**
13766 * Displays the live indicator when duration is Infinity.
13767 *
13768 * @extends Component
13769 */
13770
13771var LiveDisplay = function (_Component) {
13772  inherits(LiveDisplay, _Component);
13773
13774  /**
13775   * Creates an instance of this class.
13776   *
13777   * @param {Player} player
13778   *        The `Player` that this class should be attached to.
13779   *
13780   * @param {Object} [options]
13781   *        The key/value store of player options.
13782   */
13783  function LiveDisplay(player, options) {
13784    classCallCheck(this, LiveDisplay);
13785
13786    var _this = possibleConstructorReturn(this, _Component.call(this, player, options));
13787
13788    _this.updateShowing();
13789    _this.on(_this.player(), 'durationchange', _this.updateShowing);
13790    return _this;
13791  }
13792
13793  /**
13794   * Create the `Component`'s DOM element
13795   *
13796   * @return {Element}
13797   *         The element that was created.
13798   */
13799
13800
13801  LiveDisplay.prototype.createEl = function createEl$$1() {
13802    var el = _Component.prototype.createEl.call(this, 'div', {
13803      className: 'vjs-live-control vjs-control'
13804    });
13805
13806    this.contentEl_ = createEl('div', {
13807      className: 'vjs-live-display',
13808      innerHTML: '<span class="vjs-control-text">' + this.localize('Stream Type') + '\xA0</span>' + this.localize('LIVE')
13809    }, {
13810      'aria-live': 'off'
13811    });
13812
13813    el.appendChild(this.contentEl_);
13814    return el;
13815  };
13816
13817  LiveDisplay.prototype.dispose = function dispose() {
13818    this.contentEl_ = null;
13819
13820    _Component.prototype.dispose.call(this);
13821  };
13822
13823  /**
13824   * Check the duration to see if the LiveDisplay should be showing or not. Then show/hide
13825   * it accordingly
13826   *
13827   * @param {EventTarget~Event} [event]
13828   *        The {@link Player#durationchange} event that caused this function to run.
13829   *
13830   * @listens Player#durationchange
13831   */
13832
13833
13834  LiveDisplay.prototype.updateShowing = function updateShowing(event) {
13835    if (this.player().duration() === Infinity) {
13836      this.show();
13837    } else {
13838      this.hide();
13839    }
13840  };
13841
13842  return LiveDisplay;
13843}(Component);
13844
13845Component.registerComponent('LiveDisplay', LiveDisplay);
13846
13847/**
13848 * @file slider.js
13849 */
13850/**
13851 * The base functionality for a slider. Can be vertical or horizontal.
13852 * For instance the volume bar or the seek bar on a video is a slider.
13853 *
13854 * @extends Component
13855 */
13856
13857var Slider = function (_Component) {
13858  inherits(Slider, _Component);
13859
13860  /**
13861   * Create an instance of this class
13862   *
13863   * @param {Player} player
13864   *        The `Player` that this class should be attached to.
13865   *
13866   * @param {Object} [options]
13867   *        The key/value store of player options.
13868   */
13869  function Slider(player, options) {
13870    classCallCheck(this, Slider);
13871
13872    // Set property names to bar to match with the child Slider class is looking for
13873    var _this = possibleConstructorReturn(this, _Component.call(this, player, options));
13874
13875    _this.bar = _this.getChild(_this.options_.barName);
13876
13877    // Set a horizontal or vertical class on the slider depending on the slider type
13878    _this.vertical(!!_this.options_.vertical);
13879
13880    _this.enable();
13881    return _this;
13882  }
13883
13884  /**
13885   * Are controls are currently enabled for this slider or not.
13886   *
13887   * @return {boolean}
13888   *         true if controls are enabled, false otherwise
13889   */
13890
13891
13892  Slider.prototype.enabled = function enabled() {
13893    return this.enabled_;
13894  };
13895
13896  /**
13897   * Enable controls for this slider if they are disabled
13898   */
13899
13900
13901  Slider.prototype.enable = function enable() {
13902    if (this.enabled()) {
13903      return;
13904    }
13905
13906    this.on('mousedown', this.handleMouseDown);
13907    this.on('touchstart', this.handleMouseDown);
13908    this.on('focus', this.handleFocus);
13909    this.on('blur', this.handleBlur);
13910    this.on('click', this.handleClick);
13911
13912    this.on(this.player_, 'controlsvisible', this.update);
13913
13914    if (this.playerEvent) {
13915      this.on(this.player_, this.playerEvent, this.update);
13916    }
13917
13918    this.removeClass('disabled');
13919    this.setAttribute('tabindex', 0);
13920
13921    this.enabled_ = true;
13922  };
13923
13924  /**
13925   * Disable controls for this slider if they are enabled
13926   */
13927
13928
13929  Slider.prototype.disable = function disable() {
13930    if (!this.enabled()) {
13931      return;
13932    }
13933    var doc = this.bar.el_.ownerDocument;
13934
13935    this.off('mousedown', this.handleMouseDown);
13936    this.off('touchstart', this.handleMouseDown);
13937    this.off('focus', this.handleFocus);
13938    this.off('blur', this.handleBlur);
13939    this.off('click', this.handleClick);
13940    this.off(this.player_, 'controlsvisible', this.update);
13941    this.off(doc, 'mousemove', this.handleMouseMove);
13942    this.off(doc, 'mouseup', this.handleMouseUp);
13943    this.off(doc, 'touchmove', this.handleMouseMove);
13944    this.off(doc, 'touchend', this.handleMouseUp);
13945    this.removeAttribute('tabindex');
13946
13947    this.addClass('disabled');
13948
13949    if (this.playerEvent) {
13950      this.off(this.player_, this.playerEvent, this.update);
13951    }
13952    this.enabled_ = false;
13953  };
13954
13955  /**
13956   * Create the `Button`s DOM element.
13957   *
13958   * @param {string} type
13959   *        Type of element to create.
13960   *
13961   * @param {Object} [props={}]
13962   *        List of properties in Object form.
13963   *
13964   * @param {Object} [attributes={}]
13965   *        list of attributes in Object form.
13966   *
13967   * @return {Element}
13968   *         The element that gets created.
13969   */
13970
13971
13972  Slider.prototype.createEl = function createEl$$1(type) {
13973    var props = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
13974    var attributes = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
13975
13976    // Add the slider element class to all sub classes
13977    props.className = props.className + ' vjs-slider';
13978    props = assign({
13979      tabIndex: 0
13980    }, props);
13981
13982    attributes = assign({
13983      'role': 'slider',
13984      'aria-valuenow': 0,
13985      'aria-valuemin': 0,
13986      'aria-valuemax': 100,
13987      'tabIndex': 0
13988    }, attributes);
13989
13990    return _Component.prototype.createEl.call(this, type, props, attributes);
13991  };
13992
13993  /**
13994   * Handle `mousedown` or `touchstart` events on the `Slider`.
13995   *
13996   * @param {EventTarget~Event} event
13997   *        `mousedown` or `touchstart` event that triggered this function
13998   *
13999   * @listens mousedown
14000   * @listens touchstart
14001   * @fires Slider#slideractive
14002   */
14003
14004
14005  Slider.prototype.handleMouseDown = function handleMouseDown(event) {
14006    var doc = this.bar.el_.ownerDocument;
14007
14008    if (event.type === 'mousedown') {
14009      event.preventDefault();
14010    }
14011    // Do not call preventDefault() on touchstart in Chrome
14012    // to avoid console warnings. Use a 'touch-action: none' style
14013    // instead to prevent unintented scrolling.
14014    // https://developers.google.com/web/updates/2017/01/scrolling-intervention
14015    if (event.type === 'touchstart' && !IS_CHROME) {
14016      event.preventDefault();
14017    }
14018    blockTextSelection();
14019
14020    this.addClass('vjs-sliding');
14021    /**
14022     * Triggered when the slider is in an active state
14023     *
14024     * @event Slider#slideractive
14025     * @type {EventTarget~Event}
14026     */
14027    this.trigger('slideractive');
14028
14029    this.on(doc, 'mousemove', this.handleMouseMove);
14030    this.on(doc, 'mouseup', this.handleMouseUp);
14031    this.on(doc, 'touchmove', this.handleMouseMove);
14032    this.on(doc, 'touchend', this.handleMouseUp);
14033
14034    this.handleMouseMove(event);
14035  };
14036
14037  /**
14038   * Handle the `mousemove`, `touchmove`, and `mousedown` events on this `Slider`.
14039   * The `mousemove` and `touchmove` events will only only trigger this function during
14040   * `mousedown` and `touchstart`. This is due to {@link Slider#handleMouseDown} and
14041   * {@link Slider#handleMouseUp}.
14042   *
14043   * @param {EventTarget~Event} event
14044   *        `mousedown`, `mousemove`, `touchstart`, or `touchmove` event that triggered
14045   *        this function
14046   *
14047   * @listens mousemove
14048   * @listens touchmove
14049   */
14050
14051
14052  Slider.prototype.handleMouseMove = function handleMouseMove(event) {};
14053
14054  /**
14055   * Handle `mouseup` or `touchend` events on the `Slider`.
14056   *
14057   * @param {EventTarget~Event} event
14058   *        `mouseup` or `touchend` event that triggered this function.
14059   *
14060   * @listens touchend
14061   * @listens mouseup
14062   * @fires Slider#sliderinactive
14063   */
14064
14065
14066  Slider.prototype.handleMouseUp = function handleMouseUp() {
14067    var doc = this.bar.el_.ownerDocument;
14068
14069    unblockTextSelection();
14070
14071    this.removeClass('vjs-sliding');
14072    /**
14073     * Triggered when the slider is no longer in an active state.
14074     *
14075     * @event Slider#sliderinactive
14076     * @type {EventTarget~Event}
14077     */
14078    this.trigger('sliderinactive');
14079
14080    this.off(doc, 'mousemove', this.handleMouseMove);
14081    this.off(doc, 'mouseup', this.handleMouseUp);
14082    this.off(doc, 'touchmove', this.handleMouseMove);
14083    this.off(doc, 'touchend', this.handleMouseUp);
14084
14085    this.update();
14086  };
14087
14088  /**
14089   * Update the progress bar of the `Slider`.
14090   *
14091   * @returns {number}
14092   *          The percentage of progress the progress bar represents as a
14093   *          number from 0 to 1.
14094   */
14095
14096
14097  Slider.prototype.update = function update() {
14098
14099    // In VolumeBar init we have a setTimeout for update that pops and update
14100    // to the end of the execution stack. The player is destroyed before then
14101    // update will cause an error
14102    if (!this.el_) {
14103      return;
14104    }
14105
14106    // If scrubbing, we could use a cached value to make the handle keep up
14107    // with the user's mouse. On HTML5 browsers scrubbing is really smooth, but
14108    // some flash players are slow, so we might want to utilize this later.
14109    // var progress =  (this.player_.scrubbing()) ? this.player_.getCache().currentTime / this.player_.duration() : this.player_.currentTime() / this.player_.duration();
14110    var progress = this.getPercent();
14111    var bar = this.bar;
14112
14113    // If there's no bar...
14114    if (!bar) {
14115      return;
14116    }
14117
14118    // Protect against no duration and other division issues
14119    if (typeof progress !== 'number' || progress !== progress || progress < 0 || progress === Infinity) {
14120      progress = 0;
14121    }
14122
14123    // Convert to a percentage for setting
14124    var percentage = (progress * 100).toFixed(2) + '%';
14125    var style = bar.el().style;
14126
14127    // Set the new bar width or height
14128    if (this.vertical()) {
14129      style.height = percentage;
14130    } else {
14131      style.width = percentage;
14132    }
14133
14134    return progress;
14135  };
14136
14137  /**
14138   * Calculate distance for slider
14139   *
14140   * @param {EventTarget~Event} event
14141   *        The event that caused this function to run.
14142   *
14143   * @return {number}
14144   *         The current position of the Slider.
14145   *         - postition.x for vertical `Slider`s
14146   *         - postition.y for horizontal `Slider`s
14147   */
14148
14149
14150  Slider.prototype.calculateDistance = function calculateDistance(event) {
14151    var position = getPointerPosition(this.el_, event);
14152
14153    if (this.vertical()) {
14154      return position.y;
14155    }
14156    return position.x;
14157  };
14158
14159  /**
14160   * Handle a `focus` event on this `Slider`.
14161   *
14162   * @param {EventTarget~Event} event
14163   *        The `focus` event that caused this function to run.
14164   *
14165   * @listens focus
14166   */
14167
14168
14169  Slider.prototype.handleFocus = function handleFocus() {
14170    this.on(this.bar.el_.ownerDocument, 'keydown', this.handleKeyPress);
14171  };
14172
14173  /**
14174   * Handle a `keydown` event on the `Slider`. Watches for left, rigth, up, and down
14175   * arrow keys. This function will only be called when the slider has focus. See
14176   * {@link Slider#handleFocus} and {@link Slider#handleBlur}.
14177   *
14178   * @param {EventTarget~Event} event
14179   *        the `keydown` event that caused this function to run.
14180   *
14181   * @listens keydown
14182   */
14183
14184
14185  Slider.prototype.handleKeyPress = function handleKeyPress(event) {
14186    // Left and Down Arrows
14187    if (event.which === 37 || event.which === 40) {
14188      event.preventDefault();
14189      this.stepBack();
14190
14191      // Up and Right Arrows
14192    } else if (event.which === 38 || event.which === 39) {
14193      event.preventDefault();
14194      this.stepForward();
14195    }
14196  };
14197
14198  /**
14199   * Handle a `blur` event on this `Slider`.
14200   *
14201   * @param {EventTarget~Event} event
14202   *        The `blur` event that caused this function to run.
14203   *
14204   * @listens blur
14205   */
14206
14207  Slider.prototype.handleBlur = function handleBlur() {
14208    this.off(this.bar.el_.ownerDocument, 'keydown', this.handleKeyPress);
14209  };
14210
14211  /**
14212   * Listener for click events on slider, used to prevent clicks
14213   *   from bubbling up to parent elements like button menus.
14214   *
14215   * @param {Object} event
14216   *        Event that caused this object to run
14217   */
14218
14219
14220  Slider.prototype.handleClick = function handleClick(event) {
14221    event.stopImmediatePropagation();
14222    event.preventDefault();
14223  };
14224
14225  /**
14226   * Get/set if slider is horizontal for vertical
14227   *
14228   * @param {boolean} [bool]
14229   *        - true if slider is vertical,
14230   *        - false is horizontal
14231   *
14232   * @return {boolean}
14233   *         - true if slider is vertical, and getting
14234   *         - false if the slider is horizontal, and getting
14235   */
14236
14237
14238  Slider.prototype.vertical = function vertical(bool) {
14239    if (bool === undefined) {
14240      return this.vertical_ || false;
14241    }
14242
14243    this.vertical_ = !!bool;
14244
14245    if (this.vertical_) {
14246      this.addClass('vjs-slider-vertical');
14247    } else {
14248      this.addClass('vjs-slider-horizontal');
14249    }
14250  };
14251
14252  return Slider;
14253}(Component);
14254
14255Component.registerComponent('Slider', Slider);
14256
14257/**
14258 * @file load-progress-bar.js
14259 */
14260/**
14261 * Shows loading progress
14262 *
14263 * @extends Component
14264 */
14265
14266var LoadProgressBar = function (_Component) {
14267  inherits(LoadProgressBar, _Component);
14268
14269  /**
14270   * Creates an instance of this class.
14271   *
14272   * @param {Player} player
14273   *        The `Player` that this class should be attached to.
14274   *
14275   * @param {Object} [options]
14276   *        The key/value store of player options.
14277   */
14278  function LoadProgressBar(player, options) {
14279    classCallCheck(this, LoadProgressBar);
14280
14281    var _this = possibleConstructorReturn(this, _Component.call(this, player, options));
14282
14283    _this.partEls_ = [];
14284    _this.on(player, 'progress', _this.update);
14285    return _this;
14286  }
14287
14288  /**
14289   * Create the `Component`'s DOM element
14290   *
14291   * @return {Element}
14292   *         The element that was created.
14293   */
14294
14295
14296  LoadProgressBar.prototype.createEl = function createEl$$1() {
14297    return _Component.prototype.createEl.call(this, 'div', {
14298      className: 'vjs-load-progress',
14299      innerHTML: '<span class="vjs-control-text"><span>' + this.localize('Loaded') + '</span>: 0%</span>'
14300    });
14301  };
14302
14303  LoadProgressBar.prototype.dispose = function dispose() {
14304    this.partEls_ = null;
14305
14306    _Component.prototype.dispose.call(this);
14307  };
14308
14309  /**
14310   * Update progress bar
14311   *
14312   * @param {EventTarget~Event} [event]
14313   *        The `progress` event that caused this function to run.
14314   *
14315   * @listens Player#progress
14316   */
14317
14318
14319  LoadProgressBar.prototype.update = function update(event) {
14320    var buffered = this.player_.buffered();
14321    var duration = this.player_.duration();
14322    var bufferedEnd = this.player_.bufferedEnd();
14323    var children = this.partEls_;
14324
14325    // get the percent width of a time compared to the total end
14326    var percentify = function percentify(time, end) {
14327      // no NaN
14328      var percent = time / end || 0;
14329
14330      return (percent >= 1 ? 1 : percent) * 100 + '%';
14331    };
14332
14333    // update the width of the progress bar
14334    this.el_.style.width = percentify(bufferedEnd, duration);
14335
14336    // add child elements to represent the individual buffered time ranges
14337    for (var i = 0; i < buffered.length; i++) {
14338      var start = buffered.start(i);
14339      var end = buffered.end(i);
14340      var part = children[i];
14341
14342      if (!part) {
14343        part = this.el_.appendChild(createEl());
14344        children[i] = part;
14345      }
14346
14347      // set the percent based on the width of the progress bar (bufferedEnd)
14348      part.style.left = percentify(start, bufferedEnd);
14349      part.style.width = percentify(end - start, bufferedEnd);
14350    }
14351
14352    // remove unused buffered range elements
14353    for (var _i = children.length; _i > buffered.length; _i--) {
14354      this.el_.removeChild(children[_i - 1]);
14355    }
14356    children.length = buffered.length;
14357  };
14358
14359  return LoadProgressBar;
14360}(Component);
14361
14362Component.registerComponent('LoadProgressBar', LoadProgressBar);
14363
14364/**
14365 * @file time-tooltip.js
14366 */
14367/**
14368 * Time tooltips display a time above the progress bar.
14369 *
14370 * @extends Component
14371 */
14372
14373var TimeTooltip = function (_Component) {
14374  inherits(TimeTooltip, _Component);
14375
14376  function TimeTooltip() {
14377    classCallCheck(this, TimeTooltip);
14378    return possibleConstructorReturn(this, _Component.apply(this, arguments));
14379  }
14380
14381  /**
14382   * Create the time tooltip DOM element
14383   *
14384   * @return {Element}
14385   *         The element that was created.
14386   */
14387  TimeTooltip.prototype.createEl = function createEl$$1() {
14388    return _Component.prototype.createEl.call(this, 'div', {
14389      className: 'vjs-time-tooltip'
14390    });
14391  };
14392
14393  /**
14394   * Updates the position of the time tooltip relative to the `SeekBar`.
14395   *
14396   * @param {Object} seekBarRect
14397   *        The `ClientRect` for the {@link SeekBar} element.
14398   *
14399   * @param {number} seekBarPoint
14400   *        A number from 0 to 1, representing a horizontal reference point
14401   *        from the left edge of the {@link SeekBar}
14402   */
14403
14404
14405  TimeTooltip.prototype.update = function update(seekBarRect, seekBarPoint, content) {
14406    var tooltipRect = getBoundingClientRect(this.el_);
14407    var playerRect = getBoundingClientRect(this.player_.el());
14408    var seekBarPointPx = seekBarRect.width * seekBarPoint;
14409
14410    // do nothing if either rect isn't available
14411    // for example, if the player isn't in the DOM for testing
14412    if (!playerRect || !tooltipRect) {
14413      return;
14414    }
14415
14416    // This is the space left of the `seekBarPoint` available within the bounds
14417    // of the player. We calculate any gap between the left edge of the player
14418    // and the left edge of the `SeekBar` and add the number of pixels in the
14419    // `SeekBar` before hitting the `seekBarPoint`
14420    var spaceLeftOfPoint = seekBarRect.left - playerRect.left + seekBarPointPx;
14421
14422    // This is the space right of the `seekBarPoint` available within the bounds
14423    // of the player. We calculate the number of pixels from the `seekBarPoint`
14424    // to the right edge of the `SeekBar` and add to that any gap between the
14425    // right edge of the `SeekBar` and the player.
14426    var spaceRightOfPoint = seekBarRect.width - seekBarPointPx + (playerRect.right - seekBarRect.right);
14427
14428    // This is the number of pixels by which the tooltip will need to be pulled
14429    // further to the right to center it over the `seekBarPoint`.
14430    var pullTooltipBy = tooltipRect.width / 2;
14431
14432    // Adjust the `pullTooltipBy` distance to the left or right depending on
14433    // the results of the space calculations above.
14434    if (spaceLeftOfPoint < pullTooltipBy) {
14435      pullTooltipBy += pullTooltipBy - spaceLeftOfPoint;
14436    } else if (spaceRightOfPoint < pullTooltipBy) {
14437      pullTooltipBy = spaceRightOfPoint;
14438    }
14439
14440    // Due to the imprecision of decimal/ratio based calculations and varying
14441    // rounding behaviors, there are cases where the spacing adjustment is off
14442    // by a pixel or two. This adds insurance to these calculations.
14443    if (pullTooltipBy < 0) {
14444      pullTooltipBy = 0;
14445    } else if (pullTooltipBy > tooltipRect.width) {
14446      pullTooltipBy = tooltipRect.width;
14447    }
14448
14449    this.el_.style.right = '-' + pullTooltipBy + 'px';
14450    textContent(this.el_, content);
14451  };
14452
14453  return TimeTooltip;
14454}(Component);
14455
14456Component.registerComponent('TimeTooltip', TimeTooltip);
14457
14458/**
14459 * @file play-progress-bar.js
14460 */
14461/**
14462 * Used by {@link SeekBar} to display media playback progress as part of the
14463 * {@link ProgressControl}.
14464 *
14465 * @extends Component
14466 */
14467
14468var PlayProgressBar = function (_Component) {
14469  inherits(PlayProgressBar, _Component);
14470
14471  function PlayProgressBar() {
14472    classCallCheck(this, PlayProgressBar);
14473    return possibleConstructorReturn(this, _Component.apply(this, arguments));
14474  }
14475
14476  /**
14477   * Create the the DOM element for this class.
14478   *
14479   * @return {Element}
14480   *         The element that was created.
14481   */
14482  PlayProgressBar.prototype.createEl = function createEl() {
14483    return _Component.prototype.createEl.call(this, 'div', {
14484      className: 'vjs-play-progress vjs-slider-bar',
14485      innerHTML: '<span class="vjs-control-text"><span>' + this.localize('Progress') + '</span>: 0%</span>'
14486    });
14487  };
14488
14489  /**
14490   * Enqueues updates to its own DOM as well as the DOM of its
14491   * {@link TimeTooltip} child.
14492   *
14493   * @param {Object} seekBarRect
14494   *        The `ClientRect` for the {@link SeekBar} element.
14495   *
14496   * @param {number} seekBarPoint
14497   *        A number from 0 to 1, representing a horizontal reference point
14498   *        from the left edge of the {@link SeekBar}
14499   */
14500
14501
14502  PlayProgressBar.prototype.update = function update(seekBarRect, seekBarPoint) {
14503    var _this2 = this;
14504
14505    // If there is an existing rAF ID, cancel it so we don't over-queue.
14506    if (this.rafId_) {
14507      this.cancelAnimationFrame(this.rafId_);
14508    }
14509
14510    this.rafId_ = this.requestAnimationFrame(function () {
14511      var time = _this2.player_.scrubbing() ? _this2.player_.getCache().currentTime : _this2.player_.currentTime();
14512
14513      var content = formatTime(time, _this2.player_.duration());
14514      var timeTooltip = _this2.getChild('timeTooltip');
14515
14516      if (timeTooltip) {
14517        timeTooltip.update(seekBarRect, seekBarPoint, content);
14518      }
14519    });
14520  };
14521
14522  return PlayProgressBar;
14523}(Component);
14524
14525/**
14526 * Default options for {@link PlayProgressBar}.
14527 *
14528 * @type {Object}
14529 * @private
14530 */
14531
14532
14533PlayProgressBar.prototype.options_ = {
14534  children: []
14535};
14536
14537// Time tooltips should not be added to a player on mobile devices or IE8
14538if ((!IE_VERSION || IE_VERSION > 8) && !IS_IOS && !IS_ANDROID) {
14539  PlayProgressBar.prototype.options_.children.push('timeTooltip');
14540}
14541
14542Component.registerComponent('PlayProgressBar', PlayProgressBar);
14543
14544/**
14545 * @file mouse-time-display.js
14546 */
14547/**
14548 * The {@link MouseTimeDisplay} component tracks mouse movement over the
14549 * {@link ProgressControl}. It displays an indicator and a {@link TimeTooltip}
14550 * indicating the time which is represented by a given point in the
14551 * {@link ProgressControl}.
14552 *
14553 * @extends Component
14554 */
14555
14556var MouseTimeDisplay = function (_Component) {
14557  inherits(MouseTimeDisplay, _Component);
14558
14559  /**
14560   * Creates an instance of this class.
14561   *
14562   * @param {Player} player
14563   *        The {@link Player} that this class should be attached to.
14564   *
14565   * @param {Object} [options]
14566   *        The key/value store of player options.
14567   */
14568  function MouseTimeDisplay(player, options) {
14569    classCallCheck(this, MouseTimeDisplay);
14570
14571    var _this = possibleConstructorReturn(this, _Component.call(this, player, options));
14572
14573    _this.update = throttle(bind(_this, _this.update), 25);
14574    return _this;
14575  }
14576
14577  /**
14578   * Create the DOM element for this class.
14579   *
14580   * @return {Element}
14581   *         The element that was created.
14582   */
14583
14584
14585  MouseTimeDisplay.prototype.createEl = function createEl() {
14586    return _Component.prototype.createEl.call(this, 'div', {
14587      className: 'vjs-mouse-display'
14588    });
14589  };
14590
14591  /**
14592   * Enqueues updates to its own DOM as well as the DOM of its
14593   * {@link TimeTooltip} child.
14594   *
14595   * @param {Object} seekBarRect
14596   *        The `ClientRect` for the {@link SeekBar} element.
14597   *
14598   * @param {number} seekBarPoint
14599   *        A number from 0 to 1, representing a horizontal reference point
14600   *        from the left edge of the {@link SeekBar}
14601   */
14602
14603
14604  MouseTimeDisplay.prototype.update = function update(seekBarRect, seekBarPoint) {
14605    var _this2 = this;
14606
14607    // If there is an existing rAF ID, cancel it so we don't over-queue.
14608    if (this.rafId_) {
14609      this.cancelAnimationFrame(this.rafId_);
14610    }
14611
14612    this.rafId_ = this.requestAnimationFrame(function () {
14613      var duration = _this2.player_.duration();
14614      var content = formatTime(seekBarPoint * duration, duration);
14615
14616      _this2.el_.style.left = seekBarRect.width * seekBarPoint + 'px';
14617      _this2.getChild('timeTooltip').update(seekBarRect, seekBarPoint, content);
14618    });
14619  };
14620
14621  return MouseTimeDisplay;
14622}(Component);
14623
14624/**
14625 * Default options for `MouseTimeDisplay`
14626 *
14627 * @type {Object}
14628 * @private
14629 */
14630
14631
14632MouseTimeDisplay.prototype.options_ = {
14633  children: ['timeTooltip']
14634};
14635
14636Component.registerComponent('MouseTimeDisplay', MouseTimeDisplay);
14637
14638/**
14639 * @file seek-bar.js
14640 */
14641// The number of seconds the `step*` functions move the timeline.
14642var STEP_SECONDS = 5;
14643
14644// The interval at which the bar should update as it progresses.
14645var UPDATE_REFRESH_INTERVAL = 30;
14646
14647/**
14648 * Seek bar and container for the progress bars. Uses {@link PlayProgressBar}
14649 * as its `bar`.
14650 *
14651 * @extends Slider
14652 */
14653
14654var SeekBar = function (_Slider) {
14655  inherits(SeekBar, _Slider);
14656
14657  /**
14658   * Creates an instance of this class.
14659   *
14660   * @param {Player} player
14661   *        The `Player` that this class should be attached to.
14662   *
14663   * @param {Object} [options]
14664   *        The key/value store of player options.
14665   */
14666  function SeekBar(player, options) {
14667    classCallCheck(this, SeekBar);
14668
14669    var _this = possibleConstructorReturn(this, _Slider.call(this, player, options));
14670
14671    _this.setEventHandlers_();
14672    return _this;
14673  }
14674
14675  /**
14676   * Sets the event handlers
14677   *
14678   * @private
14679   */
14680
14681
14682  SeekBar.prototype.setEventHandlers_ = function setEventHandlers_() {
14683    var _this2 = this;
14684
14685    this.update = throttle(bind(this, this.update), UPDATE_REFRESH_INTERVAL);
14686
14687    this.on(this.player_, 'timeupdate', this.update);
14688    this.on(this.player_, 'ended', this.handleEnded);
14689
14690    // when playing, let's ensure we smoothly update the play progress bar
14691    // via an interval
14692    this.updateInterval = null;
14693
14694    this.on(this.player_, ['playing'], function () {
14695      _this2.clearInterval(_this2.updateInterval);
14696
14697      _this2.updateInterval = _this2.setInterval(function () {
14698        _this2.requestAnimationFrame(function () {
14699          _this2.update();
14700        });
14701      }, UPDATE_REFRESH_INTERVAL);
14702    });
14703
14704    this.on(this.player_, ['ended', 'pause', 'waiting'], function () {
14705      _this2.clearInterval(_this2.updateInterval);
14706    });
14707
14708    this.on(this.player_, ['timeupdate', 'ended'], this.update);
14709  };
14710
14711  /**
14712   * Create the `Component`'s DOM element
14713   *
14714   * @return {Element}
14715   *         The element that was created.
14716   */
14717
14718
14719  SeekBar.prototype.createEl = function createEl$$1() {
14720    return _Slider.prototype.createEl.call(this, 'div', {
14721      className: 'vjs-progress-holder'
14722    }, {
14723      'aria-label': this.localize('Progress Bar')
14724    });
14725  };
14726
14727  /**
14728   * This function updates the play progress bar and accessiblity
14729   * attributes to whatever is passed in.
14730   *
14731   * @param {number} currentTime
14732   *        The currentTime value that should be used for accessiblity
14733   *
14734   * @param {number} percent
14735   *        The percentage as a decimal that the bar should be filled from 0-1.
14736   *
14737   * @private
14738   */
14739
14740
14741  SeekBar.prototype.update_ = function update_(currentTime, percent) {
14742    var duration = this.player_.duration();
14743
14744    // machine readable value of progress bar (percentage complete)
14745    this.el_.setAttribute('aria-valuenow', (percent * 100).toFixed(2));
14746
14747    // human readable value of progress bar (time complete)
14748    this.el_.setAttribute('aria-valuetext', this.localize('progress bar timing: currentTime={1} duration={2}', [formatTime(currentTime, duration), formatTime(duration, duration)], '{1} of {2}'));
14749
14750    // Update the `PlayProgressBar`.
14751    this.bar.update(getBoundingClientRect(this.el_), percent);
14752  };
14753
14754  /**
14755   * Update the seek bar's UI.
14756   *
14757   * @param {EventTarget~Event} [event]
14758   *        The `timeupdate` or `ended` event that caused this to run.
14759   *
14760   * @listens Player#timeupdate
14761   *
14762   * @returns {number}
14763   *          The current percent at a number from 0-1
14764   */
14765
14766
14767  SeekBar.prototype.update = function update(event) {
14768    var percent = _Slider.prototype.update.call(this);
14769
14770    this.update_(this.getCurrentTime_(), percent);
14771    return percent;
14772  };
14773
14774  /**
14775   * Get the value of current time but allows for smooth scrubbing,
14776   * when player can't keep up.
14777   *
14778   * @return {number}
14779   *         The current time value to display
14780   *
14781   * @private
14782   */
14783
14784
14785  SeekBar.prototype.getCurrentTime_ = function getCurrentTime_() {
14786    return this.player_.scrubbing() ? this.player_.getCache().currentTime : this.player_.currentTime();
14787  };
14788
14789  /**
14790   * We want the seek bar to be full on ended
14791   * no matter what the actual internal values are. so we force it.
14792   *
14793   * @param {EventTarget~Event} [event]
14794   *        The `timeupdate` or `ended` event that caused this to run.
14795   *
14796   * @listens Player#ended
14797   */
14798
14799
14800  SeekBar.prototype.handleEnded = function handleEnded(event) {
14801    this.update_(this.player_.duration(), 1);
14802  };
14803
14804  /**
14805   * Get the percentage of media played so far.
14806   *
14807   * @return {number}
14808   *         The percentage of media played so far (0 to 1).
14809   */
14810
14811
14812  SeekBar.prototype.getPercent = function getPercent() {
14813    var percent = this.getCurrentTime_() / this.player_.duration();
14814
14815    return percent >= 1 ? 1 : percent;
14816  };
14817
14818  /**
14819   * Handle mouse down on seek bar
14820   *
14821   * @param {EventTarget~Event} event
14822   *        The `mousedown` event that caused this to run.
14823   *
14824   * @listens mousedown
14825   */
14826
14827
14828  SeekBar.prototype.handleMouseDown = function handleMouseDown(event) {
14829    if (!isSingleLeftClick(event)) {
14830      return;
14831    }
14832
14833    // Stop event propagation to prevent double fire in progress-control.js
14834    event.stopPropagation();
14835    this.player_.scrubbing(true);
14836
14837    this.videoWasPlaying = !this.player_.paused();
14838    this.player_.pause();
14839
14840    _Slider.prototype.handleMouseDown.call(this, event);
14841  };
14842
14843  /**
14844   * Handle mouse move on seek bar
14845   *
14846   * @param {EventTarget~Event} event
14847   *        The `mousemove` event that caused this to run.
14848   *
14849   * @listens mousemove
14850   */
14851
14852
14853  SeekBar.prototype.handleMouseMove = function handleMouseMove(event) {
14854    if (!isSingleLeftClick(event)) {
14855      return;
14856    }
14857
14858    var newTime = this.calculateDistance(event) * this.player_.duration();
14859
14860    // Don't let video end while scrubbing.
14861    if (newTime === this.player_.duration()) {
14862      newTime = newTime - 0.1;
14863    }
14864
14865    // Set new time (tell player to seek to new time)
14866    this.player_.currentTime(newTime);
14867  };
14868
14869  SeekBar.prototype.enable = function enable() {
14870    _Slider.prototype.enable.call(this);
14871    var mouseTimeDisplay = this.getChild('mouseTimeDisplay');
14872
14873    if (!mouseTimeDisplay) {
14874      return;
14875    }
14876
14877    mouseTimeDisplay.show();
14878  };
14879
14880  SeekBar.prototype.disable = function disable() {
14881    _Slider.prototype.disable.call(this);
14882    var mouseTimeDisplay = this.getChild('mouseTimeDisplay');
14883
14884    if (!mouseTimeDisplay) {
14885      return;
14886    }
14887
14888    mouseTimeDisplay.hide();
14889  };
14890
14891  /**
14892   * Handle mouse up on seek bar
14893   *
14894   * @param {EventTarget~Event} event
14895   *        The `mouseup` event that caused this to run.
14896   *
14897   * @listens mouseup
14898   */
14899
14900
14901  SeekBar.prototype.handleMouseUp = function handleMouseUp(event) {
14902    _Slider.prototype.handleMouseUp.call(this, event);
14903
14904    // Stop event propagation to prevent double fire in progress-control.js
14905    if (event) {
14906      event.stopPropagation();
14907    }
14908    this.player_.scrubbing(false);
14909
14910    /**
14911     * Trigger timeupdate because we're done seeking and the time has changed.
14912     * This is particularly useful for if the player is paused to time the time displays.
14913     *
14914     * @event Tech#timeupdate
14915     * @type {EventTarget~Event}
14916     */
14917    this.player_.trigger({ type: 'timeupdate', target: this, manuallyTriggered: true });
14918    if (this.videoWasPlaying) {
14919      silencePromise(this.player_.play());
14920    }
14921  };
14922
14923  /**
14924   * Move more quickly fast forward for keyboard-only users
14925   */
14926
14927
14928  SeekBar.prototype.stepForward = function stepForward() {
14929    this.player_.currentTime(this.player_.currentTime() + STEP_SECONDS);
14930  };
14931
14932  /**
14933   * Move more quickly rewind for keyboard-only users
14934   */
14935
14936
14937  SeekBar.prototype.stepBack = function stepBack() {
14938    this.player_.currentTime(this.player_.currentTime() - STEP_SECONDS);
14939  };
14940
14941  /**
14942   * Toggles the playback state of the player
14943   * This gets called when enter or space is used on the seekbar
14944   *
14945   * @param {EventTarget~Event} event
14946   *        The `keydown` event that caused this function to be called
14947   *
14948   */
14949
14950
14951  SeekBar.prototype.handleAction = function handleAction(event) {
14952    if (this.player_.paused()) {
14953      this.player_.play();
14954    } else {
14955      this.player_.pause();
14956    }
14957  };
14958
14959  /**
14960   * Called when this SeekBar has focus and a key gets pressed down. By
14961   * default it will call `this.handleAction` when the key is space or enter.
14962   *
14963   * @param {EventTarget~Event} event
14964   *        The `keydown` event that caused this function to be called.
14965   *
14966   * @listens keydown
14967   */
14968
14969
14970  SeekBar.prototype.handleKeyPress = function handleKeyPress(event) {
14971
14972    // Support Space (32) or Enter (13) key operation to fire a click event
14973    if (event.which === 32 || event.which === 13) {
14974      event.preventDefault();
14975      this.handleAction(event);
14976    } else if (_Slider.prototype.handleKeyPress) {
14977
14978      // Pass keypress handling up for unsupported keys
14979      _Slider.prototype.handleKeyPress.call(this, event);
14980    }
14981  };
14982
14983  return SeekBar;
14984}(Slider);
14985
14986/**
14987 * Default options for the `SeekBar`
14988 *
14989 * @type {Object}
14990 * @private
14991 */
14992
14993
14994SeekBar.prototype.options_ = {
14995  children: ['loadProgressBar', 'playProgressBar'],
14996  barName: 'playProgressBar'
14997};
14998
14999// MouseTimeDisplay tooltips should not be added to a player on mobile devices or IE8
15000if ((!IE_VERSION || IE_VERSION > 8) && !IS_IOS && !IS_ANDROID) {
15001  SeekBar.prototype.options_.children.splice(1, 0, 'mouseTimeDisplay');
15002}
15003
15004/**
15005 * Call the update event for this Slider when this event happens on the player.
15006 *
15007 * @type {string}
15008 */
15009SeekBar.prototype.playerEvent = 'timeupdate';
15010
15011Component.registerComponent('SeekBar', SeekBar);
15012
15013/**
15014 * @file progress-control.js
15015 */
15016/**
15017 * The Progress Control component contains the seek bar, load progress,
15018 * and play progress.
15019 *
15020 * @extends Component
15021 */
15022
15023var ProgressControl = function (_Component) {
15024  inherits(ProgressControl, _Component);
15025
15026  /**
15027   * Creates an instance of this class.
15028   *
15029   * @param {Player} player
15030   *        The `Player` that this class should be attached to.
15031   *
15032   * @param {Object} [options]
15033   *        The key/value store of player options.
15034   */
15035  function ProgressControl(player, options) {
15036    classCallCheck(this, ProgressControl);
15037
15038    var _this = possibleConstructorReturn(this, _Component.call(this, player, options));
15039
15040    _this.handleMouseMove = throttle(bind(_this, _this.handleMouseMove), 25);
15041    _this.throttledHandleMouseSeek = throttle(bind(_this, _this.handleMouseSeek), 25);
15042
15043    _this.enable();
15044    return _this;
15045  }
15046
15047  /**
15048   * Create the `Component`'s DOM element
15049   *
15050   * @return {Element}
15051   *         The element that was created.
15052   */
15053
15054
15055  ProgressControl.prototype.createEl = function createEl$$1() {
15056    return _Component.prototype.createEl.call(this, 'div', {
15057      className: 'vjs-progress-control vjs-control'
15058    });
15059  };
15060
15061  /**
15062   * When the mouse moves over the `ProgressControl`, the pointer position
15063   * gets passed down to the `MouseTimeDisplay` component.
15064   *
15065   * @param {EventTarget~Event} event
15066   *        The `mousemove` event that caused this function to run.
15067   *
15068   * @listen mousemove
15069   */
15070
15071
15072  ProgressControl.prototype.handleMouseMove = function handleMouseMove(event) {
15073    var seekBar = this.getChild('seekBar');
15074
15075    if (seekBar) {
15076      var mouseTimeDisplay = seekBar.getChild('mouseTimeDisplay');
15077      var seekBarEl = seekBar.el();
15078      var seekBarRect = getBoundingClientRect(seekBarEl);
15079      var seekBarPoint = getPointerPosition(seekBarEl, event).x;
15080
15081      // The default skin has a gap on either side of the `SeekBar`. This means
15082      // that it's possible to trigger this behavior outside the boundaries of
15083      // the `SeekBar`. This ensures we stay within it at all times.
15084      if (seekBarPoint > 1) {
15085        seekBarPoint = 1;
15086      } else if (seekBarPoint < 0) {
15087        seekBarPoint = 0;
15088      }
15089
15090      if (mouseTimeDisplay) {
15091        mouseTimeDisplay.update(seekBarRect, seekBarPoint);
15092      }
15093    }
15094  };
15095
15096  /**
15097   * A throttled version of the {@link ProgressControl#handleMouseSeek} listener.
15098   *
15099   * @method ProgressControl#throttledHandleMouseSeek
15100   * @param {EventTarget~Event} event
15101   *        The `mousemove` event that caused this function to run.
15102   *
15103   * @listen mousemove
15104   * @listen touchmove
15105   */
15106
15107  /**
15108   * Handle `mousemove` or `touchmove` events on the `ProgressControl`.
15109   *
15110   * @param {EventTarget~Event} event
15111   *        `mousedown` or `touchstart` event that triggered this function
15112   *
15113   * @listens mousemove
15114   * @listens touchmove
15115   */
15116
15117
15118  ProgressControl.prototype.handleMouseSeek = function handleMouseSeek(event) {
15119    var seekBar = this.getChild('seekBar');
15120
15121    if (seekBar) {
15122      seekBar.handleMouseMove(event);
15123    }
15124  };
15125
15126  /**
15127   * Are controls are currently enabled for this progress control.
15128   *
15129   * @return {boolean}
15130   *         true if controls are enabled, false otherwise
15131   */
15132
15133
15134  ProgressControl.prototype.enabled = function enabled() {
15135    return this.enabled_;
15136  };
15137
15138  /**
15139   * Disable all controls on the progress control and its children
15140   */
15141
15142
15143  ProgressControl.prototype.disable = function disable() {
15144    this.children().forEach(function (child) {
15145      return child.disable && child.disable();
15146    });
15147
15148    if (!this.enabled()) {
15149      return;
15150    }
15151
15152    this.off(['mousedown', 'touchstart'], this.handleMouseDown);
15153    this.off(this.el_, 'mousemove', this.handleMouseMove);
15154    this.handleMouseUp();
15155
15156    this.addClass('disabled');
15157
15158    this.enabled_ = false;
15159  };
15160
15161  /**
15162   * Enable all controls on the progress control and its children
15163   */
15164
15165
15166  ProgressControl.prototype.enable = function enable() {
15167    this.children().forEach(function (child) {
15168      return child.enable && child.enable();
15169    });
15170
15171    if (this.enabled()) {
15172      return;
15173    }
15174
15175    this.on(['mousedown', 'touchstart'], this.handleMouseDown);
15176    this.on(this.el_, 'mousemove', this.handleMouseMove);
15177    this.removeClass('disabled');
15178
15179    this.enabled_ = true;
15180  };
15181
15182  /**
15183   * Handle `mousedown` or `touchstart` events on the `ProgressControl`.
15184   *
15185   * @param {EventTarget~Event} event
15186   *        `mousedown` or `touchstart` event that triggered this function
15187   *
15188   * @listens mousedown
15189   * @listens touchstart
15190   */
15191
15192
15193  ProgressControl.prototype.handleMouseDown = function handleMouseDown(event) {
15194    var doc = this.el_.ownerDocument;
15195    var seekBar = this.getChild('seekBar');
15196
15197    if (seekBar) {
15198      seekBar.handleMouseDown(event);
15199    }
15200
15201    this.on(doc, 'mousemove', this.throttledHandleMouseSeek);
15202    this.on(doc, 'touchmove', this.throttledHandleMouseSeek);
15203    this.on(doc, 'mouseup', this.handleMouseUp);
15204    this.on(doc, 'touchend', this.handleMouseUp);
15205  };
15206
15207  /**
15208   * Handle `mouseup` or `touchend` events on the `ProgressControl`.
15209   *
15210   * @param {EventTarget~Event} event
15211   *        `mouseup` or `touchend` event that triggered this function.
15212   *
15213   * @listens touchend
15214   * @listens mouseup
15215   */
15216
15217
15218  ProgressControl.prototype.handleMouseUp = function handleMouseUp(event) {
15219    var doc = this.el_.ownerDocument;
15220    var seekBar = this.getChild('seekBar');
15221
15222    if (seekBar) {
15223      seekBar.handleMouseUp(event);
15224    }
15225
15226    this.off(doc, 'mousemove', this.throttledHandleMouseSeek);
15227    this.off(doc, 'touchmove', this.throttledHandleMouseSeek);
15228    this.off(doc, 'mouseup', this.handleMouseUp);
15229    this.off(doc, 'touchend', this.handleMouseUp);
15230  };
15231
15232  return ProgressControl;
15233}(Component);
15234
15235/**
15236 * Default options for `ProgressControl`
15237 *
15238 * @type {Object}
15239 * @private
15240 */
15241
15242
15243ProgressControl.prototype.options_ = {
15244  children: ['seekBar']
15245};
15246
15247Component.registerComponent('ProgressControl', ProgressControl);
15248
15249/**
15250 * @file fullscreen-toggle.js
15251 */
15252/**
15253 * Toggle fullscreen video
15254 *
15255 * @extends Button
15256 */
15257
15258var FullscreenToggle = function (_Button) {
15259  inherits(FullscreenToggle, _Button);
15260
15261  /**
15262   * Creates an instance of this class.
15263   *
15264   * @param {Player} player
15265   *        The `Player` that this class should be attached to.
15266   *
15267   * @param {Object} [options]
15268   *        The key/value store of player options.
15269   */
15270  function FullscreenToggle(player, options) {
15271    classCallCheck(this, FullscreenToggle);
15272
15273    var _this = possibleConstructorReturn(this, _Button.call(this, player, options));
15274
15275    _this.on(player, 'fullscreenchange', _this.handleFullscreenChange);
15276
15277    if (document_1[FullscreenApi.fullscreenEnabled] === false) {
15278      _this.disable();
15279    }
15280    return _this;
15281  }
15282
15283  /**
15284   * Builds the default DOM `className`.
15285   *
15286   * @return {string}
15287   *         The DOM `className` for this object.
15288   */
15289
15290
15291  FullscreenToggle.prototype.buildCSSClass = function buildCSSClass() {
15292    return 'vjs-fullscreen-control ' + _Button.prototype.buildCSSClass.call(this);
15293  };
15294
15295  /**
15296   * Handles fullscreenchange on the player and change control text accordingly.
15297   *
15298   * @param {EventTarget~Event} [event]
15299   *        The {@link Player#fullscreenchange} event that caused this function to be
15300   *        called.
15301   *
15302   * @listens Player#fullscreenchange
15303   */
15304
15305
15306  FullscreenToggle.prototype.handleFullscreenChange = function handleFullscreenChange(event) {
15307    if (this.player_.isFullscreen()) {
15308      this.controlText('Non-Fullscreen');
15309    } else {
15310      this.controlText('Fullscreen');
15311    }
15312  };
15313
15314  /**
15315   * This gets called when an `FullscreenToggle` is "clicked". See
15316   * {@link ClickableComponent} for more detailed information on what a click can be.
15317   *
15318   * @param {EventTarget~Event} [event]
15319   *        The `keydown`, `tap`, or `click` event that caused this function to be
15320   *        called.
15321   *
15322   * @listens tap
15323   * @listens click
15324   */
15325
15326
15327  FullscreenToggle.prototype.handleClick = function handleClick(event) {
15328    if (!this.player_.isFullscreen()) {
15329      this.player_.requestFullscreen();
15330    } else {
15331      this.player_.exitFullscreen();
15332    }
15333  };
15334
15335  return FullscreenToggle;
15336}(Button);
15337
15338/**
15339 * The text that should display over the `FullscreenToggle`s controls. Added for localization.
15340 *
15341 * @type {string}
15342 * @private
15343 */
15344
15345
15346FullscreenToggle.prototype.controlText_ = 'Fullscreen';
15347
15348Component.registerComponent('FullscreenToggle', FullscreenToggle);
15349
15350/**
15351 * Check if volume control is supported and if it isn't hide the
15352 * `Component` that was passed  using the `vjs-hidden` class.
15353 *
15354 * @param {Component} self
15355 *        The component that should be hidden if volume is unsupported
15356 *
15357 * @param {Player} player
15358 *        A reference to the player
15359 *
15360 * @private
15361 */
15362var checkVolumeSupport = function checkVolumeSupport(self, player) {
15363  // hide volume controls when they're not supported by the current tech
15364  if (player.tech_ && !player.tech_.featuresVolumeControl) {
15365    self.addClass('vjs-hidden');
15366  }
15367
15368  self.on(player, 'loadstart', function () {
15369    if (!player.tech_.featuresVolumeControl) {
15370      self.addClass('vjs-hidden');
15371    } else {
15372      self.removeClass('vjs-hidden');
15373    }
15374  });
15375};
15376
15377/**
15378 * @file volume-level.js
15379 */
15380/**
15381 * Shows volume level
15382 *
15383 * @extends Component
15384 */
15385
15386var VolumeLevel = function (_Component) {
15387  inherits(VolumeLevel, _Component);
15388
15389  function VolumeLevel() {
15390    classCallCheck(this, VolumeLevel);
15391    return possibleConstructorReturn(this, _Component.apply(this, arguments));
15392  }
15393
15394  /**
15395   * Create the `Component`'s DOM element
15396   *
15397   * @return {Element}
15398   *         The element that was created.
15399   */
15400  VolumeLevel.prototype.createEl = function createEl() {
15401    return _Component.prototype.createEl.call(this, 'div', {
15402      className: 'vjs-volume-level',
15403      innerHTML: '<span class="vjs-control-text"></span>'
15404    });
15405  };
15406
15407  return VolumeLevel;
15408}(Component);
15409
15410Component.registerComponent('VolumeLevel', VolumeLevel);
15411
15412/**
15413 * @file volume-bar.js
15414 */
15415// Required children
15416/**
15417 * The bar that contains the volume level and can be clicked on to adjust the level
15418 *
15419 * @extends Slider
15420 */
15421
15422var VolumeBar = function (_Slider) {
15423  inherits(VolumeBar, _Slider);
15424
15425  /**
15426   * Creates an instance of this class.
15427   *
15428   * @param {Player} player
15429   *        The `Player` that this class should be attached to.
15430   *
15431   * @param {Object} [options]
15432   *        The key/value store of player options.
15433   */
15434  function VolumeBar(player, options) {
15435    classCallCheck(this, VolumeBar);
15436
15437    var _this = possibleConstructorReturn(this, _Slider.call(this, player, options));
15438
15439    _this.on('slideractive', _this.updateLastVolume_);
15440    _this.on(player, 'volumechange', _this.updateARIAAttributes);
15441    player.ready(function () {
15442      return _this.updateARIAAttributes();
15443    });
15444    return _this;
15445  }
15446
15447  /**
15448   * Create the `Component`'s DOM element
15449   *
15450   * @return {Element}
15451   *         The element that was created.
15452   */
15453
15454
15455  VolumeBar.prototype.createEl = function createEl$$1() {
15456    return _Slider.prototype.createEl.call(this, 'div', {
15457      className: 'vjs-volume-bar vjs-slider-bar'
15458    }, {
15459      'aria-label': this.localize('Volume Level'),
15460      'aria-live': 'polite'
15461    });
15462  };
15463
15464  /**
15465   * Handle mouse down on volume bar
15466   *
15467   * @param {EventTarget~Event} event
15468   *        The `mousedown` event that caused this to run.
15469   *
15470   * @listens mousedown
15471   */
15472
15473
15474  VolumeBar.prototype.handleMouseDown = function handleMouseDown(event) {
15475    if (!isSingleLeftClick(event)) {
15476      return;
15477    }
15478
15479    _Slider.prototype.handleMouseDown.call(this, event);
15480  };
15481
15482  /**
15483   * Handle movement events on the {@link VolumeMenuButton}.
15484   *
15485   * @param {EventTarget~Event} event
15486   *        The event that caused this function to run.
15487   *
15488   * @listens mousemove
15489   */
15490
15491
15492  VolumeBar.prototype.handleMouseMove = function handleMouseMove(event) {
15493    if (!isSingleLeftClick(event)) {
15494      return;
15495    }
15496
15497    this.checkMuted();
15498    this.player_.volume(this.calculateDistance(event));
15499  };
15500
15501  /**
15502   * If the player is muted unmute it.
15503   */
15504
15505
15506  VolumeBar.prototype.checkMuted = function checkMuted() {
15507    if (this.player_.muted()) {
15508      this.player_.muted(false);
15509    }
15510  };
15511
15512  /**
15513   * Get percent of volume level
15514   *
15515   * @return {number}
15516   *         Volume level percent as a decimal number.
15517   */
15518
15519
15520  VolumeBar.prototype.getPercent = function getPercent() {
15521    if (this.player_.muted()) {
15522      return 0;
15523    }
15524    return this.player_.volume();
15525  };
15526
15527  /**
15528   * Increase volume level for keyboard users
15529   */
15530
15531
15532  VolumeBar.prototype.stepForward = function stepForward() {
15533    this.checkMuted();
15534    this.player_.volume(this.player_.volume() + 0.1);
15535  };
15536
15537  /**
15538   * Decrease volume level for keyboard users
15539   */
15540
15541
15542  VolumeBar.prototype.stepBack = function stepBack() {
15543    this.checkMuted();
15544    this.player_.volume(this.player_.volume() - 0.1);
15545  };
15546
15547  /**
15548   * Update ARIA accessibility attributes
15549   *
15550   * @param {EventTarget~Event} [event]
15551   *        The `volumechange` event that caused this function to run.
15552   *
15553   * @listens Player#volumechange
15554   */
15555
15556
15557  VolumeBar.prototype.updateARIAAttributes = function updateARIAAttributes(event) {
15558    var ariaValue = this.player_.muted() ? 0 : this.volumeAsPercentage_();
15559
15560    this.el_.setAttribute('aria-valuenow', ariaValue);
15561    this.el_.setAttribute('aria-valuetext', ariaValue + '%');
15562  };
15563
15564  /**
15565   * Returns the current value of the player volume as a percentage
15566   *
15567   * @private
15568   */
15569
15570
15571  VolumeBar.prototype.volumeAsPercentage_ = function volumeAsPercentage_() {
15572    return Math.round(this.player_.volume() * 100);
15573  };
15574
15575  /**
15576   * When user starts dragging the VolumeBar, store the volume and listen for
15577   * the end of the drag. When the drag ends, if the volume was set to zero,
15578   * set lastVolume to the stored volume.
15579   *
15580   * @listens slideractive
15581   * @private
15582   */
15583
15584
15585  VolumeBar.prototype.updateLastVolume_ = function updateLastVolume_() {
15586    var _this2 = this;
15587
15588    var volumeBeforeDrag = this.player_.volume();
15589
15590    this.one('sliderinactive', function () {
15591      if (_this2.player_.volume() === 0) {
15592        _this2.player_.lastVolume_(volumeBeforeDrag);
15593      }
15594    });
15595  };
15596
15597  return VolumeBar;
15598}(Slider);
15599
15600/**
15601 * Default options for the `VolumeBar`
15602 *
15603 * @type {Object}
15604 * @private
15605 */
15606
15607
15608VolumeBar.prototype.options_ = {
15609  children: ['volumeLevel'],
15610  barName: 'volumeLevel'
15611};
15612
15613/**
15614 * Call the update event for this Slider when this event happens on the player.
15615 *
15616 * @type {string}
15617 */
15618VolumeBar.prototype.playerEvent = 'volumechange';
15619
15620Component.registerComponent('VolumeBar', VolumeBar);
15621
15622/**
15623 * @file volume-control.js
15624 */
15625// Required children
15626/**
15627 * The component for controlling the volume level
15628 *
15629 * @extends Component
15630 */
15631
15632var VolumeControl = function (_Component) {
15633  inherits(VolumeControl, _Component);
15634
15635  /**
15636   * Creates an instance of this class.
15637   *
15638   * @param {Player} player
15639   *        The `Player` that this class should be attached to.
15640   *
15641   * @param {Object} [options={}]
15642   *        The key/value store of player options.
15643   */
15644  function VolumeControl(player) {
15645    var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
15646    classCallCheck(this, VolumeControl);
15647
15648    options.vertical = options.vertical || false;
15649
15650    // Pass the vertical option down to the VolumeBar if
15651    // the VolumeBar is turned on.
15652    if (typeof options.volumeBar === 'undefined' || isPlain(options.volumeBar)) {
15653      options.volumeBar = options.volumeBar || {};
15654      options.volumeBar.vertical = options.vertical;
15655    }
15656
15657    // hide this control if volume support is missing
15658    var _this = possibleConstructorReturn(this, _Component.call(this, player, options));
15659
15660    checkVolumeSupport(_this, player);
15661
15662    _this.throttledHandleMouseMove = throttle(bind(_this, _this.handleMouseMove), 25);
15663
15664    _this.on('mousedown', _this.handleMouseDown);
15665    _this.on('touchstart', _this.handleMouseDown);
15666
15667    // while the slider is active (the mouse has been pressed down and
15668    // is dragging) or in focus we do not want to hide the VolumeBar
15669    _this.on(_this.volumeBar, ['focus', 'slideractive'], function () {
15670      _this.volumeBar.addClass('vjs-slider-active');
15671      _this.addClass('vjs-slider-active');
15672      _this.trigger('slideractive');
15673    });
15674
15675    _this.on(_this.volumeBar, ['blur', 'sliderinactive'], function () {
15676      _this.volumeBar.removeClass('vjs-slider-active');
15677      _this.removeClass('vjs-slider-active');
15678      _this.trigger('sliderinactive');
15679    });
15680    return _this;
15681  }
15682
15683  /**
15684   * Create the `Component`'s DOM element
15685   *
15686   * @return {Element}
15687   *         The element that was created.
15688   */
15689
15690
15691  VolumeControl.prototype.createEl = function createEl() {
15692    var orientationClass = 'vjs-volume-horizontal';
15693
15694    if (this.options_.vertical) {
15695      orientationClass = 'vjs-volume-vertical';
15696    }
15697
15698    return _Component.prototype.createEl.call(this, 'div', {
15699      className: 'vjs-volume-control vjs-control ' + orientationClass
15700    });
15701  };
15702
15703  /**
15704   * Handle `mousedown` or `touchstart` events on the `VolumeControl`.
15705   *
15706   * @param {EventTarget~Event} event
15707   *        `mousedown` or `touchstart` event that triggered this function
15708   *
15709   * @listens mousedown
15710   * @listens touchstart
15711   */
15712
15713
15714  VolumeControl.prototype.handleMouseDown = function handleMouseDown(event) {
15715    var doc = this.el_.ownerDocument;
15716
15717    this.on(doc, 'mousemove', this.throttledHandleMouseMove);
15718    this.on(doc, 'touchmove', this.throttledHandleMouseMove);
15719    this.on(doc, 'mouseup', this.handleMouseUp);
15720    this.on(doc, 'touchend', this.handleMouseUp);
15721  };
15722
15723  /**
15724   * Handle `mouseup` or `touchend` events on the `VolumeControl`.
15725   *
15726   * @param {EventTarget~Event} event
15727   *        `mouseup` or `touchend` event that triggered this function.
15728   *
15729   * @listens touchend
15730   * @listens mouseup
15731   */
15732
15733
15734  VolumeControl.prototype.handleMouseUp = function handleMouseUp(event) {
15735    var doc = this.el_.ownerDocument;
15736
15737    this.off(doc, 'mousemove', this.throttledHandleMouseMove);
15738    this.off(doc, 'touchmove', this.throttledHandleMouseMove);
15739    this.off(doc, 'mouseup', this.handleMouseUp);
15740    this.off(doc, 'touchend', this.handleMouseUp);
15741  };
15742
15743  /**
15744   * Handle `mousedown` or `touchstart` events on the `VolumeControl`.
15745   *
15746   * @param {EventTarget~Event} event
15747   *        `mousedown` or `touchstart` event that triggered this function
15748   *
15749   * @listens mousedown
15750   * @listens touchstart
15751   */
15752
15753
15754  VolumeControl.prototype.handleMouseMove = function handleMouseMove(event) {
15755    this.volumeBar.handleMouseMove(event);
15756  };
15757
15758  return VolumeControl;
15759}(Component);
15760
15761/**
15762 * Default options for the `VolumeControl`
15763 *
15764 * @type {Object}
15765 * @private
15766 */
15767
15768
15769VolumeControl.prototype.options_ = {
15770  children: ['volumeBar']
15771};
15772
15773Component.registerComponent('VolumeControl', VolumeControl);
15774
15775/**
15776 * Check if muting volume is supported and if it isn't hide the mute toggle
15777 * button.
15778 *
15779 * @param {Component} self
15780 *        A reference to the mute toggle button
15781 *
15782 * @param {Player} player
15783 *        A reference to the player
15784 *
15785 * @private
15786 */
15787var checkMuteSupport = function checkMuteSupport(self, player) {
15788  // hide mute toggle button if it's not supported by the current tech
15789  if (player.tech_ && !player.tech_.featuresMuteControl) {
15790    self.addClass('vjs-hidden');
15791  }
15792
15793  self.on(player, 'loadstart', function () {
15794    if (!player.tech_.featuresMuteControl) {
15795      self.addClass('vjs-hidden');
15796    } else {
15797      self.removeClass('vjs-hidden');
15798    }
15799  });
15800};
15801
15802/**
15803 * @file mute-toggle.js
15804 */
15805/**
15806 * A button component for muting the audio.
15807 *
15808 * @extends Button
15809 */
15810
15811var MuteToggle = function (_Button) {
15812  inherits(MuteToggle, _Button);
15813
15814  /**
15815   * Creates an instance of this class.
15816   *
15817   * @param {Player} player
15818   *        The `Player` that this class should be attached to.
15819   *
15820   * @param {Object} [options]
15821   *        The key/value store of player options.
15822   */
15823  function MuteToggle(player, options) {
15824    classCallCheck(this, MuteToggle);
15825
15826    // hide this control if volume support is missing
15827    var _this = possibleConstructorReturn(this, _Button.call(this, player, options));
15828
15829    checkMuteSupport(_this, player);
15830
15831    _this.on(player, ['loadstart', 'volumechange'], _this.update);
15832    return _this;
15833  }
15834
15835  /**
15836   * Builds the default DOM `className`.
15837   *
15838   * @return {string}
15839   *         The DOM `className` for this object.
15840   */
15841
15842
15843  MuteToggle.prototype.buildCSSClass = function buildCSSClass() {
15844    return 'vjs-mute-control ' + _Button.prototype.buildCSSClass.call(this);
15845  };
15846
15847  /**
15848   * This gets called when an `MuteToggle` is "clicked". See
15849   * {@link ClickableComponent} for more detailed information on what a click can be.
15850   *
15851   * @param {EventTarget~Event} [event]
15852   *        The `keydown`, `tap`, or `click` event that caused this function to be
15853   *        called.
15854   *
15855   * @listens tap
15856   * @listens click
15857   */
15858
15859
15860  MuteToggle.prototype.handleClick = function handleClick(event) {
15861    var vol = this.player_.volume();
15862    var lastVolume = this.player_.lastVolume_();
15863
15864    if (vol === 0) {
15865      var volumeToSet = lastVolume < 0.1 ? 0.1 : lastVolume;
15866
15867      this.player_.volume(volumeToSet);
15868      this.player_.muted(false);
15869    } else {
15870      this.player_.muted(this.player_.muted() ? false : true);
15871    }
15872  };
15873
15874  /**
15875   * Update the `MuteToggle` button based on the state of `volume` and `muted`
15876   * on the player.
15877   *
15878   * @param {EventTarget~Event} [event]
15879   *        The {@link Player#loadstart} event if this function was called
15880   *        through an event.
15881   *
15882   * @listens Player#loadstart
15883   * @listens Player#volumechange
15884   */
15885
15886
15887  MuteToggle.prototype.update = function update(event) {
15888    this.updateIcon_();
15889    this.updateControlText_();
15890  };
15891
15892  /**
15893   * Update the appearance of the `MuteToggle` icon.
15894   *
15895   * Possible states (given `level` variable below):
15896   * - 0: crossed out
15897   * - 1: zero bars of volume
15898   * - 2: one bar of volume
15899   * - 3: two bars of volume
15900   *
15901   * @private
15902   */
15903
15904
15905  MuteToggle.prototype.updateIcon_ = function updateIcon_() {
15906    var vol = this.player_.volume();
15907    var level = 3;
15908
15909    // in iOS when a player is loaded with muted attribute
15910    // and volume is changed with a native mute button
15911    // we want to make sure muted state is updated
15912    if (IS_IOS) {
15913      this.player_.muted(this.player_.tech_.el_.muted);
15914    }
15915
15916    if (vol === 0 || this.player_.muted()) {
15917      level = 0;
15918    } else if (vol < 0.33) {
15919      level = 1;
15920    } else if (vol < 0.67) {
15921      level = 2;
15922    }
15923
15924    // TODO improve muted icon classes
15925    for (var i = 0; i < 4; i++) {
15926      removeClass(this.el_, 'vjs-vol-' + i);
15927    }
15928    addClass(this.el_, 'vjs-vol-' + level);
15929  };
15930
15931  /**
15932   * If `muted` has changed on the player, update the control text
15933   * (`title` attribute on `vjs-mute-control` element and content of
15934   * `vjs-control-text` element).
15935   *
15936   * @private
15937   */
15938
15939
15940  MuteToggle.prototype.updateControlText_ = function updateControlText_() {
15941    var soundOff = this.player_.muted() || this.player_.volume() === 0;
15942    var text = soundOff ? 'Unmute' : 'Mute';
15943
15944    if (this.controlText() !== text) {
15945      this.controlText(text);
15946    }
15947  };
15948
15949  return MuteToggle;
15950}(Button);
15951
15952/**
15953 * The text that should display over the `MuteToggle`s controls. Added for localization.
15954 *
15955 * @type {string}
15956 * @private
15957 */
15958
15959
15960MuteToggle.prototype.controlText_ = 'Mute';
15961
15962Component.registerComponent('MuteToggle', MuteToggle);
15963
15964/**
15965 * @file volume-control.js
15966 */
15967// Required children
15968/**
15969 * A Component to contain the MuteToggle and VolumeControl so that
15970 * they can work together.
15971 *
15972 * @extends Component
15973 */
15974
15975var VolumePanel = function (_Component) {
15976  inherits(VolumePanel, _Component);
15977
15978  /**
15979   * Creates an instance of this class.
15980   *
15981   * @param {Player} player
15982   *        The `Player` that this class should be attached to.
15983   *
15984   * @param {Object} [options={}]
15985   *        The key/value store of player options.
15986   */
15987  function VolumePanel(player) {
15988    var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
15989    classCallCheck(this, VolumePanel);
15990
15991    if (typeof options.inline !== 'undefined') {
15992      options.inline = options.inline;
15993    } else {
15994      options.inline = true;
15995    }
15996
15997    // pass the inline option down to the VolumeControl as vertical if
15998    // the VolumeControl is on.
15999    if (typeof options.volumeControl === 'undefined' || isPlain(options.volumeControl)) {
16000      options.volumeControl = options.volumeControl || {};
16001      options.volumeControl.vertical = !options.inline;
16002    }
16003
16004    var _this = possibleConstructorReturn(this, _Component.call(this, player, options));
16005
16006    _this.on(player, ['loadstart'], _this.volumePanelState_);
16007
16008    // while the slider is active (the mouse has been pressed down and
16009    // is dragging) we do not want to hide the VolumeBar
16010    _this.on(_this.volumeControl, ['slideractive'], _this.sliderActive_);
16011
16012    _this.on(_this.volumeControl, ['sliderinactive'], _this.sliderInactive_);
16013    return _this;
16014  }
16015
16016  /**
16017   * Add vjs-slider-active class to the VolumePanel
16018   *
16019   * @listens VolumeControl#slideractive
16020   * @private
16021   */
16022
16023
16024  VolumePanel.prototype.sliderActive_ = function sliderActive_() {
16025    this.addClass('vjs-slider-active');
16026  };
16027
16028  /**
16029   * Removes vjs-slider-active class to the VolumePanel
16030   *
16031   * @listens VolumeControl#sliderinactive
16032   * @private
16033   */
16034
16035
16036  VolumePanel.prototype.sliderInactive_ = function sliderInactive_() {
16037    this.removeClass('vjs-slider-active');
16038  };
16039
16040  /**
16041   * Adds vjs-hidden or vjs-mute-toggle-only to the VolumePanel
16042   * depending on MuteToggle and VolumeControl state
16043   *
16044   * @listens Player#loadstart
16045   * @private
16046   */
16047
16048
16049  VolumePanel.prototype.volumePanelState_ = function volumePanelState_() {
16050    // hide volume panel if neither volume control or mute toggle
16051    // are displayed
16052    if (this.volumeControl.hasClass('vjs-hidden') && this.muteToggle.hasClass('vjs-hidden')) {
16053      this.addClass('vjs-hidden');
16054    }
16055
16056    // if only mute toggle is visible we don't want
16057    // volume panel expanding when hovered or active
16058    if (this.volumeControl.hasClass('vjs-hidden') && !this.muteToggle.hasClass('vjs-hidden')) {
16059      this.addClass('vjs-mute-toggle-only');
16060    }
16061  };
16062
16063  /**
16064   * Create the `Component`'s DOM element
16065   *
16066   * @return {Element}
16067   *         The element that was created.
16068   */
16069
16070
16071  VolumePanel.prototype.createEl = function createEl() {
16072    var orientationClass = 'vjs-volume-panel-horizontal';
16073
16074    if (!this.options_.inline) {
16075      orientationClass = 'vjs-volume-panel-vertical';
16076    }
16077
16078    return _Component.prototype.createEl.call(this, 'div', {
16079      className: 'vjs-volume-panel vjs-control ' + orientationClass
16080    });
16081  };
16082
16083  return VolumePanel;
16084}(Component);
16085
16086/**
16087 * Default options for the `VolumeControl`
16088 *
16089 * @type {Object}
16090 * @private
16091 */
16092
16093
16094VolumePanel.prototype.options_ = {
16095  children: ['muteToggle', 'volumeControl']
16096};
16097
16098Component.registerComponent('VolumePanel', VolumePanel);
16099
16100/**
16101 * @file menu.js
16102 */
16103/**
16104 * The Menu component is used to build popup menus, including subtitle and
16105 * captions selection menus.
16106 *
16107 * @extends Component
16108 */
16109
16110var Menu = function (_Component) {
16111  inherits(Menu, _Component);
16112
16113  /**
16114   * Create an instance of this class.
16115   *
16116   * @param {Player} player
16117   *        the player that this component should attach to
16118   *
16119   * @param {Object} [options]
16120   *        Object of option names and values
16121   *
16122   */
16123  function Menu(player, options) {
16124    classCallCheck(this, Menu);
16125
16126    var _this = possibleConstructorReturn(this, _Component.call(this, player, options));
16127
16128    if (options) {
16129      _this.menuButton_ = options.menuButton;
16130    }
16131
16132    _this.focusedChild_ = -1;
16133
16134    _this.on('keydown', _this.handleKeyPress);
16135    return _this;
16136  }
16137
16138  /**
16139   * Add a {@link MenuItem} to the menu.
16140   *
16141   * @param {Object|string} component
16142   *        The name or instance of the `MenuItem` to add.
16143   *
16144   */
16145
16146
16147  Menu.prototype.addItem = function addItem(component) {
16148    this.addChild(component);
16149    component.on('click', bind(this, function (event) {
16150      // Unpress the associated MenuButton, and move focus back to it
16151      if (this.menuButton_) {
16152        this.menuButton_.unpressButton();
16153
16154        // don't focus menu button if item is a caption settings item
16155        // because focus will move elsewhere and it logs an error on IE8
16156        if (component.name() !== 'CaptionSettingsMenuItem') {
16157          this.menuButton_.focus();
16158        }
16159      }
16160    }));
16161  };
16162
16163  /**
16164   * Create the `Menu`s DOM element.
16165   *
16166   * @return {Element}
16167   *         the element that was created
16168   */
16169
16170
16171  Menu.prototype.createEl = function createEl$$1() {
16172    var contentElType = this.options_.contentElType || 'ul';
16173
16174    this.contentEl_ = createEl(contentElType, {
16175      className: 'vjs-menu-content'
16176    });
16177
16178    this.contentEl_.setAttribute('role', 'menu');
16179
16180    var el = _Component.prototype.createEl.call(this, 'div', {
16181      append: this.contentEl_,
16182      className: 'vjs-menu'
16183    });
16184
16185    el.appendChild(this.contentEl_);
16186
16187    // Prevent clicks from bubbling up. Needed for Menu Buttons,
16188    // where a click on the parent is significant
16189    on(el, 'click', function (event) {
16190      event.preventDefault();
16191      event.stopImmediatePropagation();
16192    });
16193
16194    return el;
16195  };
16196
16197  Menu.prototype.dispose = function dispose() {
16198    this.contentEl_ = null;
16199
16200    _Component.prototype.dispose.call(this);
16201  };
16202
16203  /**
16204   * Handle a `keydown` event on this menu. This listener is added in the constructor.
16205   *
16206   * @param {EventTarget~Event} event
16207   *        A `keydown` event that happened on the menu.
16208   *
16209   * @listens keydown
16210   */
16211
16212
16213  Menu.prototype.handleKeyPress = function handleKeyPress(event) {
16214    // Left and Down Arrows
16215    if (event.which === 37 || event.which === 40) {
16216      event.preventDefault();
16217      this.stepForward();
16218
16219      // Up and Right Arrows
16220    } else if (event.which === 38 || event.which === 39) {
16221      event.preventDefault();
16222      this.stepBack();
16223    }
16224  };
16225
16226  /**
16227   * Move to next (lower) menu item for keyboard users.
16228   */
16229
16230
16231  Menu.prototype.stepForward = function stepForward() {
16232    var stepChild = 0;
16233
16234    if (this.focusedChild_ !== undefined) {
16235      stepChild = this.focusedChild_ + 1;
16236    }
16237    this.focus(stepChild);
16238  };
16239
16240  /**
16241   * Move to previous (higher) menu item for keyboard users.
16242   */
16243
16244
16245  Menu.prototype.stepBack = function stepBack() {
16246    var stepChild = 0;
16247
16248    if (this.focusedChild_ !== undefined) {
16249      stepChild = this.focusedChild_ - 1;
16250    }
16251    this.focus(stepChild);
16252  };
16253
16254  /**
16255   * Set focus on a {@link MenuItem} in the `Menu`.
16256   *
16257   * @param {Object|string} [item=0]
16258   *        Index of child item set focus on.
16259   */
16260
16261
16262  Menu.prototype.focus = function focus() {
16263    var item = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 0;
16264
16265    var children = this.children().slice();
16266    var haveTitle = children.length && children[0].className && /vjs-menu-title/.test(children[0].className);
16267
16268    if (haveTitle) {
16269      children.shift();
16270    }
16271
16272    if (children.length > 0) {
16273      if (item < 0) {
16274        item = 0;
16275      } else if (item >= children.length) {
16276        item = children.length - 1;
16277      }
16278
16279      this.focusedChild_ = item;
16280
16281      children[item].el_.focus();
16282    }
16283  };
16284
16285  return Menu;
16286}(Component);
16287
16288Component.registerComponent('Menu', Menu);
16289
16290/**
16291 * @file menu-button.js
16292 */
16293/**
16294 * A `MenuButton` class for any popup {@link Menu}.
16295 *
16296 * @extends Component
16297 */
16298
16299var MenuButton = function (_Component) {
16300  inherits(MenuButton, _Component);
16301
16302  /**
16303   * Creates an instance of this class.
16304   *
16305   * @param {Player} player
16306   *        The `Player` that this class should be attached to.
16307   *
16308   * @param {Object} [options={}]
16309   *        The key/value store of player options.
16310   */
16311  function MenuButton(player) {
16312    var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
16313    classCallCheck(this, MenuButton);
16314
16315    var _this = possibleConstructorReturn(this, _Component.call(this, player, options));
16316
16317    _this.menuButton_ = new Button(player, options);
16318
16319    _this.menuButton_.controlText(_this.controlText_);
16320    _this.menuButton_.el_.setAttribute('aria-haspopup', 'true');
16321
16322    // Add buildCSSClass values to the button, not the wrapper
16323    var buttonClass = Button.prototype.buildCSSClass();
16324
16325    _this.menuButton_.el_.className = _this.buildCSSClass() + ' ' + buttonClass;
16326    _this.menuButton_.removeClass('vjs-control');
16327
16328    _this.addChild(_this.menuButton_);
16329
16330    _this.update();
16331
16332    _this.enabled_ = true;
16333
16334    _this.on(_this.menuButton_, 'tap', _this.handleClick);
16335    _this.on(_this.menuButton_, 'click', _this.handleClick);
16336    _this.on(_this.menuButton_, 'focus', _this.handleFocus);
16337    _this.on(_this.menuButton_, 'blur', _this.handleBlur);
16338
16339    _this.on('keydown', _this.handleSubmenuKeyPress);
16340    return _this;
16341  }
16342
16343  /**
16344   * Update the menu based on the current state of its items.
16345   */
16346
16347
16348  MenuButton.prototype.update = function update() {
16349    var menu = this.createMenu();
16350
16351    if (this.menu) {
16352      this.menu.dispose();
16353      this.removeChild(this.menu);
16354    }
16355
16356    this.menu = menu;
16357    this.addChild(menu);
16358
16359    /**
16360     * Track the state of the menu button
16361     *
16362     * @type {Boolean}
16363     * @private
16364     */
16365    this.buttonPressed_ = false;
16366    this.menuButton_.el_.setAttribute('aria-expanded', 'false');
16367
16368    if (this.items && this.items.length <= this.hideThreshold_) {
16369      this.hide();
16370    } else {
16371      this.show();
16372    }
16373  };
16374
16375  /**
16376   * Create the menu and add all items to it.
16377   *
16378   * @return {Menu}
16379   *         The constructed menu
16380   */
16381
16382
16383  MenuButton.prototype.createMenu = function createMenu() {
16384    var menu = new Menu(this.player_, { menuButton: this });
16385
16386    /**
16387     * Hide the menu if the number of items is less than or equal to this threshold. This defaults
16388     * to 0 and whenever we add items which can be hidden to the menu we'll increment it. We list
16389     * it here because every time we run `createMenu` we need to reset the value.
16390     *
16391     * @protected
16392     * @type {Number}
16393     */
16394    this.hideThreshold_ = 0;
16395
16396    // Add a title list item to the top
16397    if (this.options_.title) {
16398      var title = createEl('li', {
16399        className: 'vjs-menu-title',
16400        innerHTML: toTitleCase(this.options_.title),
16401        tabIndex: -1
16402      });
16403
16404      this.hideThreshold_ += 1;
16405
16406      menu.children_.unshift(title);
16407      prependTo(title, menu.contentEl());
16408    }
16409
16410    this.items = this.createItems();
16411
16412    if (this.items) {
16413      // Add menu items to the menu
16414      for (var i = 0; i < this.items.length; i++) {
16415        menu.addItem(this.items[i]);
16416      }
16417    }
16418
16419    return menu;
16420  };
16421
16422  /**
16423   * Create the list of menu items. Specific to each subclass.
16424   *
16425   * @abstract
16426   */
16427
16428
16429  MenuButton.prototype.createItems = function createItems() {};
16430
16431  /**
16432   * Create the `MenuButtons`s DOM element.
16433   *
16434   * @return {Element}
16435   *         The element that gets created.
16436   */
16437
16438
16439  MenuButton.prototype.createEl = function createEl$$1() {
16440    return _Component.prototype.createEl.call(this, 'div', {
16441      className: this.buildWrapperCSSClass()
16442    }, {});
16443  };
16444
16445  /**
16446   * Allow sub components to stack CSS class names for the wrapper element
16447   *
16448   * @return {string}
16449   *         The constructed wrapper DOM `className`
16450   */
16451
16452
16453  MenuButton.prototype.buildWrapperCSSClass = function buildWrapperCSSClass() {
16454    var menuButtonClass = 'vjs-menu-button';
16455
16456    // If the inline option is passed, we want to use different styles altogether.
16457    if (this.options_.inline === true) {
16458      menuButtonClass += '-inline';
16459    } else {
16460      menuButtonClass += '-popup';
16461    }
16462
16463    // TODO: Fix the CSS so that this isn't necessary
16464    var buttonClass = Button.prototype.buildCSSClass();
16465
16466    return 'vjs-menu-button ' + menuButtonClass + ' ' + buttonClass + ' ' + _Component.prototype.buildCSSClass.call(this);
16467  };
16468
16469  /**
16470   * Builds the default DOM `className`.
16471   *
16472   * @return {string}
16473   *         The DOM `className` for this object.
16474   */
16475
16476
16477  MenuButton.prototype.buildCSSClass = function buildCSSClass() {
16478    var menuButtonClass = 'vjs-menu-button';
16479
16480    // If the inline option is passed, we want to use different styles altogether.
16481    if (this.options_.inline === true) {
16482      menuButtonClass += '-inline';
16483    } else {
16484      menuButtonClass += '-popup';
16485    }
16486
16487    return 'vjs-menu-button ' + menuButtonClass + ' ' + _Component.prototype.buildCSSClass.call(this);
16488  };
16489
16490  /**
16491   * Get or set the localized control text that will be used for accessibility.
16492   *
16493   * > NOTE: This will come from the internal `menuButton_` element.
16494   *
16495   * @param {string} [text]
16496   *        Control text for element.
16497   *
16498   * @param {Element} [el=this.menuButton_.el()]
16499   *        Element to set the title on.
16500   *
16501   * @return {string}
16502   *         - The control text when getting
16503   */
16504
16505
16506  MenuButton.prototype.controlText = function controlText(text) {
16507    var el = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : this.menuButton_.el();
16508
16509    return this.menuButton_.controlText(text, el);
16510  };
16511
16512  /**
16513   * Handle a click on a `MenuButton`.
16514   * See {@link ClickableComponent#handleClick} for instances where this is called.
16515   *
16516   * @param {EventTarget~Event} event
16517   *        The `keydown`, `tap`, or `click` event that caused this function to be
16518   *        called.
16519   *
16520   * @listens tap
16521   * @listens click
16522   */
16523
16524
16525  MenuButton.prototype.handleClick = function handleClick(event) {
16526    // When you click the button it adds focus, which will show the menu.
16527    // So we'll remove focus when the mouse leaves the button. Focus is needed
16528    // for tab navigation.
16529
16530    this.one(this.menu.contentEl(), 'mouseleave', bind(this, function (e) {
16531      this.unpressButton();
16532      this.el_.blur();
16533    }));
16534    if (this.buttonPressed_) {
16535      this.unpressButton();
16536    } else {
16537      this.pressButton();
16538    }
16539  };
16540
16541  /**
16542   * Set the focus to the actual button, not to this element
16543   */
16544
16545
16546  MenuButton.prototype.focus = function focus() {
16547    this.menuButton_.focus();
16548  };
16549
16550  /**
16551   * Remove the focus from the actual button, not this element
16552   */
16553
16554
16555  MenuButton.prototype.blur = function blur() {
16556    this.menuButton_.blur();
16557  };
16558
16559  /**
16560   * This gets called when a `MenuButton` gains focus via a `focus` event.
16561   * Turns on listening for `keydown` events. When they happen it
16562   * calls `this.handleKeyPress`.
16563   *
16564   * @param {EventTarget~Event} event
16565   *        The `focus` event that caused this function to be called.
16566   *
16567   * @listens focus
16568   */
16569
16570
16571  MenuButton.prototype.handleFocus = function handleFocus() {
16572    on(document_1, 'keydown', bind(this, this.handleKeyPress));
16573  };
16574
16575  /**
16576   * Called when a `MenuButton` loses focus. Turns off the listener for
16577   * `keydown` events. Which Stops `this.handleKeyPress` from getting called.
16578   *
16579   * @param {EventTarget~Event} event
16580   *        The `blur` event that caused this function to be called.
16581   *
16582   * @listens blur
16583   */
16584
16585
16586  MenuButton.prototype.handleBlur = function handleBlur() {
16587    off(document_1, 'keydown', bind(this, this.handleKeyPress));
16588  };
16589
16590  /**
16591   * Handle tab, escape, down arrow, and up arrow keys for `MenuButton`. See
16592   * {@link ClickableComponent#handleKeyPress} for instances where this is called.
16593   *
16594   * @param {EventTarget~Event} event
16595   *        The `keydown` event that caused this function to be called.
16596   *
16597   * @listens keydown
16598   */
16599
16600
16601  MenuButton.prototype.handleKeyPress = function handleKeyPress(event) {
16602
16603    // Escape (27) key or Tab (9) key unpress the 'button'
16604    if (event.which === 27 || event.which === 9) {
16605      if (this.buttonPressed_) {
16606        this.unpressButton();
16607      }
16608      // Don't preventDefault for Tab key - we still want to lose focus
16609      if (event.which !== 9) {
16610        event.preventDefault();
16611        // Set focus back to the menu button's button
16612        this.menuButton_.el_.focus();
16613      }
16614      // Up (38) key or Down (40) key press the 'button'
16615    } else if (event.which === 38 || event.which === 40) {
16616      if (!this.buttonPressed_) {
16617        this.pressButton();
16618        event.preventDefault();
16619      }
16620    }
16621  };
16622
16623  /**
16624   * Handle a `keydown` event on a sub-menu. The listener for this is added in
16625   * the constructor.
16626   *
16627   * @param {EventTarget~Event} event
16628   *        Key press event
16629   *
16630   * @listens keydown
16631   */
16632
16633
16634  MenuButton.prototype.handleSubmenuKeyPress = function handleSubmenuKeyPress(event) {
16635
16636    // Escape (27) key or Tab (9) key unpress the 'button'
16637    if (event.which === 27 || event.which === 9) {
16638      if (this.buttonPressed_) {
16639        this.unpressButton();
16640      }
16641      // Don't preventDefault for Tab key - we still want to lose focus
16642      if (event.which !== 9) {
16643        event.preventDefault();
16644        // Set focus back to the menu button's button
16645        this.menuButton_.el_.focus();
16646      }
16647    }
16648  };
16649
16650  /**
16651   * Put the current `MenuButton` into a pressed state.
16652   */
16653
16654
16655  MenuButton.prototype.pressButton = function pressButton() {
16656    if (this.enabled_) {
16657      this.buttonPressed_ = true;
16658      this.menu.lockShowing();
16659      this.menuButton_.el_.setAttribute('aria-expanded', 'true');
16660
16661      // set the focus into the submenu, except on iOS where it is resulting in
16662      // undesired scrolling behavior when the player is in an iframe
16663      if (IS_IOS && isInFrame()) {
16664        // Return early so that the menu isn't focused
16665        return;
16666      }
16667
16668      this.menu.focus();
16669    }
16670  };
16671
16672  /**
16673   * Take the current `MenuButton` out of a pressed state.
16674   */
16675
16676
16677  MenuButton.prototype.unpressButton = function unpressButton() {
16678    if (this.enabled_) {
16679      this.buttonPressed_ = false;
16680      this.menu.unlockShowing();
16681      this.menuButton_.el_.setAttribute('aria-expanded', 'false');
16682    }
16683  };
16684
16685  /**
16686   * Disable the `MenuButton`. Don't allow it to be clicked.
16687   */
16688
16689
16690  MenuButton.prototype.disable = function disable() {
16691    this.unpressButton();
16692
16693    this.enabled_ = false;
16694    this.addClass('vjs-disabled');
16695
16696    this.menuButton_.disable();
16697  };
16698
16699  /**
16700   * Enable the `MenuButton`. Allow it to be clicked.
16701   */
16702
16703
16704  MenuButton.prototype.enable = function enable() {
16705    this.enabled_ = true;
16706    this.removeClass('vjs-disabled');
16707
16708    this.menuButton_.enable();
16709  };
16710
16711  return MenuButton;
16712}(Component);
16713
16714Component.registerComponent('MenuButton', MenuButton);
16715
16716/**
16717 * @file track-button.js
16718 */
16719/**
16720 * The base class for buttons that toggle specific  track types (e.g. subtitles).
16721 *
16722 * @extends MenuButton
16723 */
16724
16725var TrackButton = function (_MenuButton) {
16726  inherits(TrackButton, _MenuButton);
16727
16728  /**
16729   * Creates an instance of this class.
16730   *
16731   * @param {Player} player
16732   *        The `Player` that this class should be attached to.
16733   *
16734   * @param {Object} [options]
16735   *        The key/value store of player options.
16736   */
16737  function TrackButton(player, options) {
16738    classCallCheck(this, TrackButton);
16739
16740    var tracks = options.tracks;
16741
16742    var _this = possibleConstructorReturn(this, _MenuButton.call(this, player, options));
16743
16744    if (_this.items.length <= 1) {
16745      _this.hide();
16746    }
16747
16748    if (!tracks) {
16749      return possibleConstructorReturn(_this);
16750    }
16751
16752    var updateHandler = bind(_this, _this.update);
16753
16754    tracks.addEventListener('removetrack', updateHandler);
16755    tracks.addEventListener('addtrack', updateHandler);
16756    _this.player_.on('ready', updateHandler);
16757
16758    _this.player_.on('dispose', function () {
16759      tracks.removeEventListener('removetrack', updateHandler);
16760      tracks.removeEventListener('addtrack', updateHandler);
16761    });
16762    return _this;
16763  }
16764
16765  return TrackButton;
16766}(MenuButton);
16767
16768Component.registerComponent('TrackButton', TrackButton);
16769
16770/**
16771 * @file menu-item.js
16772 */
16773/**
16774 * The component for a menu item. `<li>`
16775 *
16776 * @extends ClickableComponent
16777 */
16778
16779var MenuItem = function (_ClickableComponent) {
16780  inherits(MenuItem, _ClickableComponent);
16781
16782  /**
16783   * Creates an instance of the this class.
16784   *
16785   * @param {Player} player
16786   *        The `Player` that this class should be attached to.
16787   *
16788   * @param {Object} [options={}]
16789   *        The key/value store of player options.
16790   *
16791   */
16792  function MenuItem(player, options) {
16793    classCallCheck(this, MenuItem);
16794
16795    var _this = possibleConstructorReturn(this, _ClickableComponent.call(this, player, options));
16796
16797    _this.selectable = options.selectable;
16798    _this.isSelected_ = options.selected || false;
16799    _this.multiSelectable = options.multiSelectable;
16800
16801    _this.selected(_this.isSelected_);
16802
16803    if (_this.selectable) {
16804      if (_this.multiSelectable) {
16805        _this.el_.setAttribute('role', 'menuitemcheckbox');
16806      } else {
16807        _this.el_.setAttribute('role', 'menuitemradio');
16808      }
16809    } else {
16810      _this.el_.setAttribute('role', 'menuitem');
16811    }
16812    return _this;
16813  }
16814
16815  /**
16816   * Create the `MenuItem's DOM element
16817   *
16818   * @param {string} [type=li]
16819   *        Element's node type, not actually used, always set to `li`.
16820   *
16821   * @param {Object} [props={}]
16822   *        An object of properties that should be set on the element
16823   *
16824   * @param {Object} [attrs={}]
16825   *        An object of attributes that should be set on the element
16826   *
16827   * @return {Element}
16828   *         The element that gets created.
16829   */
16830
16831
16832  MenuItem.prototype.createEl = function createEl(type, props, attrs) {
16833    // The control is textual, not just an icon
16834    this.nonIconControl = true;
16835
16836    return _ClickableComponent.prototype.createEl.call(this, 'li', assign({
16837      className: 'vjs-menu-item',
16838      innerHTML: '<span class="vjs-menu-item-text">' + this.localize(this.options_.label) + '</span>',
16839      tabIndex: -1
16840    }, props), attrs);
16841  };
16842
16843  /**
16844   * Any click on a `MenuItem` puts it into the selected state.
16845   * See {@link ClickableComponent#handleClick} for instances where this is called.
16846   *
16847   * @param {EventTarget~Event} event
16848   *        The `keydown`, `tap`, or `click` event that caused this function to be
16849   *        called.
16850   *
16851   * @listens tap
16852   * @listens click
16853   */
16854
16855
16856  MenuItem.prototype.handleClick = function handleClick(event) {
16857    this.selected(true);
16858  };
16859
16860  /**
16861   * Set the state for this menu item as selected or not.
16862   *
16863   * @param {boolean} selected
16864   *        if the menu item is selected or not
16865   */
16866
16867
16868  MenuItem.prototype.selected = function selected(_selected) {
16869    if (this.selectable) {
16870      if (_selected) {
16871        this.addClass('vjs-selected');
16872        this.el_.setAttribute('aria-checked', 'true');
16873        // aria-checked isn't fully supported by browsers/screen readers,
16874        // so indicate selected state to screen reader in the control text.
16875        this.controlText(', selected');
16876        this.isSelected_ = true;
16877      } else {
16878        this.removeClass('vjs-selected');
16879        this.el_.setAttribute('aria-checked', 'false');
16880        // Indicate un-selected state to screen reader
16881        this.controlText('');
16882        this.isSelected_ = false;
16883      }
16884    }
16885  };
16886
16887  return MenuItem;
16888}(ClickableComponent);
16889
16890Component.registerComponent('MenuItem', MenuItem);
16891
16892/**
16893 * @file text-track-menu-item.js
16894 */
16895/**
16896 * The specific menu item type for selecting a language within a text track kind
16897 *
16898 * @extends MenuItem
16899 */
16900
16901var TextTrackMenuItem = function (_MenuItem) {
16902  inherits(TextTrackMenuItem, _MenuItem);
16903
16904  /**
16905   * Creates an instance of this class.
16906   *
16907   * @param {Player} player
16908   *        The `Player` that this class should be attached to.
16909   *
16910   * @param {Object} [options]
16911   *        The key/value store of player options.
16912   */
16913  function TextTrackMenuItem(player, options) {
16914    classCallCheck(this, TextTrackMenuItem);
16915
16916    var track = options.track;
16917    var tracks = player.textTracks();
16918
16919    // Modify options for parent MenuItem class's init.
16920    options.label = track.label || track.language || 'Unknown';
16921    options.selected = track.mode === 'showing';
16922
16923    var _this = possibleConstructorReturn(this, _MenuItem.call(this, player, options));
16924
16925    _this.track = track;
16926    var changeHandler = function changeHandler() {
16927      for (var _len = arguments.length, args = Array(_len), _key = 0; _key < _len; _key++) {
16928        args[_key] = arguments[_key];
16929      }
16930
16931      _this.handleTracksChange.apply(_this, args);
16932    };
16933    var selectedLanguageChangeHandler = function selectedLanguageChangeHandler() {
16934      for (var _len2 = arguments.length, args = Array(_len2), _key2 = 0; _key2 < _len2; _key2++) {
16935        args[_key2] = arguments[_key2];
16936      }
16937
16938      _this.handleSelectedLanguageChange.apply(_this, args);
16939    };
16940
16941    player.on(['loadstart', 'texttrackchange'], changeHandler);
16942    tracks.addEventListener('change', changeHandler);
16943    tracks.addEventListener('selectedlanguagechange', selectedLanguageChangeHandler);
16944    _this.on('dispose', function () {
16945      player.off(['loadstart', 'texttrackchange'], changeHandler);
16946      tracks.removeEventListener('change', changeHandler);
16947      tracks.removeEventListener('selectedlanguagechange', selectedLanguageChangeHandler);
16948    });
16949
16950    // iOS7 doesn't dispatch change events to TextTrackLists when an
16951    // associated track's mode changes. Without something like
16952    // Object.observe() (also not present on iOS7), it's not
16953    // possible to detect changes to the mode attribute and polyfill
16954    // the change event. As a poor substitute, we manually dispatch
16955    // change events whenever the controls modify the mode.
16956    if (tracks.onchange === undefined) {
16957      var event = void 0;
16958
16959      _this.on(['tap', 'click'], function () {
16960        if (_typeof(window_1.Event) !== 'object') {
16961          // Android 2.3 throws an Illegal Constructor error for window.Event
16962          try {
16963            event = new window_1.Event('change');
16964          } catch (err) {
16965            // continue regardless of error
16966          }
16967        }
16968
16969        if (!event) {
16970          event = document_1.createEvent('Event');
16971          event.initEvent('change', true, true);
16972        }
16973
16974        tracks.dispatchEvent(event);
16975      });
16976    }
16977
16978    // set the default state based on current tracks
16979    _this.handleTracksChange();
16980    return _this;
16981  }
16982
16983  /**
16984   * This gets called when an `TextTrackMenuItem` is "clicked". See
16985   * {@link ClickableComponent} for more detailed information on what a click can be.
16986   *
16987   * @param {EventTarget~Event} event
16988   *        The `keydown`, `tap`, or `click` event that caused this function to be
16989   *        called.
16990   *
16991   * @listens tap
16992   * @listens click
16993   */
16994
16995
16996  TextTrackMenuItem.prototype.handleClick = function handleClick(event) {
16997    var kind = this.track.kind;
16998    var kinds = this.track.kinds;
16999    var tracks = this.player_.textTracks();
17000
17001    if (!kinds) {
17002      kinds = [kind];
17003    }
17004
17005    _MenuItem.prototype.handleClick.call(this, event);
17006
17007    if (!tracks) {
17008      return;
17009    }
17010
17011    for (var i = 0; i < tracks.length; i++) {
17012      var track = tracks[i];
17013
17014      if (track === this.track && kinds.indexOf(track.kind) > -1) {
17015        if (track.mode !== 'showing') {
17016          track.mode = 'showing';
17017        }
17018      } else if (track.mode !== 'disabled') {
17019        track.mode = 'disabled';
17020      }
17021    }
17022  };
17023
17024  /**
17025   * Handle text track list change
17026   *
17027   * @param {EventTarget~Event} event
17028   *        The `change` event that caused this function to be called.
17029   *
17030   * @listens TextTrackList#change
17031   */
17032
17033
17034  TextTrackMenuItem.prototype.handleTracksChange = function handleTracksChange(event) {
17035    var shouldBeSelected = this.track.mode === 'showing';
17036
17037    // Prevent redundant selected() calls because they may cause
17038    // screen readers to read the appended control text unnecessarily
17039    if (shouldBeSelected !== this.isSelected_) {
17040      this.selected(shouldBeSelected);
17041    }
17042  };
17043
17044  TextTrackMenuItem.prototype.handleSelectedLanguageChange = function handleSelectedLanguageChange(event) {
17045    if (this.track.mode === 'showing') {
17046      var selectedLanguage = this.player_.cache_.selectedLanguage;
17047
17048      // Don't replace the kind of track across the same language
17049      if (selectedLanguage && selectedLanguage.enabled && selectedLanguage.language === this.track.language && selectedLanguage.kind !== this.track.kind) {
17050        return;
17051      }
17052
17053      this.player_.cache_.selectedLanguage = {
17054        enabled: true,
17055        language: this.track.language,
17056        kind: this.track.kind
17057      };
17058    }
17059  };
17060
17061  TextTrackMenuItem.prototype.dispose = function dispose() {
17062    // remove reference to track object on dispose
17063    this.track = null;
17064
17065    _MenuItem.prototype.dispose.call(this);
17066  };
17067
17068  return TextTrackMenuItem;
17069}(MenuItem);
17070
17071Component.registerComponent('TextTrackMenuItem', TextTrackMenuItem);
17072
17073/**
17074 * @file off-text-track-menu-item.js
17075 */
17076/**
17077 * A special menu item for turning of a specific type of text track
17078 *
17079 * @extends TextTrackMenuItem
17080 */
17081
17082var OffTextTrackMenuItem = function (_TextTrackMenuItem) {
17083  inherits(OffTextTrackMenuItem, _TextTrackMenuItem);
17084
17085  /**
17086   * Creates an instance of this class.
17087   *
17088   * @param {Player} player
17089   *        The `Player` that this class should be attached to.
17090   *
17091   * @param {Object} [options]
17092   *        The key/value store of player options.
17093   */
17094  function OffTextTrackMenuItem(player, options) {
17095    classCallCheck(this, OffTextTrackMenuItem);
17096
17097    // Create pseudo track info
17098    // Requires options['kind']
17099    options.track = {
17100      player: player,
17101      kind: options.kind,
17102      kinds: options.kinds,
17103      'default': false,
17104      mode: 'disabled'
17105    };
17106
17107    if (!options.kinds) {
17108      options.kinds = [options.kind];
17109    }
17110
17111    if (options.label) {
17112      options.track.label = options.label;
17113    } else {
17114      options.track.label = options.kinds.join(' and ') + ' off';
17115    }
17116
17117    // MenuItem is selectable
17118    options.selectable = true;
17119    // MenuItem is NOT multiSelectable (i.e. only one can be marked "selected" at a time)
17120    options.multiSelectable = false;
17121
17122    return possibleConstructorReturn(this, _TextTrackMenuItem.call(this, player, options));
17123  }
17124
17125  /**
17126   * Handle text track change
17127   *
17128   * @param {EventTarget~Event} event
17129   *        The event that caused this function to run
17130   */
17131
17132
17133  OffTextTrackMenuItem.prototype.handleTracksChange = function handleTracksChange(event) {
17134    var tracks = this.player().textTracks();
17135    var shouldBeSelected = true;
17136
17137    for (var i = 0, l = tracks.length; i < l; i++) {
17138      var track = tracks[i];
17139
17140      if (this.options_.kinds.indexOf(track.kind) > -1 && track.mode === 'showing') {
17141        shouldBeSelected = false;
17142        break;
17143      }
17144    }
17145
17146    // Prevent redundant selected() calls because they may cause
17147    // screen readers to read the appended control text unnecessarily
17148    if (shouldBeSelected !== this.isSelected_) {
17149      this.selected(shouldBeSelected);
17150    }
17151  };
17152
17153  OffTextTrackMenuItem.prototype.handleSelectedLanguageChange = function handleSelectedLanguageChange(event) {
17154    var tracks = this.player().textTracks();
17155    var allHidden = true;
17156
17157    for (var i = 0, l = tracks.length; i < l; i++) {
17158      var track = tracks[i];
17159
17160      if (['captions', 'descriptions', 'subtitles'].indexOf(track.kind) > -1 && track.mode === 'showing') {
17161        allHidden = false;
17162        break;
17163      }
17164    }
17165
17166    if (allHidden) {
17167      this.player_.cache_.selectedLanguage = {
17168        enabled: false
17169      };
17170    }
17171  };
17172
17173  return OffTextTrackMenuItem;
17174}(TextTrackMenuItem);
17175
17176Component.registerComponent('OffTextTrackMenuItem', OffTextTrackMenuItem);
17177
17178/**
17179 * @file text-track-button.js
17180 */
17181/**
17182 * The base class for buttons that toggle specific text track types (e.g. subtitles)
17183 *
17184 * @extends MenuButton
17185 */
17186
17187var TextTrackButton = function (_TrackButton) {
17188  inherits(TextTrackButton, _TrackButton);
17189
17190  /**
17191   * Creates an instance of this class.
17192   *
17193   * @param {Player} player
17194   *        The `Player` that this class should be attached to.
17195   *
17196   * @param {Object} [options={}]
17197   *        The key/value store of player options.
17198   */
17199  function TextTrackButton(player) {
17200    var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
17201    classCallCheck(this, TextTrackButton);
17202
17203    options.tracks = player.textTracks();
17204
17205    return possibleConstructorReturn(this, _TrackButton.call(this, player, options));
17206  }
17207
17208  /**
17209   * Create a menu item for each text track
17210   *
17211   * @param {TextTrackMenuItem[]} [items=[]]
17212   *        Existing array of items to use during creation
17213   *
17214   * @return {TextTrackMenuItem[]}
17215   *         Array of menu items that were created
17216   */
17217
17218
17219  TextTrackButton.prototype.createItems = function createItems() {
17220    var items = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : [];
17221    var TrackMenuItem = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : TextTrackMenuItem;
17222
17223
17224    // Label is an overide for the [track] off label
17225    // USed to localise captions/subtitles
17226    var label = void 0;
17227
17228    if (this.label_) {
17229      label = this.label_ + ' off';
17230    }
17231    // Add an OFF menu item to turn all tracks off
17232    items.push(new OffTextTrackMenuItem(this.player_, {
17233      kinds: this.kinds_,
17234      kind: this.kind_,
17235      label: label
17236    }));
17237
17238    this.hideThreshold_ += 1;
17239
17240    var tracks = this.player_.textTracks();
17241
17242    if (!Array.isArray(this.kinds_)) {
17243      this.kinds_ = [this.kind_];
17244    }
17245
17246    for (var i = 0; i < tracks.length; i++) {
17247      var track = tracks[i];
17248
17249      // only add tracks that are of an appropriate kind and have a label
17250      if (this.kinds_.indexOf(track.kind) > -1) {
17251
17252        var item = new TrackMenuItem(this.player_, {
17253          track: track,
17254          // MenuItem is selectable
17255          selectable: true,
17256          // MenuItem is NOT multiSelectable (i.e. only one can be marked "selected" at a time)
17257          multiSelectable: false
17258        });
17259
17260        item.addClass('vjs-' + track.kind + '-menu-item');
17261        items.push(item);
17262      }
17263    }
17264
17265    return items;
17266  };
17267
17268  return TextTrackButton;
17269}(TrackButton);
17270
17271Component.registerComponent('TextTrackButton', TextTrackButton);
17272
17273/**
17274 * @file chapters-track-menu-item.js
17275 */
17276/**
17277 * The chapter track menu item
17278 *
17279 * @extends MenuItem
17280 */
17281
17282var ChaptersTrackMenuItem = function (_MenuItem) {
17283  inherits(ChaptersTrackMenuItem, _MenuItem);
17284
17285  /**
17286   * Creates an instance of this class.
17287   *
17288   * @param {Player} player
17289   *        The `Player` that this class should be attached to.
17290   *
17291   * @param {Object} [options]
17292   *        The key/value store of player options.
17293   */
17294  function ChaptersTrackMenuItem(player, options) {
17295    classCallCheck(this, ChaptersTrackMenuItem);
17296
17297    var track = options.track;
17298    var cue = options.cue;
17299    var currentTime = player.currentTime();
17300
17301    // Modify options for parent MenuItem class's init.
17302    options.selectable = true;
17303    options.multiSelectable = false;
17304    options.label = cue.text;
17305    options.selected = cue.startTime <= currentTime && currentTime < cue.endTime;
17306
17307    var _this = possibleConstructorReturn(this, _MenuItem.call(this, player, options));
17308
17309    _this.track = track;
17310    _this.cue = cue;
17311    track.addEventListener('cuechange', bind(_this, _this.update));
17312    return _this;
17313  }
17314
17315  /**
17316   * This gets called when an `ChaptersTrackMenuItem` is "clicked". See
17317   * {@link ClickableComponent} for more detailed information on what a click can be.
17318   *
17319   * @param {EventTarget~Event} [event]
17320   *        The `keydown`, `tap`, or `click` event that caused this function to be
17321   *        called.
17322   *
17323   * @listens tap
17324   * @listens click
17325   */
17326
17327
17328  ChaptersTrackMenuItem.prototype.handleClick = function handleClick(event) {
17329    _MenuItem.prototype.handleClick.call(this);
17330    this.player_.currentTime(this.cue.startTime);
17331    this.update(this.cue.startTime);
17332  };
17333
17334  /**
17335   * Update chapter menu item
17336   *
17337   * @param {EventTarget~Event} [event]
17338   *        The `cuechange` event that caused this function to run.
17339   *
17340   * @listens TextTrack#cuechange
17341   */
17342
17343
17344  ChaptersTrackMenuItem.prototype.update = function update(event) {
17345    var cue = this.cue;
17346    var currentTime = this.player_.currentTime();
17347
17348    // vjs.log(currentTime, cue.startTime);
17349    this.selected(cue.startTime <= currentTime && currentTime < cue.endTime);
17350  };
17351
17352  return ChaptersTrackMenuItem;
17353}(MenuItem);
17354
17355Component.registerComponent('ChaptersTrackMenuItem', ChaptersTrackMenuItem);
17356
17357/**
17358 * @file chapters-button.js
17359 */
17360/**
17361 * The button component for toggling and selecting chapters
17362 * Chapters act much differently than other text tracks
17363 * Cues are navigation vs. other tracks of alternative languages
17364 *
17365 * @extends TextTrackButton
17366 */
17367
17368var ChaptersButton = function (_TextTrackButton) {
17369  inherits(ChaptersButton, _TextTrackButton);
17370
17371  /**
17372   * Creates an instance of this class.
17373   *
17374   * @param {Player} player
17375   *        The `Player` that this class should be attached to.
17376   *
17377   * @param {Object} [options]
17378   *        The key/value store of player options.
17379   *
17380   * @param {Component~ReadyCallback} [ready]
17381   *        The function to call when this function is ready.
17382   */
17383  function ChaptersButton(player, options, ready) {
17384    classCallCheck(this, ChaptersButton);
17385    return possibleConstructorReturn(this, _TextTrackButton.call(this, player, options, ready));
17386  }
17387
17388  /**
17389   * Builds the default DOM `className`.
17390   *
17391   * @return {string}
17392   *         The DOM `className` for this object.
17393   */
17394
17395
17396  ChaptersButton.prototype.buildCSSClass = function buildCSSClass() {
17397    return 'vjs-chapters-button ' + _TextTrackButton.prototype.buildCSSClass.call(this);
17398  };
17399
17400  ChaptersButton.prototype.buildWrapperCSSClass = function buildWrapperCSSClass() {
17401    return 'vjs-chapters-button ' + _TextTrackButton.prototype.buildWrapperCSSClass.call(this);
17402  };
17403
17404  /**
17405   * Update the menu based on the current state of its items.
17406   *
17407   * @param {EventTarget~Event} [event]
17408   *        An event that triggered this function to run.
17409   *
17410   * @listens TextTrackList#addtrack
17411   * @listens TextTrackList#removetrack
17412   * @listens TextTrackList#change
17413   */
17414
17415
17416  ChaptersButton.prototype.update = function update(event) {
17417    if (!this.track_ || event && (event.type === 'addtrack' || event.type === 'removetrack')) {
17418      this.setTrack(this.findChaptersTrack());
17419    }
17420    _TextTrackButton.prototype.update.call(this);
17421  };
17422
17423  /**
17424   * Set the currently selected track for the chapters button.
17425   *
17426   * @param {TextTrack} track
17427   *        The new track to select. Nothing will change if this is the currently selected
17428   *        track.
17429   */
17430
17431
17432  ChaptersButton.prototype.setTrack = function setTrack(track) {
17433    if (this.track_ === track) {
17434      return;
17435    }
17436
17437    if (!this.updateHandler_) {
17438      this.updateHandler_ = this.update.bind(this);
17439    }
17440
17441    // here this.track_ refers to the old track instance
17442    if (this.track_) {
17443      var remoteTextTrackEl = this.player_.remoteTextTrackEls().getTrackElementByTrack_(this.track_);
17444
17445      if (remoteTextTrackEl) {
17446        remoteTextTrackEl.removeEventListener('load', this.updateHandler_);
17447      }
17448
17449      this.track_ = null;
17450    }
17451
17452    this.track_ = track;
17453
17454    // here this.track_ refers to the new track instance
17455    if (this.track_) {
17456      this.track_.mode = 'hidden';
17457
17458      var _remoteTextTrackEl = this.player_.remoteTextTrackEls().getTrackElementByTrack_(this.track_);
17459
17460      if (_remoteTextTrackEl) {
17461        _remoteTextTrackEl.addEventListener('load', this.updateHandler_);
17462      }
17463    }
17464  };
17465
17466  /**
17467   * Find the track object that is currently in use by this ChaptersButton
17468   *
17469   * @return {TextTrack|undefined}
17470   *         The current track or undefined if none was found.
17471   */
17472
17473
17474  ChaptersButton.prototype.findChaptersTrack = function findChaptersTrack() {
17475    var tracks = this.player_.textTracks() || [];
17476
17477    for (var i = tracks.length - 1; i >= 0; i--) {
17478      // We will always choose the last track as our chaptersTrack
17479      var track = tracks[i];
17480
17481      if (track.kind === this.kind_) {
17482        return track;
17483      }
17484    }
17485  };
17486
17487  /**
17488   * Get the caption for the ChaptersButton based on the track label. This will also
17489   * use the current tracks localized kind as a fallback if a label does not exist.
17490   *
17491   * @return {string}
17492   *         The tracks current label or the localized track kind.
17493   */
17494
17495
17496  ChaptersButton.prototype.getMenuCaption = function getMenuCaption() {
17497    if (this.track_ && this.track_.label) {
17498      return this.track_.label;
17499    }
17500    return this.localize(toTitleCase(this.kind_));
17501  };
17502
17503  /**
17504   * Create menu from chapter track
17505   *
17506   * @return {Menu}
17507   *         New menu for the chapter buttons
17508   */
17509
17510
17511  ChaptersButton.prototype.createMenu = function createMenu() {
17512    this.options_.title = this.getMenuCaption();
17513    return _TextTrackButton.prototype.createMenu.call(this);
17514  };
17515
17516  /**
17517   * Create a menu item for each text track
17518   *
17519   * @return {TextTrackMenuItem[]}
17520   *         Array of menu items
17521   */
17522
17523
17524  ChaptersButton.prototype.createItems = function createItems() {
17525    var items = [];
17526
17527    if (!this.track_) {
17528      return items;
17529    }
17530
17531    var cues = this.track_.cues;
17532
17533    if (!cues) {
17534      return items;
17535    }
17536
17537    for (var i = 0, l = cues.length; i < l; i++) {
17538      var cue = cues[i];
17539      var mi = new ChaptersTrackMenuItem(this.player_, { track: this.track_, cue: cue });
17540
17541      items.push(mi);
17542    }
17543
17544    return items;
17545  };
17546
17547  return ChaptersButton;
17548}(TextTrackButton);
17549
17550/**
17551 * `kind` of TextTrack to look for to associate it with this menu.
17552 *
17553 * @type {string}
17554 * @private
17555 */
17556
17557
17558ChaptersButton.prototype.kind_ = 'chapters';
17559
17560/**
17561 * The text that should display over the `ChaptersButton`s controls. Added for localization.
17562 *
17563 * @type {string}
17564 * @private
17565 */
17566ChaptersButton.prototype.controlText_ = 'Chapters';
17567
17568Component.registerComponent('ChaptersButton', ChaptersButton);
17569
17570/**
17571 * @file descriptions-button.js
17572 */
17573/**
17574 * The button component for toggling and selecting descriptions
17575 *
17576 * @extends TextTrackButton
17577 */
17578
17579var DescriptionsButton = function (_TextTrackButton) {
17580  inherits(DescriptionsButton, _TextTrackButton);
17581
17582  /**
17583   * Creates an instance of this class.
17584   *
17585   * @param {Player} player
17586   *        The `Player` that this class should be attached to.
17587   *
17588   * @param {Object} [options]
17589   *        The key/value store of player options.
17590   *
17591   * @param {Component~ReadyCallback} [ready]
17592   *        The function to call when this component is ready.
17593   */
17594  function DescriptionsButton(player, options, ready) {
17595    classCallCheck(this, DescriptionsButton);
17596
17597    var _this = possibleConstructorReturn(this, _TextTrackButton.call(this, player, options, ready));
17598
17599    var tracks = player.textTracks();
17600    var changeHandler = bind(_this, _this.handleTracksChange);
17601
17602    tracks.addEventListener('change', changeHandler);
17603    _this.on('dispose', function () {
17604      tracks.removeEventListener('change', changeHandler);
17605    });
17606    return _this;
17607  }
17608
17609  /**
17610   * Handle text track change
17611   *
17612   * @param {EventTarget~Event} event
17613   *        The event that caused this function to run
17614   *
17615   * @listens TextTrackList#change
17616   */
17617
17618
17619  DescriptionsButton.prototype.handleTracksChange = function handleTracksChange(event) {
17620    var tracks = this.player().textTracks();
17621    var disabled = false;
17622
17623    // Check whether a track of a different kind is showing
17624    for (var i = 0, l = tracks.length; i < l; i++) {
17625      var track = tracks[i];
17626
17627      if (track.kind !== this.kind_ && track.mode === 'showing') {
17628        disabled = true;
17629        break;
17630      }
17631    }
17632
17633    // If another track is showing, disable this menu button
17634    if (disabled) {
17635      this.disable();
17636    } else {
17637      this.enable();
17638    }
17639  };
17640
17641  /**
17642   * Builds the default DOM `className`.
17643   *
17644   * @return {string}
17645   *         The DOM `className` for this object.
17646   */
17647
17648
17649  DescriptionsButton.prototype.buildCSSClass = function buildCSSClass() {
17650    return 'vjs-descriptions-button ' + _TextTrackButton.prototype.buildCSSClass.call(this);
17651  };
17652
17653  DescriptionsButton.prototype.buildWrapperCSSClass = function buildWrapperCSSClass() {
17654    return 'vjs-descriptions-button ' + _TextTrackButton.prototype.buildWrapperCSSClass.call(this);
17655  };
17656
17657  return DescriptionsButton;
17658}(TextTrackButton);
17659
17660/**
17661 * `kind` of TextTrack to look for to associate it with this menu.
17662 *
17663 * @type {string}
17664 * @private
17665 */
17666
17667
17668DescriptionsButton.prototype.kind_ = 'descriptions';
17669
17670/**
17671 * The text that should display over the `DescriptionsButton`s controls. Added for localization.
17672 *
17673 * @type {string}
17674 * @private
17675 */
17676DescriptionsButton.prototype.controlText_ = 'Descriptions';
17677
17678Component.registerComponent('DescriptionsButton', DescriptionsButton);
17679
17680/**
17681 * @file subtitles-button.js
17682 */
17683/**
17684 * The button component for toggling and selecting subtitles
17685 *
17686 * @extends TextTrackButton
17687 */
17688
17689var SubtitlesButton = function (_TextTrackButton) {
17690  inherits(SubtitlesButton, _TextTrackButton);
17691
17692  /**
17693   * Creates an instance of this class.
17694   *
17695   * @param {Player} player
17696   *        The `Player` that this class should be attached to.
17697   *
17698   * @param {Object} [options]
17699   *        The key/value store of player options.
17700   *
17701   * @param {Component~ReadyCallback} [ready]
17702   *        The function to call when this component is ready.
17703   */
17704  function SubtitlesButton(player, options, ready) {
17705    classCallCheck(this, SubtitlesButton);
17706    return possibleConstructorReturn(this, _TextTrackButton.call(this, player, options, ready));
17707  }
17708
17709  /**
17710   * Builds the default DOM `className`.
17711   *
17712   * @return {string}
17713   *         The DOM `className` for this object.
17714   */
17715
17716
17717  SubtitlesButton.prototype.buildCSSClass = function buildCSSClass() {
17718    return 'vjs-subtitles-button ' + _TextTrackButton.prototype.buildCSSClass.call(this);
17719  };
17720
17721  SubtitlesButton.prototype.buildWrapperCSSClass = function buildWrapperCSSClass() {
17722    return 'vjs-subtitles-button ' + _TextTrackButton.prototype.buildWrapperCSSClass.call(this);
17723  };
17724
17725  return SubtitlesButton;
17726}(TextTrackButton);
17727
17728/**
17729 * `kind` of TextTrack to look for to associate it with this menu.
17730 *
17731 * @type {string}
17732 * @private
17733 */
17734
17735
17736SubtitlesButton.prototype.kind_ = 'subtitles';
17737
17738/**
17739 * The text that should display over the `SubtitlesButton`s controls. Added for localization.
17740 *
17741 * @type {string}
17742 * @private
17743 */
17744SubtitlesButton.prototype.controlText_ = 'Subtitles';
17745
17746Component.registerComponent('SubtitlesButton', SubtitlesButton);
17747
17748/**
17749 * @file caption-settings-menu-item.js
17750 */
17751/**
17752 * The menu item for caption track settings menu
17753 *
17754 * @extends TextTrackMenuItem
17755 */
17756
17757var CaptionSettingsMenuItem = function (_TextTrackMenuItem) {
17758  inherits(CaptionSettingsMenuItem, _TextTrackMenuItem);
17759
17760  /**
17761   * Creates an instance of this class.
17762   *
17763   * @param {Player} player
17764   *        The `Player` that this class should be attached to.
17765   *
17766   * @param {Object} [options]
17767   *        The key/value store of player options.
17768   */
17769  function CaptionSettingsMenuItem(player, options) {
17770    classCallCheck(this, CaptionSettingsMenuItem);
17771
17772    options.track = {
17773      player: player,
17774      kind: options.kind,
17775      label: options.kind + ' settings',
17776      selectable: false,
17777      'default': false,
17778      mode: 'disabled'
17779    };
17780
17781    // CaptionSettingsMenuItem has no concept of 'selected'
17782    options.selectable = false;
17783
17784    options.name = 'CaptionSettingsMenuItem';
17785
17786    var _this = possibleConstructorReturn(this, _TextTrackMenuItem.call(this, player, options));
17787
17788    _this.addClass('vjs-texttrack-settings');
17789    _this.controlText(', opens ' + options.kind + ' settings dialog');
17790    return _this;
17791  }
17792
17793  /**
17794   * This gets called when an `CaptionSettingsMenuItem` is "clicked". See
17795   * {@link ClickableComponent} for more detailed information on what a click can be.
17796   *
17797   * @param {EventTarget~Event} [event]
17798   *        The `keydown`, `tap`, or `click` event that caused this function to be
17799   *        called.
17800   *
17801   * @listens tap
17802   * @listens click
17803   */
17804
17805
17806  CaptionSettingsMenuItem.prototype.handleClick = function handleClick(event) {
17807    this.player().getChild('textTrackSettings').open();
17808  };
17809
17810  return CaptionSettingsMenuItem;
17811}(TextTrackMenuItem);
17812
17813Component.registerComponent('CaptionSettingsMenuItem', CaptionSettingsMenuItem);
17814
17815/**
17816 * @file captions-button.js
17817 */
17818/**
17819 * The button component for toggling and selecting captions
17820 *
17821 * @extends TextTrackButton
17822 */
17823
17824var CaptionsButton = function (_TextTrackButton) {
17825  inherits(CaptionsButton, _TextTrackButton);
17826
17827  /**
17828   * Creates an instance of this class.
17829   *
17830   * @param {Player} player
17831   *        The `Player` that this class should be attached to.
17832   *
17833   * @param {Object} [options]
17834   *        The key/value store of player options.
17835   *
17836   * @param {Component~ReadyCallback} [ready]
17837   *        The function to call when this component is ready.
17838   */
17839  function CaptionsButton(player, options, ready) {
17840    classCallCheck(this, CaptionsButton);
17841    return possibleConstructorReturn(this, _TextTrackButton.call(this, player, options, ready));
17842  }
17843
17844  /**
17845   * Builds the default DOM `className`.
17846   *
17847   * @return {string}
17848   *         The DOM `className` for this object.
17849   */
17850
17851
17852  CaptionsButton.prototype.buildCSSClass = function buildCSSClass() {
17853    return 'vjs-captions-button ' + _TextTrackButton.prototype.buildCSSClass.call(this);
17854  };
17855
17856  CaptionsButton.prototype.buildWrapperCSSClass = function buildWrapperCSSClass() {
17857    return 'vjs-captions-button ' + _TextTrackButton.prototype.buildWrapperCSSClass.call(this);
17858  };
17859
17860  /**
17861   * Create caption menu items
17862   *
17863   * @return {CaptionSettingsMenuItem[]}
17864   *         The array of current menu items.
17865   */
17866
17867
17868  CaptionsButton.prototype.createItems = function createItems() {
17869    var items = [];
17870
17871    if (!(this.player().tech_ && this.player().tech_.featuresNativeTextTracks) && this.player().getChild('textTrackSettings')) {
17872      items.push(new CaptionSettingsMenuItem(this.player_, { kind: this.kind_ }));
17873
17874      this.hideThreshold_ += 1;
17875    }
17876
17877    return _TextTrackButton.prototype.createItems.call(this, items);
17878  };
17879
17880  return CaptionsButton;
17881}(TextTrackButton);
17882
17883/**
17884 * `kind` of TextTrack to look for to associate it with this menu.
17885 *
17886 * @type {string}
17887 * @private
17888 */
17889
17890
17891CaptionsButton.prototype.kind_ = 'captions';
17892
17893/**
17894 * The text that should display over the `CaptionsButton`s controls. Added for localization.
17895 *
17896 * @type {string}
17897 * @private
17898 */
17899CaptionsButton.prototype.controlText_ = 'Captions';
17900
17901Component.registerComponent('CaptionsButton', CaptionsButton);
17902
17903/**
17904 * @file subs-caps-menu-item.js
17905 */
17906/**
17907 * SubsCapsMenuItem has an [cc] icon to distinguish captions from subtitles
17908 * in the SubsCapsMenu.
17909 *
17910 * @extends TextTrackMenuItem
17911 */
17912
17913var SubsCapsMenuItem = function (_TextTrackMenuItem) {
17914  inherits(SubsCapsMenuItem, _TextTrackMenuItem);
17915
17916  function SubsCapsMenuItem() {
17917    classCallCheck(this, SubsCapsMenuItem);
17918    return possibleConstructorReturn(this, _TextTrackMenuItem.apply(this, arguments));
17919  }
17920
17921  SubsCapsMenuItem.prototype.createEl = function createEl(type, props, attrs) {
17922    var innerHTML = '<span class="vjs-menu-item-text">' + this.localize(this.options_.label);
17923
17924    if (this.options_.track.kind === 'captions') {
17925      innerHTML += '\n        <span aria-hidden="true" class="vjs-icon-placeholder"></span>\n        <span class="vjs-control-text"> ' + this.localize('Captions') + '</span>\n      ';
17926    }
17927
17928    innerHTML += '</span>';
17929
17930    var el = _TextTrackMenuItem.prototype.createEl.call(this, type, assign({
17931      innerHTML: innerHTML
17932    }, props), attrs);
17933
17934    return el;
17935  };
17936
17937  return SubsCapsMenuItem;
17938}(TextTrackMenuItem);
17939
17940Component.registerComponent('SubsCapsMenuItem', SubsCapsMenuItem);
17941
17942/**
17943 * @file sub-caps-button.js
17944 */
17945/**
17946 * The button component for toggling and selecting captions and/or subtitles
17947 *
17948 * @extends TextTrackButton
17949 */
17950
17951var SubsCapsButton = function (_TextTrackButton) {
17952  inherits(SubsCapsButton, _TextTrackButton);
17953
17954  function SubsCapsButton(player) {
17955    var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
17956    classCallCheck(this, SubsCapsButton);
17957
17958    // Although North America uses "captions" in most cases for
17959    // "captions and subtitles" other locales use "subtitles"
17960    var _this = possibleConstructorReturn(this, _TextTrackButton.call(this, player, options));
17961
17962    _this.label_ = 'subtitles';
17963    if (['en', 'en-us', 'en-ca', 'fr-ca'].indexOf(_this.player_.language_) > -1) {
17964      _this.label_ = 'captions';
17965    }
17966    _this.menuButton_.controlText(toTitleCase(_this.label_));
17967    return _this;
17968  }
17969
17970  /**
17971   * Builds the default DOM `className`.
17972   *
17973   * @return {string}
17974   *         The DOM `className` for this object.
17975   */
17976
17977
17978  SubsCapsButton.prototype.buildCSSClass = function buildCSSClass() {
17979    return 'vjs-subs-caps-button ' + _TextTrackButton.prototype.buildCSSClass.call(this);
17980  };
17981
17982  SubsCapsButton.prototype.buildWrapperCSSClass = function buildWrapperCSSClass() {
17983    return 'vjs-subs-caps-button ' + _TextTrackButton.prototype.buildWrapperCSSClass.call(this);
17984  };
17985
17986  /**
17987   * Create caption/subtitles menu items
17988   *
17989   * @return {CaptionSettingsMenuItem[]}
17990   *         The array of current menu items.
17991   */
17992
17993
17994  SubsCapsButton.prototype.createItems = function createItems() {
17995    var items = [];
17996
17997    if (!(this.player().tech_ && this.player().tech_.featuresNativeTextTracks) && this.player().getChild('textTrackSettings')) {
17998      items.push(new CaptionSettingsMenuItem(this.player_, { kind: this.label_ }));
17999
18000      this.hideThreshold_ += 1;
18001    }
18002
18003    items = _TextTrackButton.prototype.createItems.call(this, items, SubsCapsMenuItem);
18004    return items;
18005  };
18006
18007  return SubsCapsButton;
18008}(TextTrackButton);
18009
18010/**
18011 * `kind`s of TextTrack to look for to associate it with this menu.
18012 *
18013 * @type {array}
18014 * @private
18015 */
18016
18017
18018SubsCapsButton.prototype.kinds_ = ['captions', 'subtitles'];
18019
18020/**
18021 * The text that should display over the `SubsCapsButton`s controls.
18022 *
18023 *
18024 * @type {string}
18025 * @private
18026 */
18027SubsCapsButton.prototype.controlText_ = 'Subtitles';
18028
18029Component.registerComponent('SubsCapsButton', SubsCapsButton);
18030
18031/**
18032 * @file audio-track-menu-item.js
18033 */
18034/**
18035 * An {@link AudioTrack} {@link MenuItem}
18036 *
18037 * @extends MenuItem
18038 */
18039
18040var AudioTrackMenuItem = function (_MenuItem) {
18041  inherits(AudioTrackMenuItem, _MenuItem);
18042
18043  /**
18044   * Creates an instance of this class.
18045   *
18046   * @param {Player} player
18047   *        The `Player` that this class should be attached to.
18048   *
18049   * @param {Object} [options]
18050   *        The key/value store of player options.
18051   */
18052  function AudioTrackMenuItem(player, options) {
18053    classCallCheck(this, AudioTrackMenuItem);
18054
18055    var track = options.track;
18056    var tracks = player.audioTracks();
18057
18058    // Modify options for parent MenuItem class's init.
18059    options.label = track.label || track.language || 'Unknown';
18060    options.selected = track.enabled;
18061
18062    var _this = possibleConstructorReturn(this, _MenuItem.call(this, player, options));
18063
18064    _this.track = track;
18065
18066    _this.addClass('vjs-' + track.kind + '-menu-item');
18067
18068    var changeHandler = function changeHandler() {
18069      for (var _len = arguments.length, args = Array(_len), _key = 0; _key < _len; _key++) {
18070        args[_key] = arguments[_key];
18071      }
18072
18073      _this.handleTracksChange.apply(_this, args);
18074    };
18075
18076    tracks.addEventListener('change', changeHandler);
18077    _this.on('dispose', function () {
18078      tracks.removeEventListener('change', changeHandler);
18079    });
18080    return _this;
18081  }
18082
18083  AudioTrackMenuItem.prototype.createEl = function createEl(type, props, attrs) {
18084    var innerHTML = '<span class="vjs-menu-item-text">' + this.localize(this.options_.label);
18085
18086    if (this.options_.track.kind === 'main-desc') {
18087      innerHTML += '\n        <span aria-hidden="true" class="vjs-icon-placeholder"></span>\n        <span class="vjs-control-text"> ' + this.localize('Descriptions') + '</span>\n      ';
18088    }
18089
18090    innerHTML += '</span>';
18091
18092    var el = _MenuItem.prototype.createEl.call(this, type, assign({
18093      innerHTML: innerHTML
18094    }, props), attrs);
18095
18096    return el;
18097  };
18098
18099  /**
18100   * This gets called when an `AudioTrackMenuItem is "clicked". See {@link ClickableComponent}
18101   * for more detailed information on what a click can be.
18102   *
18103   * @param {EventTarget~Event} [event]
18104   *        The `keydown`, `tap`, or `click` event that caused this function to be
18105   *        called.
18106   *
18107   * @listens tap
18108   * @listens click
18109   */
18110
18111
18112  AudioTrackMenuItem.prototype.handleClick = function handleClick(event) {
18113    var tracks = this.player_.audioTracks();
18114
18115    _MenuItem.prototype.handleClick.call(this, event);
18116
18117    for (var i = 0; i < tracks.length; i++) {
18118      var track = tracks[i];
18119
18120      track.enabled = track === this.track;
18121    }
18122  };
18123
18124  /**
18125   * Handle any {@link AudioTrack} change.
18126   *
18127   * @param {EventTarget~Event} [event]
18128   *        The {@link AudioTrackList#change} event that caused this to run.
18129   *
18130   * @listens AudioTrackList#change
18131   */
18132
18133
18134  AudioTrackMenuItem.prototype.handleTracksChange = function handleTracksChange(event) {
18135    this.selected(this.track.enabled);
18136  };
18137
18138  return AudioTrackMenuItem;
18139}(MenuItem);
18140
18141Component.registerComponent('AudioTrackMenuItem', AudioTrackMenuItem);
18142
18143/**
18144 * @file audio-track-button.js
18145 */
18146/**
18147 * The base class for buttons that toggle specific {@link AudioTrack} types.
18148 *
18149 * @extends TrackButton
18150 */
18151
18152var AudioTrackButton = function (_TrackButton) {
18153  inherits(AudioTrackButton, _TrackButton);
18154
18155  /**
18156   * Creates an instance of this class.
18157   *
18158   * @param {Player} player
18159   *        The `Player` that this class should be attached to.
18160   *
18161   * @param {Object} [options={}]
18162   *        The key/value store of player options.
18163   */
18164  function AudioTrackButton(player) {
18165    var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
18166    classCallCheck(this, AudioTrackButton);
18167
18168    options.tracks = player.audioTracks();
18169
18170    return possibleConstructorReturn(this, _TrackButton.call(this, player, options));
18171  }
18172
18173  /**
18174   * Builds the default DOM `className`.
18175   *
18176   * @return {string}
18177   *         The DOM `className` for this object.
18178   */
18179
18180
18181  AudioTrackButton.prototype.buildCSSClass = function buildCSSClass() {
18182    return 'vjs-audio-button ' + _TrackButton.prototype.buildCSSClass.call(this);
18183  };
18184
18185  AudioTrackButton.prototype.buildWrapperCSSClass = function buildWrapperCSSClass() {
18186    return 'vjs-audio-button ' + _TrackButton.prototype.buildWrapperCSSClass.call(this);
18187  };
18188
18189  /**
18190   * Create a menu item for each audio track
18191   *
18192   * @param {AudioTrackMenuItem[]} [items=[]]
18193   *        An array of existing menu items to use.
18194   *
18195   * @return {AudioTrackMenuItem[]}
18196   *         An array of menu items
18197   */
18198
18199
18200  AudioTrackButton.prototype.createItems = function createItems() {
18201    var items = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : [];
18202
18203    // if there's only one audio track, there no point in showing it
18204    this.hideThreshold_ = 1;
18205
18206    var tracks = this.player_.audioTracks();
18207
18208    for (var i = 0; i < tracks.length; i++) {
18209      var track = tracks[i];
18210
18211      items.push(new AudioTrackMenuItem(this.player_, {
18212        track: track,
18213        // MenuItem is selectable
18214        selectable: true,
18215        // MenuItem is NOT multiSelectable (i.e. only one can be marked "selected" at a time)
18216        multiSelectable: false
18217      }));
18218    }
18219
18220    return items;
18221  };
18222
18223  return AudioTrackButton;
18224}(TrackButton);
18225
18226/**
18227 * The text that should display over the `AudioTrackButton`s controls. Added for localization.
18228 *
18229 * @type {string}
18230 * @private
18231 */
18232
18233
18234AudioTrackButton.prototype.controlText_ = 'Audio Track';
18235Component.registerComponent('AudioTrackButton', AudioTrackButton);
18236
18237/**
18238 * @file playback-rate-menu-item.js
18239 */
18240/**
18241 * The specific menu item type for selecting a playback rate.
18242 *
18243 * @extends MenuItem
18244 */
18245
18246var PlaybackRateMenuItem = function (_MenuItem) {
18247  inherits(PlaybackRateMenuItem, _MenuItem);
18248
18249  /**
18250   * Creates an instance of this class.
18251   *
18252   * @param {Player} player
18253   *        The `Player` that this class should be attached to.
18254   *
18255   * @param {Object} [options]
18256   *        The key/value store of player options.
18257   */
18258  function PlaybackRateMenuItem(player, options) {
18259    classCallCheck(this, PlaybackRateMenuItem);
18260
18261    var label = options.rate;
18262    var rate = parseFloat(label, 10);
18263
18264    // Modify options for parent MenuItem class's init.
18265    options.label = label;
18266    options.selected = rate === 1;
18267    options.selectable = true;
18268    options.multiSelectable = false;
18269
18270    var _this = possibleConstructorReturn(this, _MenuItem.call(this, player, options));
18271
18272    _this.label = label;
18273    _this.rate = rate;
18274
18275    _this.on(player, 'ratechange', _this.update);
18276    return _this;
18277  }
18278
18279  /**
18280   * This gets called when an `PlaybackRateMenuItem` is "clicked". See
18281   * {@link ClickableComponent} for more detailed information on what a click can be.
18282   *
18283   * @param {EventTarget~Event} [event]
18284   *        The `keydown`, `tap`, or `click` event that caused this function to be
18285   *        called.
18286   *
18287   * @listens tap
18288   * @listens click
18289   */
18290
18291
18292  PlaybackRateMenuItem.prototype.handleClick = function handleClick(event) {
18293    _MenuItem.prototype.handleClick.call(this);
18294    this.player().playbackRate(this.rate);
18295  };
18296
18297  /**
18298   * Update the PlaybackRateMenuItem when the playbackrate changes.
18299   *
18300   * @param {EventTarget~Event} [event]
18301   *        The `ratechange` event that caused this function to run.
18302   *
18303   * @listens Player#ratechange
18304   */
18305
18306
18307  PlaybackRateMenuItem.prototype.update = function update(event) {
18308    this.selected(this.player().playbackRate() === this.rate);
18309  };
18310
18311  return PlaybackRateMenuItem;
18312}(MenuItem);
18313
18314/**
18315 * The text that should display over the `PlaybackRateMenuItem`s controls. Added for localization.
18316 *
18317 * @type {string}
18318 * @private
18319 */
18320
18321
18322PlaybackRateMenuItem.prototype.contentElType = 'button';
18323
18324Component.registerComponent('PlaybackRateMenuItem', PlaybackRateMenuItem);
18325
18326/**
18327 * @file playback-rate-menu-button.js
18328 */
18329/**
18330 * The component for controlling the playback rate.
18331 *
18332 * @extends MenuButton
18333 */
18334
18335var PlaybackRateMenuButton = function (_MenuButton) {
18336  inherits(PlaybackRateMenuButton, _MenuButton);
18337
18338  /**
18339   * Creates an instance of this class.
18340   *
18341   * @param {Player} player
18342   *        The `Player` that this class should be attached to.
18343   *
18344   * @param {Object} [options]
18345   *        The key/value store of player options.
18346   */
18347  function PlaybackRateMenuButton(player, options) {
18348    classCallCheck(this, PlaybackRateMenuButton);
18349
18350    var _this = possibleConstructorReturn(this, _MenuButton.call(this, player, options));
18351
18352    _this.updateVisibility();
18353    _this.updateLabel();
18354
18355    _this.on(player, 'loadstart', _this.updateVisibility);
18356    _this.on(player, 'ratechange', _this.updateLabel);
18357    return _this;
18358  }
18359
18360  /**
18361   * Create the `Component`'s DOM element
18362   *
18363   * @return {Element}
18364   *         The element that was created.
18365   */
18366
18367
18368  PlaybackRateMenuButton.prototype.createEl = function createEl$$1() {
18369    var el = _MenuButton.prototype.createEl.call(this);
18370
18371    this.labelEl_ = createEl('div', {
18372      className: 'vjs-playback-rate-value',
18373      innerHTML: '1x'
18374    });
18375
18376    el.appendChild(this.labelEl_);
18377
18378    return el;
18379  };
18380
18381  PlaybackRateMenuButton.prototype.dispose = function dispose() {
18382    this.labelEl_ = null;
18383
18384    _MenuButton.prototype.dispose.call(this);
18385  };
18386
18387  /**
18388   * Builds the default DOM `className`.
18389   *
18390   * @return {string}
18391   *         The DOM `className` for this object.
18392   */
18393
18394
18395  PlaybackRateMenuButton.prototype.buildCSSClass = function buildCSSClass() {
18396    return 'vjs-playback-rate ' + _MenuButton.prototype.buildCSSClass.call(this);
18397  };
18398
18399  PlaybackRateMenuButton.prototype.buildWrapperCSSClass = function buildWrapperCSSClass() {
18400    return 'vjs-playback-rate ' + _MenuButton.prototype.buildWrapperCSSClass.call(this);
18401  };
18402
18403  /**
18404   * Create the playback rate menu
18405   *
18406   * @return {Menu}
18407   *         Menu object populated with {@link PlaybackRateMenuItem}s
18408   */
18409
18410
18411  PlaybackRateMenuButton.prototype.createMenu = function createMenu() {
18412    var menu = new Menu(this.player());
18413    var rates = this.playbackRates();
18414
18415    if (rates) {
18416      for (var i = rates.length - 1; i >= 0; i--) {
18417        menu.addChild(new PlaybackRateMenuItem(this.player(), { rate: rates[i] + 'x' }));
18418      }
18419    }
18420
18421    return menu;
18422  };
18423
18424  /**
18425   * Updates ARIA accessibility attributes
18426   */
18427
18428
18429  PlaybackRateMenuButton.prototype.updateARIAAttributes = function updateARIAAttributes() {
18430    // Current playback rate
18431    this.el().setAttribute('aria-valuenow', this.player().playbackRate());
18432  };
18433
18434  /**
18435   * This gets called when an `PlaybackRateMenuButton` is "clicked". See
18436   * {@link ClickableComponent} for more detailed information on what a click can be.
18437   *
18438   * @param {EventTarget~Event} [event]
18439   *        The `keydown`, `tap`, or `click` event that caused this function to be
18440   *        called.
18441   *
18442   * @listens tap
18443   * @listens click
18444   */
18445
18446
18447  PlaybackRateMenuButton.prototype.handleClick = function handleClick(event) {
18448    // select next rate option
18449    var currentRate = this.player().playbackRate();
18450    var rates = this.playbackRates();
18451
18452    // this will select first one if the last one currently selected
18453    var newRate = rates[0];
18454
18455    for (var i = 0; i < rates.length; i++) {
18456      if (rates[i] > currentRate) {
18457        newRate = rates[i];
18458        break;
18459      }
18460    }
18461    this.player().playbackRate(newRate);
18462  };
18463
18464  /**
18465   * Get possible playback rates
18466   *
18467   * @return {Array}
18468   *         All possible playback rates
18469   */
18470
18471
18472  PlaybackRateMenuButton.prototype.playbackRates = function playbackRates() {
18473    return this.options_.playbackRates || this.options_.playerOptions && this.options_.playerOptions.playbackRates;
18474  };
18475
18476  /**
18477   * Get whether playback rates is supported by the tech
18478   * and an array of playback rates exists
18479   *
18480   * @return {boolean}
18481   *         Whether changing playback rate is supported
18482   */
18483
18484
18485  PlaybackRateMenuButton.prototype.playbackRateSupported = function playbackRateSupported() {
18486    return this.player().tech_ && this.player().tech_.featuresPlaybackRate && this.playbackRates() && this.playbackRates().length > 0;
18487  };
18488
18489  /**
18490   * Hide playback rate controls when they're no playback rate options to select
18491   *
18492   * @param {EventTarget~Event} [event]
18493   *        The event that caused this function to run.
18494   *
18495   * @listens Player#loadstart
18496   */
18497
18498
18499  PlaybackRateMenuButton.prototype.updateVisibility = function updateVisibility(event) {
18500    if (this.playbackRateSupported()) {
18501      this.removeClass('vjs-hidden');
18502    } else {
18503      this.addClass('vjs-hidden');
18504    }
18505  };
18506
18507  /**
18508   * Update button label when rate changed
18509   *
18510   * @param {EventTarget~Event} [event]
18511   *        The event that caused this function to run.
18512   *
18513   * @listens Player#ratechange
18514   */
18515
18516
18517  PlaybackRateMenuButton.prototype.updateLabel = function updateLabel(event) {
18518    if (this.playbackRateSupported()) {
18519      this.labelEl_.innerHTML = this.player().playbackRate() + 'x';
18520    }
18521  };
18522
18523  return PlaybackRateMenuButton;
18524}(MenuButton);
18525
18526/**
18527 * The text that should display over the `FullscreenToggle`s controls. Added for localization.
18528 *
18529 * @type {string}
18530 * @private
18531 */
18532
18533
18534PlaybackRateMenuButton.prototype.controlText_ = 'Playback Rate';
18535
18536Component.registerComponent('PlaybackRateMenuButton', PlaybackRateMenuButton);
18537
18538/**
18539 * @file spacer.js
18540 */
18541/**
18542 * Just an empty spacer element that can be used as an append point for plugins, etc.
18543 * Also can be used to create space between elements when necessary.
18544 *
18545 * @extends Component
18546 */
18547
18548var Spacer = function (_Component) {
18549  inherits(Spacer, _Component);
18550
18551  function Spacer() {
18552    classCallCheck(this, Spacer);
18553    return possibleConstructorReturn(this, _Component.apply(this, arguments));
18554  }
18555
18556  /**
18557   * Builds the default DOM `className`.
18558   *
18559   * @return {string}
18560   *         The DOM `className` for this object.
18561   */
18562  Spacer.prototype.buildCSSClass = function buildCSSClass() {
18563    return 'vjs-spacer ' + _Component.prototype.buildCSSClass.call(this);
18564  };
18565
18566  /**
18567   * Create the `Component`'s DOM element
18568   *
18569   * @return {Element}
18570   *         The element that was created.
18571   */
18572
18573
18574  Spacer.prototype.createEl = function createEl() {
18575    return _Component.prototype.createEl.call(this, 'div', {
18576      className: this.buildCSSClass()
18577    });
18578  };
18579
18580  return Spacer;
18581}(Component);
18582
18583Component.registerComponent('Spacer', Spacer);
18584
18585/**
18586 * @file custom-control-spacer.js
18587 */
18588/**
18589 * Spacer specifically meant to be used as an insertion point for new plugins, etc.
18590 *
18591 * @extends Spacer
18592 */
18593
18594var CustomControlSpacer = function (_Spacer) {
18595  inherits(CustomControlSpacer, _Spacer);
18596
18597  function CustomControlSpacer() {
18598    classCallCheck(this, CustomControlSpacer);
18599    return possibleConstructorReturn(this, _Spacer.apply(this, arguments));
18600  }
18601
18602  /**
18603   * Builds the default DOM `className`.
18604   *
18605   * @return {string}
18606   *         The DOM `className` for this object.
18607   */
18608  CustomControlSpacer.prototype.buildCSSClass = function buildCSSClass() {
18609    return 'vjs-custom-control-spacer ' + _Spacer.prototype.buildCSSClass.call(this);
18610  };
18611
18612  /**
18613   * Create the `Component`'s DOM element
18614   *
18615   * @return {Element}
18616   *         The element that was created.
18617   */
18618
18619
18620  CustomControlSpacer.prototype.createEl = function createEl() {
18621    var el = _Spacer.prototype.createEl.call(this, {
18622      className: this.buildCSSClass()
18623    });
18624
18625    // No-flex/table-cell mode requires there be some content
18626    // in the cell to fill the remaining space of the table.
18627    el.innerHTML = '\xA0';
18628    return el;
18629  };
18630
18631  return CustomControlSpacer;
18632}(Spacer);
18633
18634Component.registerComponent('CustomControlSpacer', CustomControlSpacer);
18635
18636/**
18637 * @file control-bar.js
18638 */
18639// Required children
18640/**
18641 * Container of main controls.
18642 *
18643 * @extends Component
18644 */
18645
18646var ControlBar = function (_Component) {
18647  inherits(ControlBar, _Component);
18648
18649  function ControlBar() {
18650    classCallCheck(this, ControlBar);
18651    return possibleConstructorReturn(this, _Component.apply(this, arguments));
18652  }
18653
18654  /**
18655   * Create the `Component`'s DOM element
18656   *
18657   * @return {Element}
18658   *         The element that was created.
18659   */
18660  ControlBar.prototype.createEl = function createEl() {
18661    return _Component.prototype.createEl.call(this, 'div', {
18662      className: 'vjs-control-bar',
18663      dir: 'ltr'
18664    });
18665  };
18666
18667  return ControlBar;
18668}(Component);
18669
18670/**
18671 * Default options for `ControlBar`
18672 *
18673 * @type {Object}
18674 * @private
18675 */
18676
18677
18678ControlBar.prototype.options_ = {
18679  children: ['playToggle', 'volumePanel', 'currentTimeDisplay', 'timeDivider', 'durationDisplay', 'progressControl', 'liveDisplay', 'remainingTimeDisplay', 'customControlSpacer', 'playbackRateMenuButton', 'chaptersButton', 'descriptionsButton', 'subsCapsButton', 'audioTrackButton', 'fullscreenToggle']
18680};
18681
18682Component.registerComponent('ControlBar', ControlBar);
18683
18684/**
18685 * @file error-display.js
18686 */
18687/**
18688 * A display that indicates an error has occurred. This means that the video
18689 * is unplayable.
18690 *
18691 * @extends ModalDialog
18692 */
18693
18694var ErrorDisplay = function (_ModalDialog) {
18695  inherits(ErrorDisplay, _ModalDialog);
18696
18697  /**
18698   * Creates an instance of this class.
18699   *
18700   * @param  {Player} player
18701   *         The `Player` that this class should be attached to.
18702   *
18703   * @param  {Object} [options]
18704   *         The key/value store of player options.
18705   */
18706  function ErrorDisplay(player, options) {
18707    classCallCheck(this, ErrorDisplay);
18708
18709    var _this = possibleConstructorReturn(this, _ModalDialog.call(this, player, options));
18710
18711    _this.on(player, 'error', _this.open);
18712    return _this;
18713  }
18714
18715  /**
18716   * Builds the default DOM `className`.
18717   *
18718   * @return {string}
18719   *         The DOM `className` for this object.
18720   *
18721   * @deprecated Since version 5.
18722   */
18723
18724
18725  ErrorDisplay.prototype.buildCSSClass = function buildCSSClass() {
18726    return 'vjs-error-display ' + _ModalDialog.prototype.buildCSSClass.call(this);
18727  };
18728
18729  /**
18730   * Gets the localized error message based on the `Player`s error.
18731   *
18732   * @return {string}
18733   *         The `Player`s error message localized or an empty string.
18734   */
18735
18736
18737  ErrorDisplay.prototype.content = function content() {
18738    var error = this.player().error();
18739
18740    return error ? this.localize(error.message) : '';
18741  };
18742
18743  return ErrorDisplay;
18744}(ModalDialog);
18745
18746/**
18747 * The default options for an `ErrorDisplay`.
18748 *
18749 * @private
18750 */
18751
18752
18753ErrorDisplay.prototype.options_ = mergeOptions(ModalDialog.prototype.options_, {
vendor: 5,705 bytes, lines 18754-18960
18754  pauseOnOpen: false,
18755  fillAlways: true,
18756  temporary: false,
18757  uncloseable: true
18758});
18759
18760Component.registerComponent('ErrorDisplay', ErrorDisplay);
18761
18762/**
18763 * @file text-track-settings.js
18764 */
18765var LOCAL_STORAGE_KEY = 'vjs-text-track-settings';
18766
18767var COLOR_BLACK = ['#000', 'Black'];
18768var COLOR_BLUE = ['#00F', 'Blue'];
18769var COLOR_CYAN = ['#0FF', 'Cyan'];
18770var COLOR_GREEN = ['#0F0', 'Green'];
18771var COLOR_MAGENTA = ['#F0F', 'Magenta'];
18772var COLOR_RED = ['#F00', 'Red'];
18773var COLOR_WHITE = ['#FFF', 'White'];
18774var COLOR_YELLOW = ['#FF0', 'Yellow'];
18775
18776var OPACITY_OPAQUE = ['1', 'Opaque'];
18777var OPACITY_SEMI = ['0.5', 'Semi-Transparent'];
18778var OPACITY_TRANS = ['0', 'Transparent'];
18779
18780// Configuration for the various <select> elements in the DOM of this component.
18781//
18782// Possible keys include:
18783//
18784// `default`:
18785//   The default option index. Only needs to be provided if not zero.
18786// `parser`:
18787//   A function which is used to parse the value from the selected option in
18788//   a customized way.
18789// `selector`:
18790//   The selector used to find the associated <select> element.
18791var selectConfigs = {
18792  backgroundColor: {
18793    selector: '.vjs-bg-color > select',
18794    id: 'captions-background-color-%s',
18795    label: 'Color',
18796    options: [COLOR_BLACK, COLOR_WHITE, COLOR_RED, COLOR_GREEN, COLOR_BLUE, COLOR_YELLOW, COLOR_MAGENTA, COLOR_CYAN]
18797  },
18798
18799  backgroundOpacity: {
18800    selector: '.vjs-bg-opacity > select',
18801    id: 'captions-background-opacity-%s',
18802    label: 'Transparency',
18803    options: [OPACITY_OPAQUE, OPACITY_SEMI, OPACITY_TRANS]
18804  },
18805
18806  color: {
18807    selector: '.vjs-fg-color > select',
18808    id: 'captions-foreground-color-%s',
18809    label: 'Color',
18810    options: [COLOR_WHITE, COLOR_BLACK, COLOR_RED, COLOR_GREEN, COLOR_BLUE, COLOR_YELLOW, COLOR_MAGENTA, COLOR_CYAN]
18811  },
18812
18813  edgeStyle: {
18814    selector: '.vjs-edge-style > select',
18815    id: '%s',
18816    label: 'Text Edge Style',
18817    options: [['none', 'None'], ['raised', 'Raised'], ['depressed', 'Depressed'], ['uniform', 'Uniform'], ['dropshadow', 'Dropshadow']]
18818  },
18819
18820  fontFamily: {
18821    selector: '.vjs-font-family > select',
18822    id: 'captions-font-family-%s',
18823    label: 'Font Family',
18824    options: [['proportionalSansSerif', 'Proportional Sans-Serif'], ['monospaceSansSerif', 'Monospace Sans-Serif'], ['proportionalSerif', 'Proportional Serif'], ['monospaceSerif', 'Monospace Serif'], ['casual', 'Casual'], ['script', 'Script'], ['small-caps', 'Small Caps']]
18825  },
18826
18827  fontPercent: {
18828    selector: '.vjs-font-percent > select',
18829    id: 'captions-font-size-%s',
18830    label: 'Font Size',
18831    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%']],
18832    'default': 2,
18833    parser: function parser(v) {
18834      return v === '1.00' ? null : Number(v);
18835    }
18836  },
18837
18838  textOpacity: {
18839    selector: '.vjs-text-opacity > select',
18840    id: 'captions-foreground-opacity-%s',
18841    label: 'Transparency',
18842    options: [OPACITY_OPAQUE, OPACITY_SEMI]
18843  },
18844
18845  // Options for this object are defined below.
18846  windowColor: {
18847    selector: '.vjs-window-color > select',
18848    id: 'captions-window-color-%s',
18849    label: 'Color'
18850  },
18851
18852  // Options for this object are defined below.
18853  windowOpacity: {
18854    selector: '.vjs-window-opacity > select',
18855    id: 'captions-window-opacity-%s',
18856    label: 'Transparency',
18857    options: [OPACITY_TRANS, OPACITY_SEMI, OPACITY_OPAQUE]
18858  }
18859};
18860
18861selectConfigs.windowColor.options = selectConfigs.backgroundColor.options;
18862
18863/**
18864 * Get the actual value of an option.
18865 *
18866 * @param  {string} value
18867 *         The value to get
18868 *
18869 * @param  {Function} [parser]
18870 *         Optional function to adjust the value.
18871 *
18872 * @return {Mixed}
18873 *         - Will be `undefined` if no value exists
18874 *         - Will be `undefined` if the given value is "none".
18875 *         - Will be the actual value otherwise.
18876 *
18877 * @private
18878 */
18879function parseOptionValue(value, parser) {
18880  if (parser) {
18881    value = parser(value);
18882  }
18883
18884  if (value && value !== 'none') {
18885    return value;
18886  }
18887}
18888
18889/**
18890 * Gets the value of the selected <option> element within a <select> element.
18891 *
18892 * @param  {Element} el
18893 *         the element to look in
18894 *
18895 * @param  {Function} [parser]
18896 *         Optional function to adjust the value.
18897 *
18898 * @return {Mixed}
18899 *         - Will be `undefined` if no value exists
18900 *         - Will be `undefined` if the given value is "none".
18901 *         - Will be the actual value otherwise.
18902 *
18903 * @private
18904 */
18905function getSelectedOptionValue(el, parser) {
18906  var value = el.options[el.options.selectedIndex].value;
18907
18908  return parseOptionValue(value, parser);
18909}
18910
18911/**
18912 * Sets the selected <option> element within a <select> element based on a
18913 * given value.
18914 *
18915 * @param {Element} el
18916 *        The element to look in.
18917 *
18918 * @param {string} value
18919 *        the property to look on.
18920 *
18921 * @param {Function} [parser]
18922 *        Optional function to adjust the value before comparing.
18923 *
18924 * @private
18925 */
18926function setSelectedOption(el, value, parser) {
18927  if (!value) {
18928    return;
18929  }
18930
18931  for (var i = 0; i < el.options.length; i++) {
18932    if (parseOptionValue(el.options[i].value, parser) === value) {
18933      el.selectedIndex = i;
18934      break;
18935    }
18936  }
18937}
18938
18939/**
18940 * Manipulate Text Tracks settings.
18941 *
18942 * @extends ModalDialog
18943 */
18944
18945var TextTrackSettings = function (_ModalDialog) {
18946  inherits(TextTrackSettings, _ModalDialog);
18947
18948  /**
18949   * Creates an instance of this class.
18950   *
18951   * @param {Player} player
18952   *         The `Player` that this class should be attached to.
18953   *
18954   * @param {Object} [options]
18955   *         The key/value store of player options.
18956   */
18957  function TextTrackSettings(player, options) {
18958    classCallCheck(this, TextTrackSettings);
18959
18960    options.temporary = false;
vendor: 15,421 bytes, lines 18961-19455
18961
18962    var _this = possibleConstructorReturn(this, _ModalDialog.call(this, player, options));
18963
18964    _this.updateDisplay = bind(_this, _this.updateDisplay);
18965
18966    // fill the modal and pretend we have opened it
18967    _this.fill();
18968    _this.hasBeenOpened_ = _this.hasBeenFilled_ = true;
18969
18970    _this.endDialog = createEl('p', {
18971      className: 'vjs-control-text',
18972      textContent: _this.localize('End of dialog window.')
18973    });
18974    _this.el().appendChild(_this.endDialog);
18975
18976    _this.setDefaults();
18977
18978    // Grab `persistTextTrackSettings` from the player options if not passed in child options
18979    if (options.persistTextTrackSettings === undefined) {
18980      _this.options_.persistTextTrackSettings = _this.options_.playerOptions.persistTextTrackSettings;
18981    }
18982
18983    _this.on(_this.$('.vjs-done-button'), 'click', function () {
18984      _this.saveSettings();
18985      _this.close();
18986    });
18987
18988    _this.on(_this.$('.vjs-default-button'), 'click', function () {
18989      _this.setDefaults();
18990      _this.updateDisplay();
18991    });
18992
18993    each(selectConfigs, function (config) {
18994      _this.on(_this.$(config.selector), 'change', _this.updateDisplay);
18995    });
18996
18997    if (_this.options_.persistTextTrackSettings) {
18998      _this.restoreSettings();
18999    }
19000    return _this;
19001  }
19002
19003  TextTrackSettings.prototype.dispose = function dispose() {
19004    this.endDialog = null;
19005
19006    _ModalDialog.prototype.dispose.call(this);
19007  };
19008
19009  /**
19010   * Create a <select> element with configured options.
19011   *
19012   * @param {string} key
19013   *        Configuration key to use during creation.
19014   *
19015   * @return {string}
19016   *         An HTML string.
19017   *
19018   * @private
19019   */
19020
19021
19022  TextTrackSettings.prototype.createElSelect_ = function createElSelect_(key) {
19023    var _this2 = this;
19024
19025    var legendId = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : '';
19026    var type = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 'label';
19027
19028    var config = selectConfigs[key];
19029    var id = config.id.replace('%s', this.id_);
19030    var selectLabelledbyIds = [legendId, id].join(' ').trim();
19031
19032    return ['<' + type + ' id="' + id + '" class="' + (type === 'label' ? 'vjs-label' : '') + '">', this.localize(config.label), '</' + type + '>', '<select aria-labelledby="' + selectLabelledbyIds + '">'].concat(config.options.map(function (o) {
19033      var optionId = id + '-' + o[1].replace(/\W+/g, '');
19034
19035      return ['<option id="' + optionId + '" value="' + o[0] + '" ', 'aria-labelledby="' + selectLabelledbyIds + ' ' + optionId + '">', _this2.localize(o[1]), '</option>'].join('');
19036    })).concat('</select>').join('');
19037  };
19038
19039  /**
19040   * Create foreground color element for the component
19041   *
19042   * @return {string}
19043   *         An HTML string.
19044   *
19045   * @private
19046   */
19047
19048
19049  TextTrackSettings.prototype.createElFgColor_ = function createElFgColor_() {
19050    var legendId = 'captions-text-legend-' + this.id_;
19051
19052    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('');
19053  };
19054
19055  /**
19056   * Create background color element for the component
19057   *
19058   * @return {string}
19059   *         An HTML string.
19060   *
19061   * @private
19062   */
19063
19064
19065  TextTrackSettings.prototype.createElBgColor_ = function createElBgColor_() {
19066    var legendId = 'captions-background-' + this.id_;
19067
19068    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('');
19069  };
19070
19071  /**
19072   * Create window color element for the component
19073   *
19074   * @return {string}
19075   *         An HTML string.
19076   *
19077   * @private
19078   */
19079
19080
19081  TextTrackSettings.prototype.createElWinColor_ = function createElWinColor_() {
19082    var legendId = 'captions-window-' + this.id_;
19083
19084    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('');
19085  };
19086
19087  /**
19088   * Create color elements for the component
19089   *
19090   * @return {Element}
19091   *         The element that was created
19092   *
19093   * @private
19094   */
19095
19096
19097  TextTrackSettings.prototype.createElColors_ = function createElColors_() {
19098    return createEl('div', {
19099      className: 'vjs-track-settings-colors',
19100      innerHTML: [this.createElFgColor_(), this.createElBgColor_(), this.createElWinColor_()].join('')
19101    });
19102  };
19103
19104  /**
19105   * Create font elements for the component
19106   *
19107   * @return {Element}
19108   *         The element that was created.
19109   *
19110   * @private
19111   */
19112
19113
19114  TextTrackSettings.prototype.createElFont_ = function createElFont_() {
19115    return createEl('div', {
19116      className: 'vjs-track-settings-font',
19117      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('')
19118    });
19119  };
19120
19121  /**
19122   * Create controls for the component
19123   *
19124   * @return {Element}
19125   *         The element that was created.
19126   *
19127   * @private
19128   */
19129
19130
19131  TextTrackSettings.prototype.createElControls_ = function createElControls_() {
19132    var defaultsDescription = this.localize('restore all settings to the default values');
19133
19134    return createEl('div', {
19135      className: 'vjs-track-settings-controls',
19136      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('')
19137    });
19138  };
19139
19140  TextTrackSettings.prototype.content = function content() {
19141    return [this.createElColors_(), this.createElFont_(), this.createElControls_()];
19142  };
19143
19144  TextTrackSettings.prototype.label = function label() {
19145    return this.localize('Caption Settings Dialog');
19146  };
19147
19148  TextTrackSettings.prototype.description = function description() {
19149    return this.localize('Beginning of dialog window. Escape will cancel and close the window.');
19150  };
19151
19152  TextTrackSettings.prototype.buildCSSClass = function buildCSSClass() {
19153    return _ModalDialog.prototype.buildCSSClass.call(this) + ' vjs-text-track-settings';
19154  };
19155
19156  /**
19157   * Gets an object of text track settings (or null).
19158   *
19159   * @return {Object}
19160   *         An object with config values parsed from the DOM or localStorage.
19161   */
19162
19163
19164  TextTrackSettings.prototype.getValues = function getValues() {
19165    var _this3 = this;
19166
19167    return reduce(selectConfigs, function (accum, config, key) {
19168      var value = getSelectedOptionValue(_this3.$(config.selector), config.parser);
19169
19170      if (value !== undefined) {
19171        accum[key] = value;
19172      }
19173
19174      return accum;
19175    }, {});
19176  };
19177
19178  /**
19179   * Sets text track settings from an object of values.
19180   *
19181   * @param {Object} values
19182   *        An object with config values parsed from the DOM or localStorage.
19183   */
19184
19185
19186  TextTrackSettings.prototype.setValues = function setValues(values) {
19187    var _this4 = this;
19188
19189    each(selectConfigs, function (config, key) {
19190      setSelectedOption(_this4.$(config.selector), values[key], config.parser);
19191    });
19192  };
19193
19194  /**
19195   * Sets all `<select>` elements to their default values.
19196   */
19197
19198
19199  TextTrackSettings.prototype.setDefaults = function setDefaults() {
19200    var _this5 = this;
19201
19202    each(selectConfigs, function (config) {
19203      var index = config.hasOwnProperty('default') ? config['default'] : 0;
19204
19205      _this5.$(config.selector).selectedIndex = index;
19206    });
19207  };
19208
19209  /**
19210   * Restore texttrack settings from localStorage
19211   */
19212
19213
19214  TextTrackSettings.prototype.restoreSettings = function restoreSettings() {
19215    var values = void 0;
19216
19217    try {
19218      values = JSON.parse(window_1.localStorage.getItem(LOCAL_STORAGE_KEY));
19219    } catch (err) {
19220      log$1.warn(err);
19221    }
19222
19223    if (values) {
19224      this.setValues(values);
19225    }
19226  };
19227
19228  /**
19229   * Save text track settings to localStorage
19230   */
19231
19232
19233  TextTrackSettings.prototype.saveSettings = function saveSettings() {
19234    if (!this.options_.persistTextTrackSettings) {
19235      return;
19236    }
19237
19238    var values = this.getValues();
19239
19240    try {
19241      if (Object.keys(values).length) {
19242        window_1.localStorage.setItem(LOCAL_STORAGE_KEY, JSON.stringify(values));
19243      } else {
19244        window_1.localStorage.removeItem(LOCAL_STORAGE_KEY);
19245      }
19246    } catch (err) {
19247      log$1.warn(err);
19248    }
19249  };
19250
19251  /**
19252   * Update display of text track settings
19253   */
19254
19255
19256  TextTrackSettings.prototype.updateDisplay = function updateDisplay() {
19257    var ttDisplay = this.player_.getChild('textTrackDisplay');
19258
19259    if (ttDisplay) {
19260      ttDisplay.updateDisplay();
19261    }
19262  };
19263
19264  /**
19265   * conditionally blur the element and refocus the captions button
19266   *
19267   * @private
19268   */
19269
19270
19271  TextTrackSettings.prototype.conditionalBlur_ = function conditionalBlur_() {
19272    this.previouslyActiveEl_ = null;
19273    this.off(document_1, 'keydown', this.handleKeyDown);
19274
19275    var cb = this.player_.controlBar;
19276    var subsCapsBtn = cb && cb.subsCapsButton;
19277    var ccBtn = cb && cb.captionsButton;
19278
19279    if (subsCapsBtn) {
19280      subsCapsBtn.focus();
19281    } else if (ccBtn) {
19282      ccBtn.focus();
19283    }
19284  };
19285
19286  return TextTrackSettings;
19287}(ModalDialog);
19288
19289Component.registerComponent('TextTrackSettings', TextTrackSettings);
19290
19291/**
19292 * @file resize-manager.js
19293 */
19294/**
19295 * A Resize Manager. It is in charge of triggering `playerresize` on the player in the right conditions.
19296 *
19297 * It'll either create an iframe and use a debounced resize handler on it or use the new {@link https://wicg.github.io/ResizeObserver/|ResizeObserver}.
19298 *
19299 * If the ResizeObserver is available natively, it will be used. A polyfill can be passed in as an option.
19300 * If a `playerresize` event is not needed, the ResizeManager component can be removed from the player, see the example below.
19301 * @example <caption>How to disable the resize manager</caption>
19302 * const player = videojs('#vid', {
19303 *   resizeManager: false
19304 * });
19305 *
19306 * @see {@link https://wicg.github.io/ResizeObserver/|ResizeObserver specification}
19307 *
19308 * @extends Component
19309 */
19310
19311var ResizeManager = function (_Component) {
19312  inherits(ResizeManager, _Component);
19313
19314  /**
19315   * Create the ResizeManager.
19316   *
19317   * @param {Object} player
19318   *        The `Player` that this class should be attached to.
19319   *
19320   * @param {Object} [options]
19321   *        The key/value store of ResizeManager options.
19322   *
19323   * @param {Object} [options.ResizeObserver]
19324   *        A polyfill for ResizeObserver can be passed in here.
19325   *        If this is set to null it will ignore the native ResizeObserver and fall back to the iframe fallback.
19326   */
19327  function ResizeManager(player, options) {
19328    classCallCheck(this, ResizeManager);
19329
19330    var RESIZE_OBSERVER_AVAILABLE = options.ResizeObserver || window_1.ResizeObserver;
19331
19332    // if `null` was passed, we want to disable the ResizeObserver
19333    if (options.ResizeObserver === null) {
19334      RESIZE_OBSERVER_AVAILABLE = false;
19335    }
19336
19337    // Only create an element when ResizeObserver isn't available
19338    var options_ = mergeOptions({ createEl: !RESIZE_OBSERVER_AVAILABLE }, options);
19339
19340    var _this = possibleConstructorReturn(this, _Component.call(this, player, options_));
19341
19342    _this.ResizeObserver = options.ResizeObserver || window_1.ResizeObserver;
19343    _this.loadListener_ = null;
19344    _this.resizeObserver_ = null;
19345    _this.debouncedHandler_ = debounce(function () {
19346      _this.resizeHandler();
19347    }, 100, false, player);
19348
19349    if (RESIZE_OBSERVER_AVAILABLE) {
19350      _this.resizeObserver_ = new _this.ResizeObserver(_this.debouncedHandler_);
19351      _this.resizeObserver_.observe(player.el());
19352    } else {
19353      _this.loadListener_ = function () {
19354        if (_this.el_.contentWindow) {
19355          on(_this.el_.contentWindow, 'resize', _this.debouncedHandler_);
19356        }
19357        _this.off('load', _this.loadListener_);
19358      };
19359
19360      _this.on('load', _this.loadListener_);
19361    }
19362    return _this;
19363  }
19364
19365  ResizeManager.prototype.createEl = function createEl() {
19366    return _Component.prototype.createEl.call(this, 'iframe', {
19367      className: 'vjs-resize-manager'
19368    });
19369  };
19370
19371  /**
19372   * Called when a resize is triggered on the iframe or a resize is observed via the ResizeObserver
19373   *
19374   * @fires Player#playerresize
19375   */
19376
19377
19378  ResizeManager.prototype.resizeHandler = function resizeHandler() {
19379    /**
19380     * Called when the player size has changed
19381     *
19382     * @event Player#playerresize
19383     * @type {EventTarget~Event}
19384     */
19385    this.player_.trigger('playerresize');
19386  };
19387
19388  ResizeManager.prototype.dispose = function dispose() {
19389    if (this.resizeObserver_) {
19390      if (this.player_.el()) {
19391        this.resizeObserver_.unobserve(this.player_.el());
19392      }
19393      this.resizeObserver_.disconnect();
19394    }
19395
19396    if (this.el_ && this.el_.contentWindow) {
19397      off(this.el_.contentWindow, 'resize', this.debouncedHandler_);
19398    }
19399
19400    if (this.loadListener_) {
19401      this.off('load', this.loadListener_);
19402    }
19403
19404    this.ResizeObserver = null;
19405    this.resizeObserver = null;
19406    this.debouncedHandler_ = null;
19407    this.loadListener_ = null;
19408  };
19409
19410  return ResizeManager;
19411}(Component);
19412
19413Component.registerComponent('ResizeManager', ResizeManager);
19414
19415/**
19416 * This function is used to fire a sourceset when there is something
19417 * similar to `mediaEl.load()` being called. It will try to find the source via
19418 * the `src` attribute and then the `<source>` elements. It will then fire `sourceset`
19419 * with the source that was found or empty string if we cannot know. If it cannot
19420 * find a source then `sourceset` will not be fired.
19421 *
19422 * @param {Html5} tech
19423 *        The tech object that sourceset was setup on
19424 *
19425 * @return {boolean}
19426 *         returns false if the sourceset was not fired and true otherwise.
19427 */
19428var sourcesetLoad = function sourcesetLoad(tech) {
19429  var el = tech.el();
19430
19431  // if `el.src` is set, that source will be loaded.
19432  if (el.hasAttribute('src')) {
19433    tech.triggerSourceset(el.src);
19434    return true;
19435  }
19436
19437  /**
19438   * Since there isn't a src property on the media element, source elements will be used for
19439   * implementing the source selection algorithm. This happens asynchronously and
19440   * for most cases were there is more than one source we cannot tell what source will
19441   * be loaded, without re-implementing the source selection algorithm. At this time we are not
19442   * going to do that. There are three special cases that we do handle here though:
19443   *
19444   * 1. If there are no sources, do not fire `sourceset`.
19445   * 2. If there is only one `<source>` with a `src` property/attribute that is our `src`
19446   * 3. If there is more than one `<source>` but all of them have the same `src` url.
19447   *    That will be our src.
19448   */
19449  var sources = tech.$$('source');
19450  var srcUrls = [];
19451  var src = '';
19452
19453  // if there are no sources, do not fire sourceset
19454  if (!sources.length) {
19455    return false;
vendor: 4,312 bytes, lines 19456-19606
19456  }
19457
19458  // only count valid/non-duplicate source elements
19459  for (var i = 0; i < sources.length; i++) {
19460    var url = sources[i].src;
19461
19462    if (url && srcUrls.indexOf(url) === -1) {
19463      srcUrls.push(url);
19464    }
19465  }
19466
19467  // there were no valid sources
19468  if (!srcUrls.length) {
19469    return false;
19470  }
19471
19472  // there is only one valid source element url
19473  // use that
19474  if (srcUrls.length === 1) {
19475    src = srcUrls[0];
19476  }
19477
19478  tech.triggerSourceset(src);
19479  return true;
19480};
19481
19482/**
19483 * our implementation of an `innerHTML` descriptor for browsers
19484 * that do not have one.
19485 */
19486var innerHTMLDescriptorPolyfill = {};
19487
19488if (!IS_IE8) {
19489  innerHTMLDescriptorPolyfill = Object.defineProperty({}, 'innerHTML', {
19490    get: function get() {
19491      return this.cloneNode(true).innerHTML;
19492    },
19493    set: function set(v) {
19494      // make a dummy node to use innerHTML on
19495      var dummy = document_1.createElement(this.nodeName.toLowerCase());
19496
19497      // set innerHTML to the value provided
19498      dummy.innerHTML = v;
19499
19500      // make a document fragment to hold the nodes from dummy
19501      var docFrag = document_1.createDocumentFragment();
19502
19503      // copy all of the nodes created by the innerHTML on dummy
19504      // to the document fragment
19505      while (dummy.childNodes.length) {
19506        docFrag.appendChild(dummy.childNodes[0]);
19507      }
19508
19509      // remove content
19510      this.innerText = '';
19511
19512      // now we add all of that html in one by appending the
19513      // document fragment. This is how innerHTML does it.
19514      window_1.Element.prototype.appendChild.call(this, docFrag);
19515
19516      // then return the result that innerHTML's setter would
19517      return this.innerHTML;
19518    }
19519  });
19520}
19521/**
19522 * Get a property descriptor given a list of priorities and the
19523 * property to get.
19524 */
19525var getDescriptor = function getDescriptor(priority, prop) {
19526  var descriptor = {};
19527
19528  for (var i = 0; i < priority.length; i++) {
19529    descriptor = Object.getOwnPropertyDescriptor(priority[i], prop);
19530
19531    if (descriptor && descriptor.set && descriptor.get) {
19532      break;
19533    }
19534  }
19535
19536  descriptor.enumerable = true;
19537  descriptor.configurable = true;
19538
19539  return descriptor;
19540};
19541
19542var getInnerHTMLDescriptor = function getInnerHTMLDescriptor(tech) {
19543  return getDescriptor([tech.el(), window_1.HTMLMediaElement.prototype, window_1.Element.prototype, innerHTMLDescriptorPolyfill], 'innerHTML');
19544};
19545
19546/**
19547 * Patches browser internal functions so that we can tell syncronously
19548 * if a `<source>` was appended to the media element. For some reason this
19549 * causes a `sourceset` if the the media element is ready and has no source.
19550 * This happens when:
19551 * - The page has just loaded and the media element does not have a source.
19552 * - The media element was emptied of all sources, then `load()` was called.
19553 *
19554 * It does this by patching the following functions/properties when they are supported:
19555 *
19556 * - `append()` - can be used to add a `<source>` element to the media element
19557 * - `appendChild()` - can be used to add a `<source>` element to the media element
19558 * - `insertAdjacentHTML()` -  can be used to add a `<source>` element to the media element
19559 * - `innerHTML` -  can be used to add a `<source>` element to the media element
19560 *
19561 * @param {Html5} tech
19562 *        The tech object that sourceset is being setup on.
19563 */
19564var firstSourceWatch = function firstSourceWatch(tech) {
19565  var el = tech.el();
19566
19567  // make sure firstSourceWatch isn't setup twice.
19568  if (el.resetSourceWatch_) {
19569    return;
19570  }
19571
19572  var old = {};
19573  var innerDescriptor = getInnerHTMLDescriptor(tech);
19574  var appendWrapper = function appendWrapper(appendFn) {
19575    return function () {
19576      for (var _len = arguments.length, args = Array(_len), _key = 0; _key < _len; _key++) {
19577        args[_key] = arguments[_key];
19578      }
19579
19580      var retval = appendFn.apply(el, args);
19581
19582      sourcesetLoad(tech);
19583
19584      return retval;
19585    };
19586  };
19587
19588  ['append', 'appendChild', 'insertAdjacentHTML'].forEach(function (k) {
19589    if (!el[k]) {
19590      return;
19591    }
19592
19593    // store the old function
19594    old[k] = el[k];
19595
19596    // call the old function with a sourceset if a source
19597    // was loaded
19598    el[k] = appendWrapper(old[k]);
19599  });
19600
19601  Object.defineProperty(el, 'innerHTML', mergeOptions(innerDescriptor, {
19602    set: appendWrapper(innerDescriptor.set)
19603  }));
19604
19605  el.resetSourceWatch_ = function () {
19606    el.resetSourceWatch_ = null;
vendor: 33,747 bytes, lines 19607-20726
19607    Object.keys(old).forEach(function (k) {
19608      el[k] = old[k];
19609    });
19610
19611    Object.defineProperty(el, 'innerHTML', innerDescriptor);
19612  };
19613
19614  // on the first sourceset, we need to revert our changes
19615  tech.one('sourceset', el.resetSourceWatch_);
19616};
19617
19618/**
19619 * our implementation of a `src` descriptor for browsers
19620 * that do not have one.
19621 */
19622
19623var srcDescriptorPolyfill = {};
19624
19625if (!IS_IE8) {
19626  srcDescriptorPolyfill = Object.defineProperty({}, 'src', {
19627    get: function get() {
19628      if (this.hasAttribute('src')) {
19629        return getAbsoluteURL(window_1.Element.prototype.getAttribute.call(this, 'src'));
19630      }
19631
19632      return '';
19633    },
19634    set: function set(v) {
19635      window_1.Element.prototype.setAttribute.call(this, 'src', v);
19636
19637      return v;
19638    }
19639  });
19640}
19641
19642var getSrcDescriptor = function getSrcDescriptor(tech) {
19643  return getDescriptor([tech.el(), window_1.HTMLMediaElement.prototype, srcDescriptorPolyfill], 'src');
19644};
19645
19646/**
19647 * setup `sourceset` handling on the `Html5` tech. This function
19648 * patches the following element properties/functions:
19649 *
19650 * - `src` - to determine when `src` is set
19651 * - `setAttribute()` - to determine when `src` is set
19652 * - `load()` - this re-triggers the source selection algorithm, and can
19653 *              cause a sourceset.
19654 *
19655 * If there is no source when we are adding `sourceset` support or during a `load()`
19656 * we also patch the functions listed in `firstSourceWatch`.
19657 *
19658 * @param {Html5} tech
19659 *        The tech to patch
19660 */
19661var setupSourceset = function setupSourceset(tech) {
19662  if (!tech.featuresSourceset) {
19663    return;
19664  }
19665
19666  var el = tech.el();
19667
19668  // make sure sourceset isn't setup twice.
19669  if (el.resetSourceset_) {
19670    return;
19671  }
19672
19673  var srcDescriptor = getSrcDescriptor(tech);
19674  var oldSetAttribute = el.setAttribute;
19675  var oldLoad = el.load;
19676
19677  Object.defineProperty(el, 'src', mergeOptions(srcDescriptor, {
19678    set: function set(v) {
19679      var retval = srcDescriptor.set.call(el, v);
19680
19681      // we use the getter here to get the actual value set on src
19682      tech.triggerSourceset(el.src);
19683
19684      return retval;
19685    }
19686  }));
19687
19688  el.setAttribute = function (n, v) {
19689    var retval = oldSetAttribute.call(el, n, v);
19690
19691    if (/src/i.test(n)) {
19692      tech.triggerSourceset(el.src);
19693    }
19694
19695    return retval;
19696  };
19697
19698  el.load = function () {
19699    var retval = oldLoad.call(el);
19700
19701    // if load was called, but there was no source to fire
19702    // sourceset on. We have to watch for a source append
19703    // as that can trigger a `sourceset` when the media element
19704    // has no source
19705    if (!sourcesetLoad(tech)) {
19706      tech.triggerSourceset('');
19707      firstSourceWatch(tech);
19708    }
19709
19710    return retval;
19711  };
19712
19713  if (el.currentSrc) {
19714    tech.triggerSourceset(el.currentSrc);
19715  } else if (!sourcesetLoad(tech)) {
19716    firstSourceWatch(tech);
19717  }
19718
19719  el.resetSourceset_ = function () {
19720    el.resetSourceset_ = null;
19721    el.load = oldLoad;
19722    el.setAttribute = oldSetAttribute;
19723    Object.defineProperty(el, 'src', srcDescriptor);
19724    if (el.resetSourceWatch_) {
19725      el.resetSourceWatch_();
19726    }
19727  };
19728};
19729
19730var _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.']);
19731
19732/**
19733 * @file html5.js
19734 */
19735/**
19736 * HTML5 Media Controller - Wrapper for HTML5 Media API
19737 *
19738 * @mixes Tech~SouceHandlerAdditions
19739 * @extends Tech
19740 */
19741
19742var Html5 = function (_Tech) {
19743  inherits(Html5, _Tech);
19744
19745  /**
19746   * Create an instance of this Tech.
19747   *
19748   * @param {Object} [options]
19749   *        The key/value store of player options.
19750   *
19751   * @param {Component~ReadyCallback} ready
19752   *        Callback function to call when the `HTML5` Tech is ready.
19753   */
19754  function Html5(options, ready) {
19755    classCallCheck(this, Html5);
19756
19757    var _this = possibleConstructorReturn(this, _Tech.call(this, options, ready));
19758
19759    var source = options.source;
19760    var crossoriginTracks = false;
19761
19762    // Set the source if one is provided
19763    // 1) Check if the source is new (if not, we want to keep the original so playback isn't interrupted)
19764    // 2) Check to see if the network state of the tag was failed at init, and if so, reset the source
19765    // anyway so the error gets fired.
19766    if (source && (_this.el_.currentSrc !== source.src || options.tag && options.tag.initNetworkState_ === 3)) {
19767      _this.setSource(source);
19768    } else {
19769      _this.handleLateInit_(_this.el_);
19770    }
19771
19772    // setup sourceset after late sourceset/init
19773    if (options.enableSourceset) {
19774      _this.setupSourcesetHandling_();
19775    }
19776
19777    if (_this.el_.hasChildNodes()) {
19778
19779      var nodes = _this.el_.childNodes;
19780      var nodesLength = nodes.length;
19781      var removeNodes = [];
19782
19783      while (nodesLength--) {
19784        var node = nodes[nodesLength];
19785        var nodeName = node.nodeName.toLowerCase();
19786
19787        if (nodeName === 'track') {
19788          if (!_this.featuresNativeTextTracks) {
19789            // Empty video tag tracks so the built-in player doesn't use them also.
19790            // This may not be fast enough to stop HTML5 browsers from reading the tags
19791            // so we'll need to turn off any default tracks if we're manually doing
19792            // captions and subtitles. videoElement.textTracks
19793            removeNodes.push(node);
19794          } else {
19795            // store HTMLTrackElement and TextTrack to remote list
19796            _this.remoteTextTrackEls().addTrackElement_(node);
19797            _this.remoteTextTracks().addTrack(node.track);
19798            _this.textTracks().addTrack(node.track);
19799            if (!crossoriginTracks && !_this.el_.hasAttribute('crossorigin') && isCrossOrigin(node.src)) {
19800              crossoriginTracks = true;
19801            }
19802          }
19803        }
19804      }
19805
19806      for (var i = 0; i < removeNodes.length; i++) {
19807        _this.el_.removeChild(removeNodes[i]);
19808      }
19809    }
19810
19811    _this.proxyNativeTracks_();
19812    if (_this.featuresNativeTextTracks && crossoriginTracks) {
19813      log$1.warn(tsml(_templateObject$2));
19814    }
19815
19816    // prevent iOS Safari from disabling metadata text tracks during native playback
19817    _this.restoreMetadataTracksInIOSNativePlayer_();
19818
19819    // Determine if native controls should be used
19820    // Our goal should be to get the custom controls on mobile solid everywhere
19821    // so we can remove this all together. Right now this will block custom
19822    // controls on touch enabled laptops like the Chrome Pixel
19823    if ((TOUCH_ENABLED || IS_IPHONE || IS_NATIVE_ANDROID) && options.nativeControlsForTouch === true) {
19824      _this.setControls(true);
19825    }
19826
19827    // on iOS, we want to proxy `webkitbeginfullscreen` and `webkitendfullscreen`
19828    // into a `fullscreenchange` event
19829    _this.proxyWebkitFullscreen_();
19830
19831    _this.triggerReady();
19832    return _this;
19833  }
19834
19835  /**
19836   * Dispose of `HTML5` media element and remove all tracks.
19837   */
19838
19839
19840  Html5.prototype.dispose = function dispose() {
19841    if (this.el_ && this.el_.resetSourceset_) {
19842      this.el_.resetSourceset_();
19843    }
19844    Html5.disposeMediaElement(this.el_);
19845    this.options_ = null;
19846
19847    // tech will handle clearing of the emulated track list
19848    _Tech.prototype.dispose.call(this);
19849  };
19850
19851  /**
19852   * Modify the media element so that we can detect when
19853   * the source is changed. Fires `sourceset` just after the source has changed
19854   */
19855
19856
19857  Html5.prototype.setupSourcesetHandling_ = function setupSourcesetHandling_() {
19858    setupSourceset(this);
19859  };
19860
19861  /**
19862   * When a captions track is enabled in the iOS Safari native player, all other
19863   * tracks are disabled (including metadata tracks), which nulls all of their
19864   * associated cue points. This will restore metadata tracks to their pre-fullscreen
19865   * state in those cases so that cue points are not needlessly lost.
19866   *
19867   * @private
19868   */
19869
19870
19871  Html5.prototype.restoreMetadataTracksInIOSNativePlayer_ = function restoreMetadataTracksInIOSNativePlayer_() {
19872    var textTracks = this.textTracks();
19873    var metadataTracksPreFullscreenState = void 0;
19874
19875    // captures a snapshot of every metadata track's current state
19876    var takeMetadataTrackSnapshot = function takeMetadataTrackSnapshot() {
19877      metadataTracksPreFullscreenState = [];
19878
19879      for (var i = 0; i < textTracks.length; i++) {
19880        var track = textTracks[i];
19881
19882        if (track.kind === 'metadata') {
19883          metadataTracksPreFullscreenState.push({
19884            track: track,
19885            storedMode: track.mode
19886          });
19887        }
19888      }
19889    };
19890
19891    // snapshot each metadata track's initial state, and update the snapshot
19892    // each time there is a track 'change' event
19893    takeMetadataTrackSnapshot();
19894    textTracks.addEventListener('change', takeMetadataTrackSnapshot);
19895
19896    this.on('dispose', function () {
19897      return textTracks.removeEventListener('change', takeMetadataTrackSnapshot);
19898    });
19899
19900    var restoreTrackMode = function restoreTrackMode() {
19901      for (var i = 0; i < metadataTracksPreFullscreenState.length; i++) {
19902        var storedTrack = metadataTracksPreFullscreenState[i];
19903
19904        if (storedTrack.track.mode === 'disabled' && storedTrack.track.mode !== storedTrack.storedMode) {
19905          storedTrack.track.mode = storedTrack.storedMode;
19906        }
19907      }
19908      // we only want this handler to be executed on the first 'change' event
19909      textTracks.removeEventListener('change', restoreTrackMode);
19910    };
19911
19912    // when we enter fullscreen playback, stop updating the snapshot and
19913    // restore all track modes to their pre-fullscreen state
19914    this.on('webkitbeginfullscreen', function () {
19915      textTracks.removeEventListener('change', takeMetadataTrackSnapshot);
19916
19917      // remove the listener before adding it just in case it wasn't previously removed
19918      textTracks.removeEventListener('change', restoreTrackMode);
19919      textTracks.addEventListener('change', restoreTrackMode);
19920    });
19921
19922    // start updating the snapshot again after leaving fullscreen
19923    this.on('webkitendfullscreen', function () {
19924      // remove the listener before adding it just in case it wasn't previously removed
19925      textTracks.removeEventListener('change', takeMetadataTrackSnapshot);
19926      textTracks.addEventListener('change', takeMetadataTrackSnapshot);
19927
19928      // remove the restoreTrackMode handler in case it wasn't triggered during fullscreen playback
19929      textTracks.removeEventListener('change', restoreTrackMode);
19930    });
19931  };
19932
19933  /**
19934   * Proxy all native track list events to our track lists if the browser we are playing
19935   * in supports that type of track list.
19936   *
19937   * @private
19938   */
19939
19940
19941  Html5.prototype.proxyNativeTracks_ = function proxyNativeTracks_() {
19942    var _this2 = this;
19943
19944    NORMAL.names.forEach(function (name) {
19945      var props = NORMAL[name];
19946      var elTracks = _this2.el()[props.getterName];
19947      var techTracks = _this2[props.getterName]();
19948
19949      if (!_this2['featuresNative' + props.capitalName + 'Tracks'] || !elTracks || !elTracks.addEventListener) {
19950        return;
19951      }
19952      var listeners = {
19953        change: function change(e) {
19954          techTracks.trigger({
19955            type: 'change',
19956            target: techTracks,
19957            currentTarget: techTracks,
19958            srcElement: techTracks
19959          });
19960        },
19961        addtrack: function addtrack(e) {
19962          techTracks.addTrack(e.track);
19963        },
19964        removetrack: function removetrack(e) {
19965          techTracks.removeTrack(e.track);
19966        }
19967      };
19968      var removeOldTracks = function removeOldTracks() {
19969        var removeTracks = [];
19970
19971        for (var i = 0; i < techTracks.length; i++) {
19972          var found = false;
19973
19974          for (var j = 0; j < elTracks.length; j++) {
19975            if (elTracks[j] === techTracks[i]) {
19976              found = true;
19977              break;
19978            }
19979          }
19980
19981          if (!found) {
19982            removeTracks.push(techTracks[i]);
19983          }
19984        }
19985
19986        while (removeTracks.length) {
19987          techTracks.removeTrack(removeTracks.shift());
19988        }
19989      };
19990
19991      Object.keys(listeners).forEach(function (eventName) {
19992        var listener = listeners[eventName];
19993
19994        elTracks.addEventListener(eventName, listener);
19995        _this2.on('dispose', function (e) {
19996          return elTracks.removeEventListener(eventName, listener);
19997        });
19998      });
19999
20000      // Remove (native) tracks that are not used anymore
20001      _this2.on('loadstart', removeOldTracks);
20002      _this2.on('dispose', function (e) {
20003        return _this2.off('loadstart', removeOldTracks);
20004      });
20005    });
20006  };
20007
20008  /**
20009   * Create the `Html5` Tech's DOM element.
20010   *
20011   * @return {Element}
20012   *         The element that gets created.
20013   */
20014
20015
20016  Html5.prototype.createEl = function createEl$$1() {
20017    var el = this.options_.tag;
20018
20019    // Check if this browser supports moving the element into the box.
20020    // On the iPhone video will break if you move the element,
20021    // So we have to create a brand new element.
20022    // If we ingested the player div, we do not need to move the media element.
20023    if (!el || !(this.options_.playerElIngest || this.movingMediaElementInDOM)) {
20024
20025      // If the original tag is still there, clone and remove it.
20026      if (el) {
20027        var clone = el.cloneNode(true);
20028
20029        if (el.parentNode) {
20030          el.parentNode.insertBefore(clone, el);
20031        }
20032        Html5.disposeMediaElement(el);
20033        el = clone;
20034      } else {
20035        el = document_1.createElement('video');
20036
20037        // determine if native controls should be used
20038        var tagAttributes = this.options_.tag && getAttributes(this.options_.tag);
20039        var attributes = mergeOptions({}, tagAttributes);
20040
20041        if (!TOUCH_ENABLED || this.options_.nativeControlsForTouch !== true) {
20042          delete attributes.controls;
20043        }
20044
20045        setAttributes(el, assign(attributes, {
20046          id: this.options_.techId,
20047          'class': 'vjs-tech'
20048        }));
20049      }
20050
20051      el.playerId = this.options_.playerId;
20052    }
20053
20054    if (typeof this.options_.preload !== 'undefined') {
20055      setAttribute(el, 'preload', this.options_.preload);
20056    }
20057
20058    // Update specific tag settings, in case they were overridden
20059    // `autoplay` has to be *last* so that `muted` and `playsinline` are present
20060    // when iOS/Safari or other browsers attempt to autoplay.
20061    var settingsAttrs = ['loop', 'muted', 'playsinline', 'autoplay'];
20062
20063    for (var i = 0; i < settingsAttrs.length; i++) {
20064      var attr = settingsAttrs[i];
20065      var value = this.options_[attr];
20066
20067      if (typeof value !== 'undefined') {
20068        if (value) {
20069          setAttribute(el, attr, attr);
20070        } else {
20071          removeAttribute(el, attr);
20072        }
20073        el[attr] = value;
20074      }
20075    }
20076
20077    return el;
20078  };
20079
20080  /**
20081   * This will be triggered if the loadstart event has already fired, before videojs was
20082   * ready. Two known examples of when this can happen are:
20083   * 1. If we're loading the playback object after it has started loading
20084   * 2. The media is already playing the (often with autoplay on) then
20085   *
20086   * This function will fire another loadstart so that videojs can catchup.
20087   *
20088   * @fires Tech#loadstart
20089   *
20090   * @return {undefined}
20091   *         returns nothing.
20092   */
20093
20094
20095  Html5.prototype.handleLateInit_ = function handleLateInit_(el) {
20096    if (el.networkState === 0 || el.networkState === 3) {
20097      // The video element hasn't started loading the source yet
20098      // or didn't find a source
20099      return;
20100    }
20101
20102    if (el.readyState === 0) {
20103      // NetworkState is set synchronously BUT loadstart is fired at the
20104      // end of the current stack, usually before setInterval(fn, 0).
20105      // So at this point we know loadstart may have already fired or is
20106      // about to fire, and either way the player hasn't seen it yet.
20107      // We don't want to fire loadstart prematurely here and cause a
20108      // double loadstart so we'll wait and see if it happens between now
20109      // and the next loop, and fire it if not.
20110      // HOWEVER, we also want to make sure it fires before loadedmetadata
20111      // which could also happen between now and the next loop, so we'll
20112      // watch for that also.
20113      var loadstartFired = false;
20114      var setLoadstartFired = function setLoadstartFired() {
20115        loadstartFired = true;
20116      };
20117
20118      this.on('loadstart', setLoadstartFired);
20119
20120      var triggerLoadstart = function triggerLoadstart() {
20121        // We did miss the original loadstart. Make sure the player
20122        // sees loadstart before loadedmetadata
20123        if (!loadstartFired) {
20124          this.trigger('loadstart');
20125        }
20126      };
20127
20128      this.on('loadedmetadata', triggerLoadstart);
20129
20130      this.ready(function () {
20131        this.off('loadstart', setLoadstartFired);
20132        this.off('loadedmetadata', triggerLoadstart);
20133
20134        if (!loadstartFired) {
20135          // We did miss the original native loadstart. Fire it now.
20136          this.trigger('loadstart');
20137        }
20138      });
20139
20140      return;
20141    }
20142
20143    // From here on we know that loadstart already fired and we missed it.
20144    // The other readyState events aren't as much of a problem if we double
20145    // them, so not going to go to as much trouble as loadstart to prevent
20146    // that unless we find reason to.
20147    var eventsToTrigger = ['loadstart'];
20148
20149    // loadedmetadata: newly equal to HAVE_METADATA (1) or greater
20150    eventsToTrigger.push('loadedmetadata');
20151
20152    // loadeddata: newly increased to HAVE_CURRENT_DATA (2) or greater
20153    if (el.readyState >= 2) {
20154      eventsToTrigger.push('loadeddata');
20155    }
20156
20157    // canplay: newly increased to HAVE_FUTURE_DATA (3) or greater
20158    if (el.readyState >= 3) {
20159      eventsToTrigger.push('canplay');
20160    }
20161
20162    // canplaythrough: newly equal to HAVE_ENOUGH_DATA (4)
20163    if (el.readyState >= 4) {
20164      eventsToTrigger.push('canplaythrough');
20165    }
20166
20167    // We still need to give the player time to add event listeners
20168    this.ready(function () {
20169      eventsToTrigger.forEach(function (type) {
20170        this.trigger(type);
20171      }, this);
20172    });
20173  };
20174
20175  /**
20176   * Set current time for the `HTML5` tech.
20177   *
20178   * @param {number} seconds
20179   *        Set the current time of the media to this.
20180   */
20181
20182
20183  Html5.prototype.setCurrentTime = function setCurrentTime(seconds) {
20184    try {
20185      this.el_.currentTime = seconds;
20186    } catch (e) {
20187      log$1(e, 'Video is not ready. (Video.js)');
20188      // this.warning(VideoJS.warnings.videoNotReady);
20189    }
20190  };
20191
20192  /**
20193   * Get the current duration of the HTML5 media element.
20194   *
20195   * @return {number}
20196   *         The duration of the media or 0 if there is no duration.
20197   */
20198
20199
20200  Html5.prototype.duration = function duration() {
20201    var _this3 = this;
20202
20203    // Android Chrome will report duration as Infinity for VOD HLS until after
20204    // playback has started, which triggers the live display erroneously.
20205    // Return NaN if playback has not started and trigger a durationupdate once
20206    // the duration can be reliably known.
20207    if (this.el_.duration === Infinity && IS_ANDROID && IS_CHROME && this.el_.currentTime === 0) {
20208      // Wait for the first `timeupdate` with currentTime > 0 - there may be
20209      // several with 0
20210      var checkProgress = function checkProgress() {
20211        if (_this3.el_.currentTime > 0) {
20212          // Trigger durationchange for genuinely live video
20213          if (_this3.el_.duration === Infinity) {
20214            _this3.trigger('durationchange');
20215          }
20216          _this3.off('timeupdate', checkProgress);
20217        }
20218      };
20219
20220      this.on('timeupdate', checkProgress);
20221      return NaN;
20222    }
20223    return this.el_.duration || NaN;
20224  };
20225
20226  /**
20227   * Get the current width of the HTML5 media element.
20228   *
20229   * @return {number}
20230   *         The width of the HTML5 media element.
20231   */
20232
20233
20234  Html5.prototype.width = function width() {
20235    return this.el_.offsetWidth;
20236  };
20237
20238  /**
20239   * Get the current height of the HTML5 media element.
20240   *
20241   * @return {number}
20242   *         The heigth of the HTML5 media element.
20243   */
20244
20245
20246  Html5.prototype.height = function height() {
20247    return this.el_.offsetHeight;
20248  };
20249
20250  /**
20251   * Proxy iOS `webkitbeginfullscreen` and `webkitendfullscreen` into
20252   * `fullscreenchange` event.
20253   *
20254   * @private
20255   * @fires fullscreenchange
20256   * @listens webkitendfullscreen
20257   * @listens webkitbeginfullscreen
20258   * @listens webkitbeginfullscreen
20259   */
20260
20261
20262  Html5.prototype.proxyWebkitFullscreen_ = function proxyWebkitFullscreen_() {
20263    var _this4 = this;
20264
20265    if (!('webkitDisplayingFullscreen' in this.el_)) {
20266      return;
20267    }
20268
20269    var endFn = function endFn() {
20270      this.trigger('fullscreenchange', { isFullscreen: false });
20271    };
20272
20273    var beginFn = function beginFn() {
20274      if ('webkitPresentationMode' in this.el_ && this.el_.webkitPresentationMode !== 'picture-in-picture') {
20275        this.one('webkitendfullscreen', endFn);
20276
20277        this.trigger('fullscreenchange', { isFullscreen: true });
20278      }
20279    };
20280
20281    this.on('webkitbeginfullscreen', beginFn);
20282    this.on('dispose', function () {
20283      _this4.off('webkitbeginfullscreen', beginFn);
20284      _this4.off('webkitendfullscreen', endFn);
20285    });
20286  };
20287
20288  /**
20289   * Check if fullscreen is supported on the current playback device.
20290   *
20291   * @return {boolean}
20292   *         - True if fullscreen is supported.
20293   *         - False if fullscreen is not supported.
20294   */
20295
20296
20297  Html5.prototype.supportsFullScreen = function supportsFullScreen() {
20298    if (typeof this.el_.webkitEnterFullScreen === 'function') {
20299      var userAgent = window_1.navigator && window_1.navigator.userAgent || '';
20300
20301      // Seems to be broken in Chromium/Chrome && Safari in Leopard
20302      if (/Android/.test(userAgent) || !/Chrome|Mac OS X 10.5/.test(userAgent)) {
20303        return true;
20304      }
20305    }
20306    return false;
20307  };
20308
20309  /**
20310   * Request that the `HTML5` Tech enter fullscreen.
20311   */
20312
20313
20314  Html5.prototype.enterFullScreen = function enterFullScreen() {
20315    var video = this.el_;
20316
20317    if (video.paused && video.networkState <= video.HAVE_METADATA) {
20318      // attempt to prime the video element for programmatic access
20319      // this isn't necessary on the desktop but shouldn't hurt
20320      this.el_.play();
20321
20322      // playing and pausing synchronously during the transition to fullscreen
20323      // can get iOS ~6.1 devices into a play/pause loop
20324      this.setTimeout(function () {
20325        video.pause();
20326        video.webkitEnterFullScreen();
20327      }, 0);
20328    } else {
20329      video.webkitEnterFullScreen();
20330    }
20331  };
20332
20333  /**
20334   * Request that the `HTML5` Tech exit fullscreen.
20335   */
20336
20337
20338  Html5.prototype.exitFullScreen = function exitFullScreen() {
20339    this.el_.webkitExitFullScreen();
20340  };
20341
20342  /**
20343   * A getter/setter for the `Html5` Tech's source object.
20344   * > Note: Please use {@link Html5#setSource}
20345   *
20346   * @param {Tech~SourceObject} [src]
20347   *        The source object you want to set on the `HTML5` techs element.
20348   *
20349   * @return {Tech~SourceObject|undefined}
20350   *         - The current source object when a source is not passed in.
20351   *         - undefined when setting
20352   *
20353   * @deprecated Since version 5.
20354   */
20355
20356
20357  Html5.prototype.src = function src(_src) {
20358    if (_src === undefined) {
20359      return this.el_.src;
20360    }
20361
20362    // Setting src through `src` instead of `setSrc` will be deprecated
20363    this.setSrc(_src);
20364  };
20365
20366  /**
20367   * Reset the tech by removing all sources and then calling
20368   * {@link Html5.resetMediaElement}.
20369   */
20370
20371
20372  Html5.prototype.reset = function reset() {
20373    Html5.resetMediaElement(this.el_);
20374  };
20375
20376  /**
20377   * Get the current source on the `HTML5` Tech. Falls back to returning the source from
20378   * the HTML5 media element.
20379   *
20380   * @return {Tech~SourceObject}
20381   *         The current source object from the HTML5 tech. With a fallback to the
20382   *         elements source.
20383   */
20384
20385
20386  Html5.prototype.currentSrc = function currentSrc() {
20387    if (this.currentSource_) {
20388      return this.currentSource_.src;
20389    }
20390    return this.el_.currentSrc;
20391  };
20392
20393  /**
20394   * Set controls attribute for the HTML5 media Element.
20395   *
20396   * @param {string} val
20397   *        Value to set the controls attribute to
20398   */
20399
20400
20401  Html5.prototype.setControls = function setControls(val) {
20402    this.el_.controls = !!val;
20403  };
20404
20405  /**
20406   * Create and returns a remote {@link TextTrack} object.
20407   *
20408   * @param {string} kind
20409   *        `TextTrack` kind (subtitles, captions, descriptions, chapters, or metadata)
20410   *
20411   * @param {string} [label]
20412   *        Label to identify the text track
20413   *
20414   * @param {string} [language]
20415   *        Two letter language abbreviation
20416   *
20417   * @return {TextTrack}
20418   *         The TextTrack that gets created.
20419   */
20420
20421
20422  Html5.prototype.addTextTrack = function addTextTrack(kind, label, language) {
20423    if (!this.featuresNativeTextTracks) {
20424      return _Tech.prototype.addTextTrack.call(this, kind, label, language);
20425    }
20426
20427    return this.el_.addTextTrack(kind, label, language);
20428  };
20429
20430  /**
20431   * Creates either native TextTrack or an emulated TextTrack depending
20432   * on the value of `featuresNativeTextTracks`
20433   *
20434   * @param {Object} options
20435   *        The object should contain the options to intialize the TextTrack with.
20436   *
20437   * @param {string} [options.kind]
20438   *        `TextTrack` kind (subtitles, captions, descriptions, chapters, or metadata).
20439   *
20440   * @param {string} [options.label].
20441   *        Label to identify the text track
20442   *
20443   * @param {string} [options.language]
20444   *        Two letter language abbreviation.
20445   *
20446   * @param {boolean} [options.default]
20447   *        Default this track to on.
20448   *
20449   * @param {string} [options.id]
20450   *        The internal id to assign this track.
20451   *
20452   * @param {string} [options.src]
20453   *        A source url for the track.
20454   *
20455   * @return {HTMLTrackElement}
20456   *         The track element that gets created.
20457   */
20458
20459
20460  Html5.prototype.createRemoteTextTrack = function createRemoteTextTrack(options) {
20461    if (!this.featuresNativeTextTracks) {
20462      return _Tech.prototype.createRemoteTextTrack.call(this, options);
20463    }
20464    var htmlTrackElement = document_1.createElement('track');
20465
20466    if (options.kind) {
20467      htmlTrackElement.kind = options.kind;
20468    }
20469    if (options.label) {
20470      htmlTrackElement.label = options.label;
20471    }
20472    if (options.language || options.srclang) {
20473      htmlTrackElement.srclang = options.language || options.srclang;
20474    }
20475    if (options['default']) {
20476      htmlTrackElement['default'] = options['default'];
20477    }
20478    if (options.id) {
20479      htmlTrackElement.id = options.id;
20480    }
20481    if (options.src) {
20482      htmlTrackElement.src = options.src;
20483    }
20484
20485    return htmlTrackElement;
20486  };
20487
20488  /**
20489   * Creates a remote text track object and returns an html track element.
20490   *
20491   * @param {Object} options The object should contain values for
20492   * kind, language, label, and src (location of the WebVTT file)
20493   * @param {Boolean} [manualCleanup=true] if set to false, the TextTrack will be
20494   * automatically removed from the video element whenever the source changes
20495   * @return {HTMLTrackElement} An Html Track Element.
20496   * This can be an emulated {@link HTMLTrackElement} or a native one.
20497   * @deprecated The default value of the "manualCleanup" parameter will default
20498   * to "false" in upcoming versions of Video.js
20499   */
20500
20501
20502  Html5.prototype.addRemoteTextTrack = function addRemoteTextTrack(options, manualCleanup) {
20503    var htmlTrackElement = _Tech.prototype.addRemoteTextTrack.call(this, options, manualCleanup);
20504
20505    if (this.featuresNativeTextTracks) {
20506      this.el().appendChild(htmlTrackElement);
20507    }
20508
20509    return htmlTrackElement;
20510  };
20511
20512  /**
20513   * Remove remote `TextTrack` from `TextTrackList` object
20514   *
20515   * @param {TextTrack} track
20516   *        `TextTrack` object to remove
20517   */
20518
20519
20520  Html5.prototype.removeRemoteTextTrack = function removeRemoteTextTrack(track) {
20521    _Tech.prototype.removeRemoteTextTrack.call(this, track);
20522
20523    if (this.featuresNativeTextTracks) {
20524      var tracks = this.$$('track');
20525
20526      var i = tracks.length;
20527
20528      while (i--) {
20529        if (track === tracks[i] || track === tracks[i].track) {
20530          this.el().removeChild(tracks[i]);
20531        }
20532      }
20533    }
20534  };
20535
20536  /**
20537   * Gets available media playback quality metrics as specified by the W3C's Media
20538   * Playback Quality API.
20539   *
20540   * @see [Spec]{@link https://wicg.github.io/media-playback-quality}
20541   *
20542   * @return {Object}
20543   *         An object with supported media playback quality metrics
20544   */
20545
20546
20547  Html5.prototype.getVideoPlaybackQuality = function getVideoPlaybackQuality() {
20548    if (typeof this.el().getVideoPlaybackQuality === 'function') {
20549      return this.el().getVideoPlaybackQuality();
20550    }
20551
20552    var videoPlaybackQuality = {};
20553
20554    if (typeof this.el().webkitDroppedFrameCount !== 'undefined' && typeof this.el().webkitDecodedFrameCount !== 'undefined') {
20555      videoPlaybackQuality.droppedVideoFrames = this.el().webkitDroppedFrameCount;
20556      videoPlaybackQuality.totalVideoFrames = this.el().webkitDecodedFrameCount;
20557    }
20558
20559    if (window_1.performance && typeof window_1.performance.now === 'function') {
20560      videoPlaybackQuality.creationTime = window_1.performance.now();
20561    } else if (window_1.performance && window_1.performance.timing && typeof window_1.performance.timing.navigationStart === 'number') {
20562      videoPlaybackQuality.creationTime = window_1.Date.now() - window_1.performance.timing.navigationStart;
20563    }
20564
20565    return videoPlaybackQuality;
20566  };
20567
20568  return Html5;
20569}(Tech);
20570
20571/* HTML5 Support Testing ---------------------------------------------------- */
20572
20573if (isReal()) {
20574
20575  /**
20576   * Element for testing browser HTML5 media capabilities
20577   *
20578   * @type {Element}
20579   * @constant
20580   * @private
20581   */
20582  Html5.TEST_VID = document_1.createElement('video');
20583  var track = document_1.createElement('track');
20584
20585  track.kind = 'captions';
20586  track.srclang = 'en';
20587  track.label = 'English';
20588  Html5.TEST_VID.appendChild(track);
20589}
20590
20591/**
20592 * Check if HTML5 media is supported by this browser/device.
20593 *
20594 * @return {boolean}
20595 *         - True if HTML5 media is supported.
20596 *         - False if HTML5 media is not supported.
20597 */
20598Html5.isSupported = function () {
20599  // IE9 with no Media Player is a LIAR! (#984)
20600  try {
20601    Html5.TEST_VID.volume = 0.5;
20602  } catch (e) {
20603    return false;
20604  }
20605
20606  return !!(Html5.TEST_VID && Html5.TEST_VID.canPlayType);
20607};
20608
20609/**
20610 * Check if the tech can support the given type
20611 *
20612 * @param {string} type
20613 *        The mimetype to check
20614 * @return {string} 'probably', 'maybe', or '' (empty string)
20615 */
20616Html5.canPlayType = function (type) {
20617  return Html5.TEST_VID.canPlayType(type);
20618};
20619
20620/**
20621 * Check if the tech can support the given source
20622 * @param {Object} srcObj
20623 *        The source object
20624 * @param {Object} options
20625 *        The options passed to the tech
20626 * @return {string} 'probably', 'maybe', or '' (empty string)
20627 */
20628Html5.canPlaySource = function (srcObj, options) {
20629  return Html5.canPlayType(srcObj.type);
20630};
20631
20632/**
20633 * Check if the volume can be changed in this browser/device.
20634 * Volume cannot be changed in a lot of mobile devices.
20635 * Specifically, it can't be changed from 1 on iOS.
20636 *
20637 * @return {boolean}
20638 *         - True if volume can be controlled
20639 *         - False otherwise
20640 */
20641Html5.canControlVolume = function () {
20642  // IE will error if Windows Media Player not installed #3315
20643  try {
20644    var volume = Html5.TEST_VID.volume;
20645
20646    Html5.TEST_VID.volume = volume / 2 + 0.1;
20647    return volume !== Html5.TEST_VID.volume;
20648  } catch (e) {
20649    return false;
20650  }
20651};
20652
20653/**
20654 * Check if the volume can be muted in this browser/device.
20655 * Some devices, e.g. iOS, don't allow changing volume
20656 * but permits muting/unmuting.
20657 *
20658 * @return {bolean}
20659 *      - True if volume can be muted
20660 *      - False otherwise
20661 */
20662Html5.canMuteVolume = function () {
20663  try {
20664    var muted = Html5.TEST_VID.muted;
20665
20666    // in some versions of iOS muted property doesn't always
20667    // work, so we want to set both property and attribute
20668    Html5.TEST_VID.muted = !muted;
20669    if (Html5.TEST_VID.muted) {
20670      setAttribute(Html5.TEST_VID, 'muted', 'muted');
20671    } else {
20672      removeAttribute(Html5.TEST_VID, 'muted', 'muted');
20673    }
20674    return muted !== Html5.TEST_VID.muted;
20675  } catch (e) {
20676    return false;
20677  }
20678};
20679
20680/**
20681 * Check if the playback rate can be changed in this browser/device.
20682 *
20683 * @return {boolean}
20684 *         - True if playback rate can be controlled
20685 *         - False otherwise
20686 */
20687Html5.canControlPlaybackRate = function () {
20688  // Playback rate API is implemented in Android Chrome, but doesn't do anything
20689  // https://github.com/videojs/video.js/issues/3180
20690  if (IS_ANDROID && IS_CHROME && CHROME_VERSION < 58) {
20691    return false;
20692  }
20693  // IE will error if Windows Media Player not installed #3315
20694  try {
20695    var playbackRate = Html5.TEST_VID.playbackRate;
20696
20697    Html5.TEST_VID.playbackRate = playbackRate / 2 + 0.1;
20698    return playbackRate !== Html5.TEST_VID.playbackRate;
20699  } catch (e) {
20700    return false;
20701  }
20702};
20703
20704/**
20705 * Check if we can override a video/audio elements attributes, with
20706 * Object.defineProperty.
20707 *
20708 * @return {boolean}
20709 *         - True if builtin attributes can be overriden
20710 *         - False otherwise
20711 */
20712Html5.canOverrideAttributes = function () {
20713  if (IS_IE8) {
20714    return false;
20715  }
20716  // if we cannot overwrite the src/innerHTML property, there is no support
20717  // iOS 7 safari for instance cannot do this.
20718  try {
20719    var noop = function noop() {};
20720
20721    Object.defineProperty(document_1.createElement('video'), 'src', { get: noop, set: noop });
20722    Object.defineProperty(document_1.createElement('audio'), 'src', { get: noop, set: noop });
20723    Object.defineProperty(document_1.createElement('video'), 'innerHTML', { get: noop, set: noop });
20724    Object.defineProperty(document_1.createElement('audio'), 'innerHTML', { get: noop, set: noop });
20725  } catch (e) {
20726    return false;
20727  }
20728
20729  return true;
20730};
20731
20732/**
20733 * Check to see if native `TextTrack`s are supported by this browser/device.
20734 *
20735 * @return {boolean}
20736 *         - True if native `TextTrack`s are supported.
20737 *         - False otherwise
20738 */
20739Html5.supportsNativeTextTracks = function () {
20740  return IS_ANY_SAFARI || IS_IOS && IS_CHROME;
20741};
20742
20743/**
20744 * Check to see if native `VideoTrack`s are supported by this browser/device
20745 *
20746 * @return {boolean}
20747 *        - True if native `VideoTrack`s are supported.
20748 *        - False otherwise
20749 */
20750Html5.supportsNativeVideoTracks = function () {
20751  return !!(Html5.TEST_VID && Html5.TEST_VID.videoTracks);
20752};
20753
20754/**
20755 * Check to see if native `AudioTrack`s are supported by this browser/device
20756 *
20757 * @return {boolean}
20758 *        - True if native `AudioTrack`s are supported.
20759 *        - False otherwise
20760 */
20761Html5.supportsNativeAudioTracks = function () {
20762  return !!(Html5.TEST_VID && Html5.TEST_VID.audioTracks);
20763};
20764
20765/**
20766 * An array of events available on the Html5 tech.
20767 *
20768 * @private
20769 * @type {Array}
20770 */
20771Html5.Events = ['loadstart', 'suspend', 'abort', 'error', 'emptied', 'stalled', 'loadedmetadata', 'loadeddata', 'canplay', 'canplaythrough', 'playing', 'waiting', 'seeking', 'seeked', 'ended', 'durationchange', 'timeupdate', 'progress', 'play', 'pause', 'ratechange', 'resize', 'volumechange'];
20772
20773/**
20774 * Boolean indicating whether the `Tech` supports volume control.
20775 *
20776 * @type {boolean}
20777 * @default {@link Html5.canControlVolume}
20778 */
20779Html5.prototype.featuresVolumeControl = Html5.canControlVolume();
20780
20781/**
20782 * Boolean indicating whether the `Tech` supports muting volume.
20783 *
20784 * @type {bolean}
20785 * @default {@link Html5.canMuteVolume}
20786 */
20787Html5.prototype.featuresMuteControl = Html5.canMuteVolume();
20788
20789/**
20790 * Boolean indicating whether the `Tech` supports changing the speed at which the media
20791 * plays. Examples:
20792 *   - Set player to play 2x (twice) as fast
20793 *   - Set player to play 0.5x (half) as fast
20794 *
20795 * @type {boolean}
20796 * @default {@link Html5.canControlPlaybackRate}
20797 */
20798Html5.prototype.featuresPlaybackRate = Html5.canControlPlaybackRate();
20799
20800/**
20801 * Boolean indicating wether the `Tech` supports the `sourceset` event.
20802 *
20803 * @type {boolean}
20804 * @default
20805 */
20806Html5.prototype.featuresSourceset = Html5.canOverrideAttributes();
20807
20808/**
20809 * Boolean indicating whether the `HTML5` tech currently supports the media element
20810 * moving in the DOM. iOS breaks if you move the media element, so this is set this to
20811 * false there. Everywhere else this should be true.
20812 *
20813 * @type {boolean}
20814 * @default
20815 */
20816Html5.prototype.movingMediaElementInDOM = !IS_IOS;
20817
20818// TODO: Previous comment: No longer appears to be used. Can probably be removed.
20819//       Is this true?
20820/**
20821 * Boolean indicating whether the `HTML5` tech currently supports automatic media resize
20822 * when going into fullscreen.
20823 *
20824 * @type {boolean}
20825 * @default
20826 */
20827Html5.prototype.featuresFullscreenResize = true;
20828
20829/**
20830 * Boolean indicating whether the `HTML5` tech currently supports the progress event.
20831 * If this is false, manual `progress` events will be triggred instead.
20832 *
20833 * @type {boolean}
20834 * @default
20835 */
20836Html5.prototype.featuresProgressEvents = true;
20837
20838/**
20839 * Boolean indicating whether the `HTML5` tech currently supports the timeupdate event.
20840 * If this is false, manual `timeupdate` events will be triggred instead.
20841 *
20842 * @default
20843 */
20844Html5.prototype.featuresTimeupdateEvents = true;
20845
20846/**
20847 * Boolean indicating whether the `HTML5` tech currently supports native `TextTrack`s.
20848 *
20849 * @type {boolean}
20850 * @default {@link Html5.supportsNativeTextTracks}
20851 */
20852Html5.prototype.featuresNativeTextTracks = Html5.supportsNativeTextTracks();
20853
20854/**
20855 * Boolean indicating whether the `HTML5` tech currently supports native `VideoTrack`s.
20856 *
20857 * @type {boolean}
20858 * @default {@link Html5.supportsNativeVideoTracks}
20859 */
20860Html5.prototype.featuresNativeVideoTracks = Html5.supportsNativeVideoTracks();
20861
20862/**
20863 * Boolean indicating whether the `HTML5` tech currently supports native `AudioTrack`s.
20864 *
20865 * @type {boolean}
20866 * @default {@link Html5.supportsNativeAudioTracks}
20867 */
20868Html5.prototype.featuresNativeAudioTracks = Html5.supportsNativeAudioTracks();
20869
20870// HTML5 Feature detection and Device Fixes --------------------------------- //
vendor: 14,210 bytes, lines 20871-21295
20871var canPlayType = Html5.TEST_VID && Html5.TEST_VID.constructor.prototype.canPlayType;
20872var mpegurlRE = /^application\/(?:x-|vnd\.apple\.)mpegurl/i;
20873var mp4RE = /^video\/mp4/i;
20874
20875Html5.patchCanPlayType = function () {
20876
20877  // Android 4.0 and above can play HLS to some extent but it reports being unable to do so
20878  // Firefox and Chrome report correctly
20879  if (ANDROID_VERSION >= 4.0 && !IS_FIREFOX && !IS_CHROME) {
20880    Html5.TEST_VID.constructor.prototype.canPlayType = function (type) {
20881      if (type && mpegurlRE.test(type)) {
20882        return 'maybe';
20883      }
20884      return canPlayType.call(this, type);
20885    };
20886
20887    // Override Android 2.2 and less canPlayType method which is broken
20888  } else if (IS_OLD_ANDROID) {
20889    Html5.TEST_VID.constructor.prototype.canPlayType = function (type) {
20890      if (type && mp4RE.test(type)) {
20891        return 'maybe';
20892      }
20893      return canPlayType.call(this, type);
20894    };
20895  }
20896};
20897
20898Html5.unpatchCanPlayType = function () {
20899  var r = Html5.TEST_VID.constructor.prototype.canPlayType;
20900
20901  Html5.TEST_VID.constructor.prototype.canPlayType = canPlayType;
20902  return r;
20903};
20904
20905// by default, patch the media element
20906Html5.patchCanPlayType();
20907
20908Html5.disposeMediaElement = function (el) {
20909  if (!el) {
20910    return;
20911  }
20912
20913  if (el.parentNode) {
20914    el.parentNode.removeChild(el);
20915  }
20916
20917  // remove any child track or source nodes to prevent their loading
20918  while (el.hasChildNodes()) {
20919    el.removeChild(el.firstChild);
20920  }
20921
20922  // remove any src reference. not setting `src=''` because that causes a warning
20923  // in firefox
20924  el.removeAttribute('src');
20925
20926  // force the media element to update its loading state by calling load()
20927  // however IE on Windows 7N has a bug that throws an error so need a try/catch (#793)
20928  if (typeof el.load === 'function') {
20929    // wrapping in an iife so it's not deoptimized (#1060#discussion_r10324473)
20930    (function () {
20931      try {
20932        el.load();
20933      } catch (e) {
20934        // not supported
20935      }
20936    })();
20937  }
20938};
20939
20940Html5.resetMediaElement = function (el) {
20941  if (!el) {
20942    return;
20943  }
20944
20945  var sources = el.querySelectorAll('source');
20946  var i = sources.length;
20947
20948  while (i--) {
20949    el.removeChild(sources[i]);
20950  }
20951
20952  // remove any src reference.
20953  // not setting `src=''` because that throws an error
20954  el.removeAttribute('src');
20955
20956  if (typeof el.load === 'function') {
20957    // wrapping in an iife so it's not deoptimized (#1060#discussion_r10324473)
20958    (function () {
20959      try {
20960        el.load();
20961      } catch (e) {
20962        // satisfy linter
20963      }
20964    })();
20965  }
20966};
20967
20968/* Native HTML5 element property wrapping ----------------------------------- */
20969// Wrap native boolean attributes with getters that check both property and attribute
20970// The list is as followed:
20971// muted, defaultMuted, autoplay, controls, loop, playsinline
20972[
20973/**
20974 * Get the value of `muted` from the media element. `muted` indicates
20975 * that the volume for the media should be set to silent. This does not actually change
20976 * the `volume` attribute.
20977 *
20978 * @method Html5#muted
20979 * @return {boolean}
20980 *         - True if the value of `volume` should be ignored and the audio set to silent.
20981 *         - False if the value of `volume` should be used.
20982 *
20983 * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-media-muted}
20984 */
20985'muted',
20986
20987/**
20988 * Get the value of `defaultMuted` from the media element. `defaultMuted` indicates
20989 * whether the media should start muted or not. Only changes the default state of the
20990 * media. `muted` and `defaultMuted` can have different values. {@link Html5#muted} indicates the
20991 * current state.
20992 *
20993 * @method Html5#defaultMuted
20994 * @return {boolean}
20995 *         - The value of `defaultMuted` from the media element.
20996 *         - True indicates that the media should start muted.
20997 *         - False indicates that the media should not start muted
20998 *
20999 * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-media-defaultmuted}
21000 */
21001'defaultMuted',
21002
21003/**
21004 * Get the value of `autoplay` from the media element. `autoplay` indicates
21005 * that the media should start to play as soon as the page is ready.
21006 *
21007 * @method Html5#autoplay
21008 * @return {boolean}
21009 *         - The value of `autoplay` from the media element.
21010 *         - True indicates that the media should start as soon as the page loads.
21011 *         - False indicates that the media should not start as soon as the page loads.
21012 *
21013 * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#attr-media-autoplay}
21014 */
21015'autoplay',
21016
21017/**
21018 * Get the value of `controls` from the media element. `controls` indicates
21019 * whether the native media controls should be shown or hidden.
21020 *
21021 * @method Html5#controls
21022 * @return {boolean}
21023 *         - The value of `controls` from the media element.
21024 *         - True indicates that native controls should be showing.
21025 *         - False indicates that native controls should be hidden.
21026 *
21027 * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#attr-media-controls}
21028 */
21029'controls',
21030
21031/**
21032 * Get the value of `loop` from the media element. `loop` indicates
21033 * that the media should return to the start of the media and continue playing once
21034 * it reaches the end.
21035 *
21036 * @method Html5#loop
21037 * @return {boolean}
21038 *         - The value of `loop` from the media element.
21039 *         - True indicates that playback should seek back to start once
21040 *           the end of a media is reached.
21041 *         - False indicates that playback should not loop back to the start when the
21042 *           end of the media is reached.
21043 *
21044 * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#attr-media-loop}
21045 */
21046'loop',
21047
21048/**
21049 * Get the value of `playsinline` from the media element. `playsinline` indicates
21050 * to the browser that non-fullscreen playback is preferred when fullscreen
21051 * playback is the native default, such as in iOS Safari.
21052 *
21053 * @method Html5#playsinline
21054 * @return {boolean}
21055 *         - The value of `playsinline` from the media element.
21056 *         - True indicates that the media should play inline.
21057 *         - False indicates that the media should not play inline.
21058 *
21059 * @see [Spec]{@link https://html.spec.whatwg.org/#attr-video-playsinline}
21060 */
21061'playsinline'].forEach(function (prop) {
21062  Html5.prototype[prop] = function () {
21063    return this.el_[prop] || this.el_.hasAttribute(prop);
21064  };
21065});
21066
21067// Wrap native boolean attributes with setters that set both property and attribute
21068// The list is as followed:
21069// setMuted, setDefaultMuted, setAutoplay, setLoop, setPlaysinline
21070// setControls is special-cased above
21071[
21072/**
21073 * Set the value of `muted` on the media element. `muted` indicates that the current
21074 * audio level should be silent.
21075 *
21076 * @method Html5#setMuted
21077 * @param {boolean} muted
21078 *        - True if the audio should be set to silent
21079 *        - False otherwise
21080 *
21081 * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-media-muted}
21082 */
21083'muted',
21084
21085/**
21086 * Set the value of `defaultMuted` on the media element. `defaultMuted` indicates that the current
21087 * audio level should be silent, but will only effect the muted level on intial playback..
21088 *
21089 * @method Html5.prototype.setDefaultMuted
21090 * @param {boolean} defaultMuted
21091 *        - True if the audio should be set to silent
21092 *        - False otherwise
21093 *
21094 * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-media-defaultmuted}
21095 */
21096'defaultMuted',
21097
21098/**
21099 * Set the value of `autoplay` on the media element. `autoplay` indicates
21100 * that the media should start to play as soon as the page is ready.
21101 *
21102 * @method Html5#setAutoplay
21103 * @param {boolean} autoplay
21104 *         - True indicates that the media should start as soon as the page loads.
21105 *         - False indicates that the media should not start as soon as the page loads.
21106 *
21107 * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#attr-media-autoplay}
21108 */
21109'autoplay',
21110
21111/**
21112 * Set the value of `loop` on the media element. `loop` indicates
21113 * that the media should return to the start of the media and continue playing once
21114 * it reaches the end.
21115 *
21116 * @method Html5#setLoop
21117 * @param {boolean} loop
21118 *         - True indicates that playback should seek back to start once
21119 *           the end of a media is reached.
21120 *         - False indicates that playback should not loop back to the start when the
21121 *           end of the media is reached.
21122 *
21123 * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#attr-media-loop}
21124 */
21125'loop',
21126
21127/**
21128 * Set the value of `playsinline` from the media element. `playsinline` indicates
21129 * to the browser that non-fullscreen playback is preferred when fullscreen
21130 * playback is the native default, such as in iOS Safari.
21131 *
21132 * @method Html5#setPlaysinline
21133 * @param {boolean} playsinline
21134 *         - True indicates that the media should play inline.
21135 *         - False indicates that the media should not play inline.
21136 *
21137 * @see [Spec]{@link https://html.spec.whatwg.org/#attr-video-playsinline}
21138 */
21139'playsinline'].forEach(function (prop) {
21140  Html5.prototype['set' + toTitleCase(prop)] = function (v) {
21141    this.el_[prop] = v;
21142
21143    if (v) {
21144      this.el_.setAttribute(prop, prop);
21145    } else {
21146      this.el_.removeAttribute(prop);
21147    }
21148  };
21149});
21150
21151// Wrap native properties with a getter
21152// The list is as followed
21153// paused, currentTime, buffered, volume, poster, preload, error, seeking
21154// seekable, ended, playbackRate, defaultPlaybackRate, played, networkState
21155// readyState, videoWidth, videoHeight
21156[
21157/**
21158 * Get the value of `paused` from the media element. `paused` indicates whether the media element
21159 * is currently paused or not.
21160 *
21161 * @method Html5#paused
21162 * @return {boolean}
21163 *         The value of `paused` from the media element.
21164 *
21165 * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-media-paused}
21166 */
21167'paused',
21168
21169/**
21170 * Get the value of `currentTime` from the media element. `currentTime` indicates
21171 * the current second that the media is at in playback.
21172 *
21173 * @method Html5#currentTime
21174 * @return {number}
21175 *         The value of `currentTime` from the media element.
21176 *
21177 * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-media-currenttime}
21178 */
21179'currentTime',
21180
21181/**
21182 * Get the value of `buffered` from the media element. `buffered` is a `TimeRange`
21183 * object that represents the parts of the media that are already downloaded and
21184 * available for playback.
21185 *
21186 * @method Html5#buffered
21187 * @return {TimeRange}
21188 *         The value of `buffered` from the media element.
21189 *
21190 * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-media-buffered}
21191 */
21192'buffered',
21193
21194/**
21195 * Get the value of `volume` from the media element. `volume` indicates
21196 * the current playback volume of audio for a media. `volume` will be a value from 0
21197 * (silent) to 1 (loudest and default).
21198 *
21199 * @method Html5#volume
21200 * @return {number}
21201 *         The value of `volume` from the media element. Value will be between 0-1.
21202 *
21203 * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-a-volume}
21204 */
21205'volume',
21206
21207/**
21208 * Get the value of `poster` from the media element. `poster` indicates
21209 * that the url of an image file that can/will be shown when no media data is available.
21210 *
21211 * @method Html5#poster
21212 * @return {string}
21213 *         The value of `poster` from the media element. Value will be a url to an
21214 *         image.
21215 *
21216 * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#attr-video-poster}
21217 */
21218'poster',
21219
21220/**
21221 * Get the value of `preload` from the media element. `preload` indicates
21222 * what should download before the media is interacted with. It can have the following
21223 * values:
21224 * - none: nothing should be downloaded
21225 * - metadata: poster and the first few frames of the media may be downloaded to get
21226 *   media dimensions and other metadata
21227 * - auto: allow the media and metadata for the media to be downloaded before
21228 *    interaction
21229 *
21230 * @method Html5#preload
21231 * @return {string}
21232 *         The value of `preload` from the media element. Will be 'none', 'metadata',
21233 *         or 'auto'.
21234 *
21235 * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#attr-media-preload}
21236 */
21237'preload',
21238
21239/**
21240 * Get the value of the `error` from the media element. `error` indicates any
21241 * MediaError that may have occured during playback. If error returns null there is no
21242 * current error.
21243 *
21244 * @method Html5#error
21245 * @return {MediaError|null}
21246 *         The value of `error` from the media element. Will be `MediaError` if there
21247 *         is a current error and null otherwise.
21248 *
21249 * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-media-error}
21250 */
21251'error',
21252
21253/**
21254 * Get the value of `seeking` from the media element. `seeking` indicates whether the
21255 * media is currently seeking to a new position or not.
21256 *
21257 * @method Html5#seeking
21258 * @return {boolean}
21259 *         - The value of `seeking` from the media element.
21260 *         - True indicates that the media is currently seeking to a new position.
21261 *         - Flase indicates that the media is not seeking to a new position at this time.
21262 *
21263 * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-media-seeking}
21264 */
21265'seeking',
21266
21267/**
21268 * Get the value of `seekable` from the media element. `seekable` returns a
21269 * `TimeRange` object indicating ranges of time that can currently be `seeked` to.
21270 *
21271 * @method Html5#seekable
21272 * @return {TimeRange}
21273 *         The value of `seekable` from the media element. A `TimeRange` object
21274 *         indicating the current ranges of time that can be seeked to.
21275 *
21276 * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-media-seekable}
21277 */
21278'seekable',
21279
21280/**
21281 * Get the value of `ended` from the media element. `ended` indicates whether
21282 * the media has reached the end or not.
21283 *
21284 * @method Html5#ended
21285 * @return {boolean}
21286 *         - The value of `ended` from the media element.
21287 *         - True indicates that the media has ended.
21288 *         - False indicates that the media has not ended.
21289 *
21290 * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-media-ended}
21291 */
21292'ended',
21293
21294/**
21295 * Get the value of `playbackRate` from the media element. `playbackRate` indicates
21296 * the rate at which the media is currently playing back. Examples:
21297 *   - if playbackRate is set to 2, media will play twice as fast.
21298 *   - if playbackRate is set to 0.5, media will play half as fast.
21299 *
21300 * @method Html5#playbackRate
21301 * @return {number}
21302 *         The value of `playbackRate` from the media element. A number indicating
21303 *         the current playback speed of the media, where 1 is normal speed.
21304 *
21305 * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-media-playbackrate}
21306 */
21307'playbackRate',
21308
21309/**
21310 * Get the value of `defaultPlaybackRate` from the media element. `defaultPlaybackRate` indicates
21311 * the rate at which the media is currently playing back. This value will not indicate the current
21312 * `playbackRate` after playback has started, use {@link Html5#playbackRate} for that.
21313 *
21314 * Examples:
21315 *   - if defaultPlaybackRate is set to 2, media will play twice as fast.
21316 *   - if defaultPlaybackRate is set to 0.5, media will play half as fast.
21317 *
21318 * @method Html5.prototype.defaultPlaybackRate
21319 * @return {number}
21320 *         The value of `defaultPlaybackRate` from the media element. A number indicating
21321 *         the current playback speed of the media, where 1 is normal speed.
21322 *
21323 * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-media-playbackrate}
21324 */
21325'defaultPlaybackRate',
21326
21327/**
21328 * Get the value of `played` from the media element. `played` returns a `TimeRange`
21329 * object representing points in the media timeline that have been played.
21330 *
21331 * @method Html5#played
21332 * @return {TimeRange}
21333 *         The value of `played` from the media element. A `TimeRange` object indicating
21334 *         the ranges of time that have been played.
21335 *
21336 * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-media-played}
21337 */
21338'played',
21339
21340/**
21341 * Get the value of `networkState` from the media element. `networkState` indicates
21342 * the current network state. It returns an enumeration from the following list:
21343 * - 0: NETWORK_EMPTY
21344 * - 1: NEWORK_IDLE
21345 * - 2: NETWORK_LOADING
21346 * - 3: NETWORK_NO_SOURCE
21347 *
21348 * @method Html5#networkState
21349 * @return {number}
21350 *         The value of `networkState` from the media element. This will be a number
21351 *         from the list in the description.
21352 *
21353 * @see [Spec] {@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-media-networkstate}
21354 */
21355'networkState',
21356
21357/**
21358 * Get the value of `readyState` from the media element. `readyState` indicates
21359 * the current state of the media element. It returns an enumeration from the
21360 * following list:
21361 * - 0: HAVE_NOTHING
21362 * - 1: HAVE_METADATA
21363 * - 2: HAVE_CURRENT_DATA
21364 * - 3: HAVE_FUTURE_DATA
21365 * - 4: HAVE_ENOUGH_DATA
21366 *
21367 * @method Html5#readyState
21368 * @return {number}
21369 *         The value of `readyState` from the media element. This will be a number
21370 *         from the list in the description.
21371 *
21372 * @see [Spec] {@link https://www.w3.org/TR/html5/embedded-content-0.html#ready-states}
21373 */
21374'readyState',
21375
21376/**
21377 * Get the value of `videoWidth` from the video element. `videoWidth` indicates
21378 * the current width of the video in css pixels.
21379 *
21380 * @method Html5#videoWidth
21381 * @return {number}
21382 *         The value of `videoWidth` from the video element. This will be a number
21383 *         in css pixels.
21384 *
21385 * @see [Spec] {@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-video-videowidth}
21386 */
21387'videoWidth',
21388
21389/**
21390 * Get the value of `videoHeight` from the video element. `videoHeigth` indicates
21391 * the current height of the video in css pixels.
21392 *
21393 * @method Html5#videoHeight
21394 * @return {number}
21395 *         The value of `videoHeight` from the video element. This will be a number
21396 *         in css pixels.
21397 *
21398 * @see [Spec] {@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-video-videowidth}
21399 */
21400'videoHeight'].forEach(function (prop) {
21401  Html5.prototype[prop] = function () {
21402    return this.el_[prop];
21403  };
21404});
21405
21406// Wrap native properties with a setter in this format:
21407// set + toTitleCase(name)
21408// The list is as follows:
21409// setVolume, setSrc, setPoster, setPreload, setPlaybackRate, setDefaultPlaybackRate
21410[
21411/**
21412 * Set the value of `volume` on the media element. `volume` indicates the current
21413 * audio level as a percentage in decimal form. This means that 1 is 100%, 0.5 is 50%, and
21414 * so on.
21415 *
21416 * @method Html5#setVolume
21417 * @param {number} percentAsDecimal
21418 *        The volume percent as a decimal. Valid range is from 0-1.
21419 *
21420 * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-a-volume}
21421 */
21422'volume',
21423
21424/**
21425 * Set the value of `src` on the media element. `src` indicates the current
21426 * {@link Tech~SourceObject} for the media.
21427 *
21428 * @method Html5#setSrc
21429 * @param {Tech~SourceObject} src
21430 *        The source object to set as the current source.
21431 *
21432 * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-media-src}
21433 */
21434'src',
21435
21436/**
21437 * Set the value of `poster` on the media element. `poster` is the url to
21438 * an image file that can/will be shown when no media data is available.
21439 *
21440 * @method Html5#setPoster
21441 * @param {string} poster
21442 *        The url to an image that should be used as the `poster` for the media
21443 *        element.
21444 *
21445 * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#attr-media-poster}
21446 */
21447'poster',
21448
21449/**
21450 * Set the value of `preload` on the media element. `preload` indicates
21451 * what should download before the media is interacted with. It can have the following
21452 * values:
21453 * - none: nothing should be downloaded
21454 * - metadata: poster and the first few frames of the media may be downloaded to get
21455 *   media dimensions and other metadata
21456 * - auto: allow the media and metadata for the media to be downloaded before
21457 *    interaction
21458 *
21459 * @method Html5#setPreload
21460 * @param {string} preload
21461 *         The value of `preload` to set on the media element. Must be 'none', 'metadata',
21462 *         or 'auto'.
21463 *
21464 * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#attr-media-preload}
21465 */
21466'preload',
21467
21468/**
21469 * Set the value of `playbackRate` on the media element. `playbackRate` indicates
21470 * the rate at which the media should play back. Examples:
21471 *   - if playbackRate is set to 2, media will play twice as fast.
21472 *   - if playbackRate is set to 0.5, media will play half as fast.
21473 *
21474 * @method Html5#setPlaybackRate
21475 * @return {number}
21476 *         The value of `playbackRate` from the media element. A number indicating
21477 *         the current playback speed of the media, where 1 is normal speed.
21478 *
21479 * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-media-playbackrate}
21480 */
21481'playbackRate',
21482
21483/**
21484 * Set the value of `defaultPlaybackRate` on the media element. `defaultPlaybackRate` indicates
21485 * the rate at which the media should play back upon initial startup. Changing this value
21486 * after a video has started will do nothing. Instead you should used {@link Html5#setPlaybackRate}.
21487 *
21488 * Example Values:
21489 *   - if playbackRate is set to 2, media will play twice as fast.
21490 *   - if playbackRate is set to 0.5, media will play half as fast.
21491 *
21492 * @method Html5.prototype.setDefaultPlaybackRate
21493 * @return {number}
21494 *         The value of `defaultPlaybackRate` from the media element. A number indicating
21495 *         the current playback speed of the media, where 1 is normal speed.
21496 *
21497 * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-media-defaultplaybackrate}
21498 */
21499'defaultPlaybackRate'].forEach(function (prop) {
21500  Html5.prototype['set' + toTitleCase(prop)] = function (v) {
21501    this.el_[prop] = v;
21502  };
21503});
21504
21505// wrap native functions with a function
21506// The list is as follows:
21507// pause, load play
21508[
21509/**
21510 * A wrapper around the media elements `pause` function. This will call the `HTML5`
21511 * media elements `pause` function.
21512 *
21513 * @method Html5#pause
21514 * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-media-pause}
21515 */
21516'pause',
21517
21518/**
21519 * A wrapper around the media elements `load` function. This will call the `HTML5`s
21520 * media element `load` function.
21521 *
21522 * @method Html5#load
21523 * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-media-load}
21524 */
21525'load',
21526
21527/**
21528 * A wrapper around the media elements `play` function. This will call the `HTML5`s
21529 * media element `play` function.
21530 *
21531 * @method Html5#play
21532 * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-media-play}
21533 */
21534'play'].forEach(function (prop) {
21535  Html5.prototype[prop] = function () {
21536    return this.el_[prop]();
21537  };
21538});
21539
21540Tech.withSourceHandlers(Html5);
21541
21542/**
21543 * Native source handler for Html5, simply passes the source to the media element.
21544 *
21545 * @proprety {Tech~SourceObject} source
21546 *        The source object
21547 *
21548 * @proprety {Html5} tech
21549 *        The instance of the HTML5 tech.
21550 */
21551Html5.nativeSourceHandler = {};
21552
21553/**
21554 * Check if the media element can play the given mime type.
21555 *
21556 * @param {string} type
21557 *        The mimetype to check
21558 *
21559 * @return {string}
21560 *         'probably', 'maybe', or '' (empty string)
21561 */
21562Html5.nativeSourceHandler.canPlayType = function (type) {
21563  // IE9 on Windows 7 without MediaPlayer throws an error here
21564  // https://github.com/videojs/video.js/issues/519
21565  try {
21566    return Html5.TEST_VID.canPlayType(type);
21567  } catch (e) {
21568    return '';
21569  }
21570};
21571
21572/**
21573 * Check if the media element can handle a source natively.
21574 *
21575 * @param {Tech~SourceObject} source
21576 *         The source object
21577 *
21578 * @param {Object} [options]
21579 *         Options to be passed to the tech.
21580 *
21581 * @return {string}
21582 *         'probably', 'maybe', or '' (empty string).
21583 */
21584Html5.nativeSourceHandler.canHandleSource = function (source, options) {
21585
21586  // If a type was provided we should rely on that
21587  if (source.type) {
21588    return Html5.nativeSourceHandler.canPlayType(source.type);
21589
21590    // If no type, fall back to checking 'video/[EXTENSION]'
21591  } else if (source.src) {
21592    var ext = getFileExtension(source.src);
21593
21594    return Html5.nativeSourceHandler.canPlayType('video/' + ext);
21595  }
21596
21597  return '';
21598};
21599
21600/**
21601 * Pass the source to the native media element.
21602 *
21603 * @param {Tech~SourceObject} source
21604 *        The source object
21605 *
21606 * @param {Html5} tech
21607 *        The instance of the Html5 tech
21608 *
21609 * @param {Object} [options]
21610 *        The options to pass to the source
21611 */
21612Html5.nativeSourceHandler.handleSource = function (source, tech, options) {
21613  tech.setSrc(source.src);
21614};
21615
21616/**
21617 * A noop for the native dispose function, as cleanup is not needed.
21618 */
21619Html5.nativeSourceHandler.dispose = function () {};
21620
21621// Register the native source handler
21622Html5.registerSourceHandler(Html5.nativeSourceHandler);
21623
21624Tech.registerTech('Html5', Html5);
21625
21626var _templateObject$1 = taggedTemplateLiteralLoose(['\n        Using the tech directly can be dangerous. I hope you know 
21626what 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      ']);
21627
21628/**
21629 * @file player.js
21630 */
21631// Subclasses Component
21632// The following imports are used only to ensure that the corresponding modules
21633// are always included in the video.js package. Importing the modules will
21634// execute them and they will register themselves with video.js.
21635// Import Html5 tech, at least for disposing the original video tag.
vendor: 14,011 bytes, lines 21635-22158
21635
21636// The following tech events are simply re-triggered
21637// on the player when they happen
21638var TECH_EVENTS_RETRIGGER = [
21639/**
21640 * Fired while the user agent is downloading media data.
21641 *
21642 * @event Player#progress
21643 * @type {EventTarget~Event}
21644 */
21645/**
21646 * Retrigger the `progress` event that was triggered by the {@link Tech}.
21647 *
21648 * @private
21649 * @method Player#handleTechProgress_
21650 * @fires Player#progress
21651 * @listens Tech#progress
21652 */
21653'progress',
21654
21655/**
21656 * Fires when the loading of an audio/video is aborted.
21657 *
21658 * @event Player#abort
21659 * @type {EventTarget~Event}
21660 */
21661/**
21662 * Retrigger the `abort` event that was triggered by the {@link Tech}.
21663 *
21664 * @private
21665 * @method Player#handleTechAbort_
21666 * @fires Player#abort
21667 * @listens Tech#abort
21668 */
21669'abort',
21670
21671/**
21672 * Fires when the browser is intentionally not getting media data.
21673 *
21674 * @event Player#suspend
21675 * @type {EventTarget~Event}
21676 */
21677/**
21678 * Retrigger the `suspend` event that was triggered by the {@link Tech}.
21679 *
21680 * @private
21681 * @method Player#handleTechSuspend_
21682 * @fires Player#suspend
21683 * @listens Tech#suspend
21684 */
21685'suspend',
21686
21687/**
21688 * Fires when the current playlist is empty.
21689 *
21690 * @event Player#emptied
21691 * @type {EventTarget~Event}
21692 */
21693/**
21694 * Retrigger the `emptied` event that was triggered by the {@link Tech}.
21695 *
21696 * @private
21697 * @method Player#handleTechEmptied_
21698 * @fires Player#emptied
21699 * @listens Tech#emptied
21700 */
21701'emptied',
21702/**
21703 * Fires when the browser is trying to get media data, but data is not available.
21704 *
21705 * @event Player#stalled
21706 * @type {EventTarget~Event}
21707 */
21708/**
21709 * Retrigger the `stalled` event that was triggered by the {@link Tech}.
21710 *
21711 * @private
21712 * @method Player#handleTechStalled_
21713 * @fires Player#stalled
21714 * @listens Tech#stalled
21715 */
21716'stalled',
21717
21718/**
21719 * Fires when the browser has loaded meta data for the audio/video.
21720 *
21721 * @event Player#loadedmetadata
21722 * @type {EventTarget~Event}
21723 */
21724/**
21725 * Retrigger the `stalled` event that was triggered by the {@link Tech}.
21726 *
21727 * @private
21728 * @method Player#handleTechLoadedmetadata_
21729 * @fires Player#loadedmetadata
21730 * @listens Tech#loadedmetadata
21731 */
21732'loadedmetadata',
21733
21734/**
21735 * Fires when the browser has loaded the current frame of the audio/video.
21736 *
21737 * @event Player#loadeddata
21738 * @type {event}
21739 */
21740/**
21741 * Retrigger the `loadeddata` event that was triggered by the {@link Tech}.
21742 *
21743 * @private
21744 * @method Player#handleTechLoaddeddata_
21745 * @fires Player#loadeddata
21746 * @listens Tech#loadeddata
21747 */
21748'loadeddata',
21749
21750/**
21751 * Fires when the current playback position has changed.
21752 *
21753 * @event Player#timeupdate
21754 * @type {event}
21755 */
21756/**
21757 * Retrigger the `timeupdate` event that was triggered by the {@link Tech}.
21758 *
21759 * @private
21760 * @method Player#handleTechTimeUpdate_
21761 * @fires Player#timeupdate
21762 * @listens Tech#timeupdate
21763 */
21764'timeupdate',
21765
21766/**
21767 * Fires when the video's intrinsic dimensions change
21768 *
21769 * @event Player#resize
21770 * @type {event}
21771 */
21772/**
21773 * Retrigger the `resize` event that was triggered by the {@link Tech}.
21774 *
21775 * @private
21776 * @method Player#handleTechResize_
21777 * @fires Player#resize
21778 * @listens Tech#resize
21779 */
21780'resize',
21781
21782/**
21783 * Fires when the volume has been changed
21784 *
21785 * @event Player#volumechange
21786 * @type {event}
21787 */
21788/**
21789 * Retrigger the `volumechange` event that was triggered by the {@link Tech}.
21790 *
21791 * @private
21792 * @method Player#handleTechVolumechange_
21793 * @fires Player#volumechange
21794 * @listens Tech#volumechange
21795 */
21796'volumechange',
21797
21798/**
21799 * Fires when the text track has been changed
21800 *
21801 * @event Player#texttrackchange
21802 * @type {event}
21803 */
21804/**
21805 * Retrigger the `texttrackchange` event that was triggered by the {@link Tech}.
21806 *
21807 * @private
21808 * @method Player#handleTechTexttrackchange_
21809 * @fires Player#texttrackchange
21810 * @listens Tech#texttrackchange
21811 */
21812'texttrackchange'];
21813
21814// events to queue when playback rate is zero
21815// this is a hash for the sole purpose of mapping non-camel-cased event names
21816// to camel-cased function names
21817var TECH_EVENTS_QUEUE = {
21818  canplay: 'CanPlay',
21819  canplaythrough: 'CanPlayThrough',
21820  playing: 'Playing',
21821  seeked: 'Seeked'
21822};
21823
21824/**
21825 * An instance of the `Player` class is created when any of the Video.js setup methods
21826 * are used to initialize a video.
21827 *
21828 * After an instance has been created it can be accessed globally in two ways:
21829 * 1. By calling `videojs('example_video_1');`
21830 * 2. By using it directly via  `videojs.players.example_video_1;`
21831 *
21832 * @extends Component
21833 */
21834
21835var Player = function (_Component) {
21836  inherits(Player, _Component);
21837
21838  /**
21839   * Create an instance of this class.
21840   *
21841   * @param {Element} tag
21842   *        The original video DOM element used for configuring options.
21843   *
21844   * @param {Object} [options]
21845   *        Object of option names and values.
21846   *
21847   * @param {Component~ReadyCallback} [ready]
21848   *        Ready callback function.
21849   */
21850  function Player(tag, options, ready) {
21851    classCallCheck(this, Player);
21852
21853    // Make sure tag ID exists
21854    tag.id = tag.id || options.id || 'vjs_video_' + newGUID();
21855
21856    // Set Options
21857    // The options argument overrides options set in the video tag
21858    // which overrides globally set options.
21859    // This latter part coincides with the load order
21860    // (tag must exist before Player)
21861    options = assign(Player.getTagSettings(tag), options);
21862
21863    // Delay the initialization of children because we need to set up
21864    // player properties first, and can't use `this` before `super()`
21865    options.initChildren = false;
21866
21867    // Same with creating the element
21868    options.createEl = false;
21869
21870    // don't auto mixin the evented mixin
21871    options.evented = false;
21872
21873    // we don't want the player to report touch activity on itself
21874    // see enableTouchActivity in Component
21875    options.reportTouchActivity = false;
21876
21877    // If language is not set, get the closest lang attribute
21878    if (!options.language) {
21879      if (typeof tag.closest === 'function') {
21880        var closest = tag.closest('[lang]');
21881
21882        if (closest && closest.getAttribute) {
21883          options.language = closest.getAttribute('lang');
21884        }
21885      } else {
21886        var element = tag;
21887
21888        while (element && element.nodeType === 1) {
21889          if (getAttributes(element).hasOwnProperty('lang')) {
21890            options.language = element.getAttribute('lang');
21891            break;
21892          }
21893          element = element.parentNode;
21894        }
21895      }
21896    }
21897
21898    // Run base component initializing with new options
21899
21900    // Tracks when a tech changes the poster
21901    var _this = possibleConstructorReturn(this, _Component.call(this, null, options, ready));
21902
21903    _this.isPosterFromTech_ = false;
21904
21905    // Holds callback info that gets queued when playback rate is zero
21906    // and a seek is happening
21907    _this.queuedCallbacks_ = [];
21908
21909    // Turn off API access because we're loading a new tech that might load asynchronously
21910    _this.isReady_ = false;
21911
21912    // Init state hasStarted_
21913    _this.hasStarted_ = false;
21914
21915    // Init state userActive_
21916    _this.userActive_ = false;
21917
21918    // if the global option object was accidentally blown away by
21919    // someone, bail early with an informative error
21920    if (!_this.options_ || !_this.options_.techOrder || !_this.options_.techOrder.length) {
21921      throw new Error('No techOrder specified. Did you overwrite ' + 'videojs.options instead of just changing the ' + 'properties you want to override?');
21922    }
21923
21924    // Store the original tag used to set options
21925    _this.tag = tag;
21926
21927    // Store the tag attributes used to restore html5 element
21928    _this.tagAttributes = tag && getAttributes(tag);
21929
21930    // Update current language
21931    _this.language(_this.options_.language);
21932
21933    // Update Supported Languages
21934    if (options.languages) {
21935      // Normalise player option languages to lowercase
21936      var languagesToLower = {};
21937
21938      Object.getOwnPropertyNames(options.languages).forEach(function (name$$1) {
21939        languagesToLower[name$$1.toLowerCase()] = options.languages[name$$1];
21940      });
21941      _this.languages_ = languagesToLower;
21942    } else {
21943      _this.languages_ = Player.prototype.options_.languages;
21944    }
21945
21946    // Cache for video property values.
21947    _this.cache_ = {};
21948
21949    // Set poster
21950    _this.poster_ = options.poster || '';
21951
21952    // Set controls
21953    _this.controls_ = !!options.controls;
21954
21955    // Set default values for lastVolume
21956    _this.cache_.lastVolume = 1;
21957
21958    // Original tag settings stored in options
21959    // now remove immediately so native controls don't flash.
21960    // May be turned back on by HTML5 tech if nativeControlsForTouch is true
21961    tag.controls = false;
21962    tag.removeAttribute('controls');
21963
21964    // the attribute overrides the option
21965    if (tag.hasAttribute('autoplay')) {
21966      _this.options_.autoplay = true;
21967    } else {
21968      // otherwise use the setter to validate and
21969      // set the correct value.
21970      _this.autoplay(_this.options_.autoplay);
21971    }
21972
21973    /*
21974     * Store the internal state of scrubbing
21975     *
21976     * @private
21977     * @return {Boolean} True if the user is scrubbing
21978     */
21979    _this.scrubbing_ = false;
21980
21981    _this.el_ = _this.createEl();
21982
21983    // Set default value for lastPlaybackRate
21984    _this.cache_.lastPlaybackRate = _this.defaultPlaybackRate();
21985
21986    // Make this an evented object and use `el_` as its event bus.
21987    evented(_this, { eventBusKey: 'el_' });
21988
21989    // We also want to pass the original player options to each component and plugin
21990    // as well so they don't need to reach back into the player for options later.
21991    // We also need to do another copy of this.options_ so we don't end up with
21992    // an infinite loop.
21993    var playerOptionsCopy = mergeOptions(_this.options_);
21994
21995    // Load plugins
21996    if (options.plugins) {
21997      var plugins = options.plugins;
21998
21999      Object.keys(plugins).forEach(function (name$$1) {
22000        if (typeof this[name$$1] === 'function') {
22001          this[name$$1](plugins[name$$1]);
22002        } else {
22003          throw new Error('plugin "' + name$$1 + '" does not exist');
22004        }
22005      }, _this);
22006    }
22007
22008    _this.options_.playerOptions = playerOptionsCopy;
22009
22010    _this.middleware_ = [];
22011
22012    _this.initChildren();
22013
22014    // Set isAudio based on whether or not an audio tag was used
22015    _this.isAudio(tag.nodeName.toLowerCase() === 'audio');
22016
22017    // Update controls className. Can't do this when the controls are initially
22018    // set because the element doesn't exist yet.
22019    if (_this.controls()) {
22020      _this.addClass('vjs-controls-enabled');
22021    } else {
22022      _this.addClass('vjs-controls-disabled');
22023    }
22024
22025    // Set ARIA label and region role depending on player type
22026    _this.el_.setAttribute('role', 'region');
22027    if (_this.isAudio()) {
22028      _this.el_.setAttribute('aria-label', _this.localize('Audio Player'));
22029    } else {
22030      _this.el_.setAttribute('aria-label', _this.localize('Video Player'));
22031    }
22032
22033    if (_this.isAudio()) {
22034      _this.addClass('vjs-audio');
22035    }
22036
22037    if (_this.flexNotSupported_()) {
22038      _this.addClass('vjs-no-flex');
22039    }
22040
22041    // TODO: Make this smarter. Toggle user state between touching/mousing
22042    // using events, since devices can have both touch and mouse events.
22043    // if (browser.TOUCH_ENABLED) {
22044    //   this.addClass('vjs-touch-enabled');
22045    // }
22046
22047    // iOS Safari has broken hover handling
22048    if (!IS_IOS) {
22049      _this.addClass('vjs-workinghover');
22050    }
22051
22052    // Make player easily findable by ID
22053    Player.players[_this.id_] = _this;
22054
22055    // Add a major version class to aid css in plugins
22056    var majorVersion = version.split('.')[0];
22057
22058    _this.addClass('vjs-v' + majorVersion);
22059
22060    // When the player is first initialized, trigger activity so components
22061    // like the control bar show themselves if needed
22062    _this.userActive(true);
22063    _this.reportUserActivity();
22064
22065    _this.one('play', _this.listenForUserActivity_);
22066    _this.on('fullscreenchange', _this.handleFullscreenChange_);
22067    _this.on('stageclick', _this.handleStageClick_);
22068
22069    _this.changingSrc_ = false;
22070    _this.playWaitingForReady_ = false;
22071    _this.playOnLoadstart_ = null;
22072    return _this;
22073  }
22074
22075  /**
22076   * Destroys the video player and does any necessary cleanup.
22077   *
22078   * This is especially helpful if you are dynamically adding and removing videos
22079   * to/from the DOM.
22080   *
22081   * @fires Player#dispose
22082   */
22083
22084
22085  Player.prototype.dispose = function dispose() {
22086    /**
22087     * Called when the player is being disposed of.
22088     *
22089     * @event Player#dispose
22090     * @type {EventTarget~Event}
22091     */
22092    this.trigger('dispose');
22093    // prevent dispose from being called twice
22094    this.off('dispose');
22095
22096    if (this.styleEl_ && this.styleEl_.parentNode) {
22097      this.styleEl_.parentNode.removeChild(this.styleEl_);
22098      this.styleEl_ = null;
22099    }
22100
22101    // Kill reference to this player
22102    Player.players[this.id_] = null;
22103
22104    if (this.tag && this.tag.player) {
22105      this.tag.player = null;
22106    }
22107
22108    if (this.el_ && this.el_.player) {
22109      this.el_.player = null;
22110    }
22111
22112    if (this.tech_) {
22113      this.tech_.dispose();
22114      this.isPosterFromTech_ = false;
22115      this.poster_ = '';
22116    }
22117
22118    if (this.playerElIngest_) {
22119      this.playerElIngest_ = null;
22120    }
22121
22122    if (this.tag) {
22123      this.tag = null;
22124    }
22125
22126    clearCacheForPlayer(this);
22127
22128    // the actual .el_ is removed here
22129    _Component.prototype.dispose.call(this);
22130  };
22131
22132  /**
22133   * Create the `Player`'s DOM element.
22134   *
22135   * @return {Element}
22136   *         The DOM element that gets created.
22137   */
22138
22139
22140  Player.prototype.createEl = function createEl$$1() {
22141    var tag = this.tag;
22142    var el = void 0;
22143    var playerElIngest = this.playerElIngest_ = tag.parentNode && tag.parentNode.hasAttribute && tag.parentNode.hasAttribute('data-vjs-player');
22144    var divEmbed = this.tag.tagName.toLowerCase() === 'video-js';
22145
22146    if (playerElIngest) {
22147      el = this.el_ = tag.parentNode;
22148    } else if (!divEmbed) {
22149      el = this.el_ = _Component.prototype.createEl.call(this, 'div');
22150    }
22151
22152    // Copy over all the attributes from the tag, including ID and class
22153    // ID will now reference player box, not the video tag
22154    var attrs = getAttributes(tag);
22155
22156    if (divEmbed) {
22157      el = this.el_ = tag;
22158      tag = this.tag = 
22158document_1.createElement('video');
22159      while (el.children.length) {
22160        tag.appendChild(el.firstChild);
22161      }
22162
22163      if (!hasClass(el, 'video-js')) {
22164        addClass(el, 'video-js');
22165      }
22166
22167      el.appendChild(tag);
22168
22169      playerElIngest = this.playerElIngest_ = el;
22170
22171      // copy over properties from the video-js element
22172      // ie8 doesn't support Object.keys nor hasOwnProperty
22173      // on dom elements so we have to specify properties individually
22174      ['autoplay', 'controls', 'crossOrigin', 'defaultMuted', 'defaultPlaybackRate', 'loop', 'muted', 'playbackRate', 'src', 'volume'].forEach(function (prop) {
22175        if (typeof el[prop] !== 'undefined') {
22176          tag[prop] = el[prop];
22177        }
22178      });
22179    }
22180
22181    // set tabindex to -1 to remove the video element from the focus order
22182    tag.setAttribute('tabindex', '-1');
22183    // Workaround for #4583 (JAWS+IE doesn't announce BPB or play button)
22184    // See https://github.com/FreedomScientific/VFO-standards-support/issues/78
22185    // Note that we can't detect if JAWS is being used, but this ARIA attribute
22186    //  doesn't change behavior of IE11 if JAWS is not being used
22187    if (IE_VERSION) {
22188      tag.setAttribute('role', 'application');
22189    }
22190
22191    // Remove width/height attrs from tag so CSS can make it 100% width/height
22192    tag.removeAttribute('width');
22193    tag.removeAttribute('height');
22194
22195    Object.getOwnPropertyNames(attrs).forEach(function (attr) {
22196      // workaround so we don't totally break IE7
22197      // http://stackoverflow.com/questions/3653444/css-styles-not-applied-on-dynamic-elements-in-internet-explorer-7
22198      if (attr === 'class') {
22199        el.className += ' ' + attrs[attr];
22200
22201        if (divEmbed) {
22202          tag.className += ' ' + attrs[attr];
22203        }
22204      } else {
22205        el.setAttribute(attr, attrs[attr]);
22206
22207        if (divEmbed) {
22208          tag.setAttribute(attr, attrs[attr]);
22209        }
22210      }
22211    });
22212
22213    // Update tag id/class for use as HTML5 playback tech
22214    // Might think we should do this after embedding in container so .vjs-tech class
22215    // doesn't flash 100% width/height, but class only applies with .video-js parent
22216    tag.playerId = tag.id;
22217    tag.id += '_html5_api';
22218    tag.className = 'vjs-tech';
22219
22220    // Make player findable on elements
22221    tag.player = el.player = this;
22222    // Default state of video is paused
22223    this.addClass('vjs-paused');
22224
22225    // Add a style element in the player that we'll use to set the width/height
22226    // of the player in a way that's still overrideable by CSS, just like the
22227    // video element
22228    if (window_1.VIDEOJS_NO_DYNAMIC_STYLE !== true) {
22229      this.styleEl_ = createStyleElement('vjs-styles-dimensions');
22230      var defaultsStyleEl = $('.vjs-styles-defaults');
22231      var head = $('head');
22232
22233      head.insertBefore(this.styleEl_, defaultsStyleEl ? defaultsStyleEl.nextSibling : head.firstChild);
22234    }
22235
22236    // Pass in the width/height/aspectRatio options which will update the style el
22237    this.width(this.options_.width);
22238    this.height(this.options_.height);
22239    this.fluid(this.options_.fluid);
22240    this.aspectRatio(this.options_.aspectRatio);
22241
22242    // Hide any links within the video/audio tag, because IE doesn't hide them completely.
22243    var links = tag.getElementsByTagName('a');
22244
22245    for (var i = 0; i < links.length; i++) {
22246      var linkEl = links.item(i);
22247
22248      addClass(linkEl, 'vjs-hidden');
22249      linkEl.setAttribute('hidden', 'hidden');
22250    }
22251
22252    // insertElFirst seems to cause the networkState to flicker from 3 to 2, so
22253    // keep track of the original for later so we can know if the source originally failed
22254    tag.initNetworkState_ = tag.networkState;
22255
22256    // Wrap video tag in div (el/box) container
22257    if (tag.parentNode && !playerElIngest) {
22258      tag.parentNode.insertBefore(el, tag);
22259    }
22260
22261    // insert the tag as the first child of the player element
22262    // then manually add it to the children array so that this.addChild
22263    // will work properly for other components
22264    //
22265    // Breaks iPhone, fixed in HTML5 setup.
22266    prependTo(tag, el);
22267    this.children_.unshift(tag);
22268
22269    // Set lang attr on player to ensure CSS :lang() in consistent with player
22270    // if it's been set to something different to the doc
22271    this.el_.setAttribute('lang', this.language_);
22272
22273    this.el_ = el;
22274
22275    return el;
22276  };
22277
22278  /**
22279   * A getter/setter for the `Player`'s width. Returns the player'
vendor: 3,783 bytes, lines 22279-22430
22279s configured value.
22280   * To get the current width use `currentWidth()`.
22281   *
22282   * @param {number} [value]
22283   *        The value to set the `Player`'s width to.
22284   *
22285   * @return {number}
22286   *         The current width of the `Player` when getting.
22287   */
22288
22289
22290  Player.prototype.width = function width(value) {
22291    return this.dimension('width', value);
22292  };
22293
22294  /**
22295   * A getter/setter for the `Player`'s height. Returns the player's configured value.
22296   * To get the current height use `currentheight()`.
22297   *
22298   * @param {number} [value]
22299   *        The value to set the `Player`'s heigth to.
22300   *
22301   * @return {number}
22302   *         The current height of the `Player` when getting.
22303   */
22304
22305
22306  Player.prototype.height = function height(value) {
22307    return this.dimension('height', value);
22308  };
22309
22310  /**
22311   * A getter/setter for the `Player`'s width & height.
22312   *
22313   * @param {string} dimension
22314   *        This string can be:
22315   *        - 'width'
22316   *        - 'height'
22317   *
22318   * @param {number} [value]
22319   *        Value for dimension specified in the first argument.
22320   *
22321   * @return {number}
22322   *         The dimension arguments value when getting (width/height).
22323   */
22324
22325
22326  Player.prototype.dimension = function dimension(_dimension, value) {
22327    var privDimension = _dimension + '_';
22328
22329    if (value === undefined) {
22330      return this[privDimension] || 0;
22331    }
22332
22333    if (value === '') {
22334      // If an empty string is given, reset the dimension to be automatic
22335      this[privDimension] = undefined;
22336      this.updateStyleEl_();
22337      return;
22338    }
22339
22340    var parsedVal = parseFloat(value);
22341
22342    if (isNaN(parsedVal)) {
22343      log$1.error('Improper value "' + value + '" supplied for for ' + _dimension);
22344      return;
22345    }
22346
22347    this[privDimension] = parsedVal;
22348    this.updateStyleEl_();
22349  };
22350
22351  /**
22352   * A getter/setter/toggler for the vjs-fluid `className` on the `Player`.
22353   *
22354   * @param {boolean} [bool]
22355   *        - A value of true adds the class.
22356   *        - A value of false removes the class.
22357   *        - No value will toggle the fluid class.
22358   *
22359   * @return {boolean|undefined}
22360   *         - The value of fluid when getting.
22361   *         - `undefined` when setting.
22362   */
22363
22364
22365  Player.prototype.fluid = function fluid(bool) {
22366    if (bool === undefined) {
22367      return !!this.fluid_;
22368    }
22369
22370    this.fluid_ = !!bool;
22371
22372    if (bool) {
22373      this.addClass('vjs-fluid');
22374    } else {
22375      this.removeClass('vjs-fluid');
22376    }
22377
22378    this.updateStyleEl_();
22379  };
22380
22381  /**
22382   * Get/Set the aspect ratio
22383   *
22384   * @param {string} [ratio]
22385   *        Aspect ratio for player
22386   *
22387   * @return {string|undefined}
22388   *         returns the current aspect ratio when getting
22389   */
22390
22391  /**
22392   * A getter/setter for the `Player`'s aspect ratio.
22393   *
22394   * @param {string} [ratio]
22395   *        The value to set the `Player's aspect ratio to.
22396   *
22397   * @return {string|undefined}
22398   *         - The current aspect ratio of the `Player` when getting.
22399   *         - undefined when setting
22400   */
22401
22402
22403  Player.prototype.aspectRatio = function aspectRatio(ratio) {
22404    if (ratio === undefined) {
22405      return this.aspectRatio_;
22406    }
22407
22408    // Check for width:height format
22409    if (!/^\d+\:\d+$/.test(ratio)) {
22410      throw new Error('Improper value supplied for aspect ratio. The format should be width:height, for example 16:9.');
22411    }
22412    this.aspectRatio_ = ratio;
22413
22414    // We're assuming if you set an aspect ratio you want fluid mode,
22415    // because in fixed mode you could calculate width and height yourself.
22416    this.fluid(true);
22417
22418    this.updateStyleEl_();
22419  };
22420
22421  /**
22422   * Update styles of the `Player` element (height, width and aspect ratio).
22423   *
22424   * @private
22425   * @listens Tech#loadedmetadata
22426   */
22427
22428
22429  Player.prototype.updateStyleEl_ = function updateStyleEl_() {
22430    if (window_1
vendor: 9,090 bytes, lines 22430-22692
22430.VIDEOJS_NO_DYNAMIC_STYLE === true) {
22431      var _width = typeof this.width_ === 'number' ? this.width_ : this.options_.width;
22432      var _height = typeof this.height_ === 'number' ? this.height_ : this.options_.height;
22433      var techEl = this.tech_ && this.tech_.el();
22434
22435      if (techEl) {
22436        if (_width >= 0) {
22437          techEl.width = _width;
22438        }
22439        if (_height >= 0) {
22440          techEl.height = _height;
22441        }
22442      }
22443
22444      return;
22445    }
22446
22447    var width = void 0;
22448    var height = void 0;
22449    var aspectRatio = void 0;
22450    var idClass = void 0;
22451
22452    // The aspect ratio is either used directly or to calculate width and height.
22453    if (this.aspectRatio_ !== undefined && this.aspectRatio_ !== 'auto') {
22454      // Use any aspectRatio that's been specifically set
22455      aspectRatio = this.aspectRatio_;
22456    } else if (this.videoWidth() > 0) {
22457      // Otherwise try to get the aspect ratio from the video metadata
22458      aspectRatio = this.videoWidth() + ':' + this.videoHeight();
22459    } else {
22460      // Or use a default. The video element's is 2:1, but 16:9 is more common.
22461      aspectRatio = '16:9';
22462    }
22463
22464    // Get the ratio as a decimal we can use to calculate dimensions
22465    var ratioParts = aspectRatio.split(':');
22466    var ratioMultiplier = ratioParts[1] / ratioParts[0];
22467
22468    if (this.width_ !== undefined) {
22469      // Use any width that's been specifically set
22470      width = this.width_;
22471    } else if (this.height_ !== undefined) {
22472      // Or calulate the width from the aspect ratio if a height has been set
22473      width = this.height_ / ratioMultiplier;
22474    } else {
22475      // Or use the video's metadata, or use the video el's default of 300
22476      width = this.videoWidth() || 300;
22477    }
22478
22479    if (this.height_ !== undefined) {
22480      // Use any height that's been specifically set
22481      height = this.height_;
22482    } else {
22483      // Otherwise calculate the height from the ratio and the width
22484      height = width * ratioMultiplier;
22485    }
22486
22487    // Ensure the CSS class is valid by starting with an alpha character
22488    if (/^[^a-zA-Z]/.test(this.id())) {
22489      idClass = 'dimensions-' + this.id();
22490    } else {
22491      idClass = this.id() + '-dimensions';
22492    }
22493
22494    // Ensure the right class is still on the player for the style element
22495    this.addClass(idClass);
22496
22497    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    ');
22498  };
22499
22500  /**
22501   * Load/Create an instance of playback {@link Tech} including element
22502   * and API methods. Then append the `Tech` element in `Player` as a child.
22503   *
22504   * @param {string} techName
22505   *        name of the playback technology
22506   *
22507   * @param {string} source
22508   *        video source
22509   *
22510   * @private
22511   */
22512
22513
22514  Player.prototype.loadTech_ = function loadTech_(techName, source) {
22515    var _this2 = this;
22516
22517    // Pause and remove current playback technology
22518    if (this.tech_) {
22519      this.unloadTech_();
22520    }
22521
22522    var titleTechName = toTitleCase(techName);
22523    var camelTechName = techName.charAt(0).toLowerCase() + techName.slice(1);
22524
22525    // get rid of the HTML5 video tag as soon as we are using another tech
22526    if (titleTechName !== 'Html5' && this.tag) {
22527      Tech.getTech('Html5').disposeMediaElement(this.tag);
22528      this.tag.player = null;
22529      this.tag = null;
22530    }
22531
22532    this.techName_ = titleTechName;
22533
22534    // Turn off API access because we're loading a new tech that might load asynchronously
22535    this.isReady_ = false;
22536
22537    // if autoplay is a string we pass false to the tech
22538    // because the player is going to handle autoplay on `loadstart`
22539    var autoplay = typeof this.autoplay() === 'string' ? false : this.autoplay();
22540
22541    // Grab tech-specific options from player options and add source and parent element to use.
22542    var techOptions = {
22543      source: source,
22544      autoplay: autoplay,
22545      'nativeControlsForTouch': this.options_.nativeControlsForTouch,
22546      'playerId': this.id(),
22547      'techId': this.id() + '_' + titleTechName + '_api',
22548      'playsinline': this.options_.playsinline,
22549      'preload': this.options_.preload,
22550      'loop': this.options_.loop,
22551      'muted': this.options_.muted,
22552      'poster': this.poster(),
22553      'language': this.language(),
22554      'playerElIngest': this.playerElIngest_ || false,
22555      'vtt.js': this.options_['vtt.js'],
22556      'canOverridePoster': !!this.options_.techCanOverridePoster,
22557      'enableSourceset': this.options_.enableSourceset
22558    };
22559
22560    ALL.names.forEach(function (name$$1) {
22561      var props = ALL[name$$1];
22562
22563      techOptions[props.getterName] = _this2[props.privateName];
22564    });
22565
22566    assign(techOptions, this.options_[titleTechName]);
22567    assign(techOptions, this.options_[camelTechName]);
22568    assign(techOptions, this.options_[techName.toLowerCase()]);
22569
22570    if (this.tag) {
22571      techOptions.tag = this.tag;
22572    }
22573
22574    if (source && source.src === this.cache_.src && this.cache_.currentTime > 0) {
22575      techOptions.startTime = this.cache_.currentTime;
22576    }
22577
22578    // Initialize tech instance
22579    var TechClass = Tech.getTech(techName);
22580
22581    if (!TechClass) {
22582      throw new Error('No Tech named \'' + titleTechName + '\' exists! \'' + titleTechName + '\' should be registered using videojs.registerTech()\'');
22583    }
22584
22585    this.tech_ = new TechClass(techOptions);
22586
22587    // player.triggerReady is always async, so don't need this to be async
22588    this.tech_.ready(bind(this, this.handleTechReady_), true);
22589
22590    textTrackConverter.jsonToTextTracks(this.textTracksJson_ || [], this.tech_);
22591
22592    // Listen to all HTML5-defined events and trigger them on the player
22593    TECH_EVENTS_RETRIGGER.forEach(function (event) {
22594      _this2.on(_this2.tech_, event, _this2['handleTech' + toTitleCase(event) + '_']);
22595    });
22596
22597    Object.keys(TECH_EVENTS_QUEUE).forEach(function (event) {
22598      _this2.on(_this2.tech_, event, function (eventObj) {
22599        if (_this2.tech_.playbackRate() === 0 && _this2.tech_.seeking()) {
22600          _this2.queuedCallbacks_.push({
22601            callback: _this2['handleTech' + TECH_EVENTS_QUEUE[event] + '_'].bind(_this2),
22602            event: eventObj
22603          });
22604          return;
22605        }
22606        _this2['handleTech' + TECH_EVENTS_QUEUE[event] + '_'](eventObj);
22607      });
22608    });
22609
22610    this.on(this.tech_, 'loadstart', this.handleTechLoadStart_);
22611    this.on(this.tech_, 'sourceset', this.handleTechSourceset_);
22612    this.on(this.tech_, 'waiting', this.handleTechWaiting_);
22613    this.on(this.tech_, 'ended', this.handleTechEnded_);
22614    this.on(this.tech_, 'seeking', this.handleTechSeeking_);
22615    this.on(this.tech_, 'play', this.handleTechPlay_);
22616    this.on(this.tech_, 'firstplay', this.handleTechFirstPlay_);
22617    this.on(this.tech_, 'pause', this.handleTechPause_);
22618    this.on(this.tech_, 'durationchange', this.handleTechDurationChange_);
22619    this.on(this.tech_, 'fullscreenchange', this.handleTechFullscreenChange_);
22620    this.on(this.tech_, 'error', this.handleTechError_);
22621    this.on(this.tech_, 'loadedmetadata', this.updateStyleEl_);
22622    this.on(this.tech_, 'posterchange', this.handleTechPosterChange_);
22623    this.on(this.tech_, 'textdata', this.handleTechTextData_);
22624    this.on(this.tech_, 'ratechange', this.handleTechRateChange_);
22625
22626    this.usingNativeControls(this.techGet_('controls'));
22627
22628    if (this.controls() && !this.usingNativeControls()) {
22629      this.addTechControlsListeners_();
22630    }
22631
22632    // Add the tech element in the DOM if it was not already there
22633    // Make sure to not insert the original video element if using Html5
22634    if (this.tech_.el().parentNode !== this.el() && (titleTechName !== 'Html5' || !this.tag)) {
22635      prependTo(this.tech_.el(), this.el());
22636    }
22637
22638    // Get rid of the original video tag reference after the first tech is loaded
22639    if (this.tag) {
22640      this.tag.player = null;
22641      this.tag = null;
22642    }
22643  };
22644
22645  /**
22646   * Unload and dispose of the current playback {@link Tech}.
22647   *
22648   * @private
22649   */
22650
22651
22652  Player.prototype.unloadTech_ = function unloadTech_() {
22653    var _this3 = this;
22654
22655    // Save the current text tracks so that we can reuse the same text tracks with the next tech
22656    ALL.names.forEach(function (name$$1) {
22657      var props = ALL[name$$1];
22658
22659      _this3[props.privateName] = _this3[props.getterName]();
22660    });
22661    this.textTracksJson_ = textTrackConverter.textTracksToJson(this.tech_);
22662
22663    this.isReady_ = false;
22664
22665    this.tech_.dispose();
22666
22667    this.tech_ = false;
22668
22669    if (this.isPosterFromTech_) {
22670      this.poster_ = '';
22671      this.trigger('posterchange');
22672    }
22673
22674    this.isPosterFromTech_ = false;
22675  };
22676
22677  /**
22678   * Return a reference to the current {@link Tech}.
22679   * It will print a warning by default about the danger of using the tech directly
22680   * but any argument that is passed in will silence the warning.
22681   *
22682   * @param {*} [safety]
22683   *        Anything passed in to silence the warning
22684   *
22685   * @return {Tech}
22686   *         The Tech
22687   */
22688
22689
22690  Player.prototype.tech = function tech(safety) {
22691    if (safety === undefined) {
22692      log$1.warn(
22692tsml(_templateObject$1));
22693    }
22694
22695    return this.tech_;
22696  };
22697
22698  /**
22699   * Set up click and touch listeners for the playback element
22700   *
22701   * - On desktops: a click on the video itself will toggle playback
22702   * - On mobile devices: a click on the video toggles controls
22703   *   which is done by toggling the user state between active and
22704   *   inactive
22705   * - A tap can signal that a user has become active or has become inactive
22706   *   e.g. a quick tap on an iPhone movie should reveal the controls. Another
22707   *   quick tap should hide them again (signaling the user is in an inactive
22708   *   viewing state)
22709   * - In addition to this, we still want the user to be considered inactive after
22710   *   a few seconds of inactivity.
22711   *
22712   * > Note: the only part of iOS interaction we can't mimic with this setup
22713   * is a touch and hold on the video element counting as activity in order to
22714   * keep the controls showing, but that shouldn't be an issue. A touch and hold
22715   * on any controls will still keep the user active
22716   *
22717   * @private
22718   */
22719
22720
22721  Player.prototype.addTechControlsListeners_ = function addTechControlsListeners_() {
22722    // Make sure to remove all the previous listeners in case we are called multiple times.
22723    this.removeTechControlsListeners_();
22724
22725    // Some browsers (Chrome & IE) don't trigger a click on a flash swf, but do
22726    // trigger mousedown/up.
22727    // http://stackoverflow.com/questions/1444562/javascript-onclick-event-over-flash-object
22728    // Any touch events are set to block the mousedown event from happening
22729    this.on(this.tech_, 'mousedown', this.handleTechClick_);
22730
22731    // If the controls were hidden we don't want that to change without a tap event
22732    // so we'll check if the controls were already showing before reporting user
22733    // activity
22734    this.on(this.tech_, 'touchstart', this.handleTechTouchStart_);
22735    this.on(this.tech_, 'touchmove', this.handleTechTouchMove_);
22736    this.on(this.tech_, 'touchend', this.handleTechTouchEnd_);
22737
22738    // The tap listener needs to come after the touchend listener because the tap
22739    // listener cancels out any reportedUserActivity when setting userActive(false)
22740    this.on(this.tech_, 'tap', this.handleTechTap_);
22741  };
22742
22743  /**
22744   * Remove the listeners used for click and tap controls. This is needed for
22745   * toggling to controls disabled, where a tap/touch should do nothing.
22746   *
22747   * @private
22748   */
22749
22750
22751  Player.prototype.removeTechControlsListeners_ = function removeTechControlsListeners_() {
22752    // We don't want to just use `this.off()` because there might be other needed
22753    // listeners added by techs that extend this.
22754    this.off(this.tech_, 'tap', this.handleTechTap_);
22755    this.off(this.tech_, 'touchstart', this.handleTechTouchStart_);
22756    this.off(this.tech_, 'touchmove', this.handleTechTouchMove_);
22757    this.off(this.tech_, 'touchend', this.handleTechTouchEnd_);
22758    this.off(this.tech_, 'mousedown', this.handleTechClick_);
22759  };
22760
22761  /**
22762   * Player waits for the tech to be ready
22763   *
22764   * @private
22765   */
22766
22767
22768  Player.prototype.handleTechReady_ = function handleTechReady_() {
22769    this.triggerReady();
22770
22771    // Keep the same volume as before
22772    if (this.cache_.volume) {
22773      this.techCall_('setVolume', this.cache_.volume);
22774    }
22775
22776    // Look if the tech found a higher resolution poster while loading
22777    this.handleTechPosterChange_();
22778
22779    // Update the duration if available
22780    this.handleTechDurationChange_();
22781
22782    // Chrome and Safari both have issues with autoplay.
22783    // In Safari (5.1.1), when we move the video element into the container div, autoplay doesn't work.
22784    // In Chrome (15), if you have autoplay + a poster + no controls, the video gets hidden (but audio plays)
22785    // This fixes both issues. Need to wait for API, so it updates displays correctly
22786    if ((this.src() || this.currentSrc()) && this.tag && this.options_.autoplay && this.paused()) {
vendor: 5,078 bytes, lines 22787-22967
22787      try {
22788        // Chrome Fix. Fixed in Chrome v16.
22789        delete this.tag.poster;
22790      } catch (e) {
22791        log$1('deleting tag.poster throws in some browsers', e);
22792      }
22793    }
22794  };
22795
22796  /**
22797   * Retrigger the `loadstart` event that was triggered by the {@link Tech}. This
22798   * function will also trigger {@link Player#firstplay} if it is the first loadstart
22799   * for a video.
22800   *
22801   * @fires Player#loadstart
22802   * @fires Player#firstplay
22803   * @listens Tech#loadstart
22804   * @private
22805   */
22806
22807
22808  Player.prototype.handleTechLoadStart_ = function handleTechLoadStart_() {
22809    // TODO: Update to use `emptied` event instead. See #1277.
22810
22811    this.removeClass('vjs-ended');
22812    this.removeClass('vjs-seeking');
22813
22814    // reset the error state
22815    this.error(null);
22816
22817    // If it's already playing we want to trigger a firstplay event now.
22818    // The firstplay event relies on both the play and loadstart events
22819    // which can happen in any order for a new source
22820    if (!this.paused()) {
22821      /**
22822       * Fired when the user agent begins looking for media data
22823       *
22824       * @event Player#loadstart
22825       * @type {EventTarget~Event}
22826       */
22827      this.trigger('loadstart');
22828      this.trigger('firstplay');
22829    } else {
22830      // reset the hasStarted state
22831      this.hasStarted(false);
22832      this.trigger('loadstart');
22833    }
22834
22835    // autoplay happens after loadstart for the browser,
22836    // so we mimic that behavior
22837    this.manualAutoplay_(this.autoplay());
22838  };
22839
22840  /**
22841   * Handle autoplay string values, rather than the typical boolean
22842   * values that should be handled by the tech. Note that this is not
22843   * part of any specification. Valid values and what they do can be
22844   * found on the autoplay getter at Player#autoplay()
22845   */
22846
22847
22848  Player.prototype.manualAutoplay_ = function manualAutoplay_(type) {
22849    var _this4 = this;
22850
22851    if (!this.tech_ || typeof type !== 'string') {
22852      return;
22853    }
22854
22855    var muted = function muted() {
22856      var previouslyMuted = _this4.muted();
22857
22858      _this4.muted(true);
22859
22860      var playPromise = _this4.play();
22861
22862      if (!playPromise || !playPromise.then || !playPromise['catch']) {
22863        return;
22864      }
22865
22866      return playPromise['catch'](function (e) {
22867        // restore old value of muted on failure
22868        _this4.muted(previouslyMuted);
22869      });
22870    };
22871
22872    var promise = void 0;
22873
22874    if (type === 'any') {
22875      promise = this.play();
22876
22877      if (promise && promise.then && promise['catch']) {
22878        promise['catch'](function () {
22879          return muted();
22880        });
22881      }
22882    } else if (type === 'muted') {
22883      promise = muted();
22884    } else {
22885      promise = this.play();
22886    }
22887
22888    if (!promise || !promise.then || !promise['catch']) {
22889      return;
22890    }
22891
22892    return promise.then(function () {
22893      _this4.trigger({ type: 'autoplay-success', autoplay: type });
22894    })['catch'](function (e) {
22895      _this4.trigger({ type: 'autoplay-failure', autoplay: type });
22896    });
22897  };
22898
22899  /**
22900   * Update the internal source caches so that we return the correct source from
22901   * `src()`, `currentSource()`, and `currentSources()`.
22902   *
22903   * > Note: `currentSources` will not be updated if the source that is passed in exists
22904   *         in the current `currentSources` cache.
22905   *
22906   *
22907   * @param {Tech~SourceObject} srcObj
22908   *        A string or object source to update our caches to.
22909   */
22910
22911
22912  Player.prototype.updateSourceCaches_ = function updateSourceCaches_() {
22913    var srcObj = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : '';
22914
22915
22916    var src = srcObj;
22917    var type = '';
22918
22919    if (typeof src !== 'string') {
22920      src = srcObj.src;
22921      type = srcObj.type;
22922    }
22923    // make sure all the caches are set to default values
22924    // to prevent null checking
22925    this.cache_.source = this.cache_.source || {};
22926    this.cache_.sources = this.cache_.sources || [];
22927
22928    // try to get the type of the src that was passed in
22929    if (src && !type) {
22930      type = findMimetype(this, src);
22931    }
22932
22933    // update `currentSource` cache always
22934    this.cache_.source = mergeOptions({}, srcObj, { src: src, type: type });
22935
22936    var matchingSources = this.cache_.sources.filter(function (s) {
22937      return s.src && s.src === src;
22938    });
22939    var sourceElSources = [];
22940    var sourceEls = this.$$('source');
22941    var matchingSourceEls = [];
22942
22943    for (var i = 0; i < sourceEls.length; i++) {
22944      var sourceObj = getAttributes(sourceEls[i]);
22945
22946      sourceElSources.push(sourceObj);
22947
22948      if (sourceObj.src && sourceObj.src === src) {
22949        matchingSourceEls.push(sourceObj.src);
22950      }
22951    }
22952
22953    // if we have matching source els but not matching sources
22954    // the current source cache is not up to date
22955    if (matchingSourceEls.length && !matchingSources.length) {
22956      this.cache_.sources = sourceElSources;
22957      // if we don't have matching source or source els set the
22958      // sources cache to the `currentSource` cache
22959    } else if (!matchingSources.length) {
22960      this.cache_.sources = [this.cache_.source];
22961    }
22962
22963    // update the tech `src` cache
22964    this.cache_.src = src;
22965  };
22966
22967  /**
22968   * *EXPERIMENTAL* Fired when the source is set or changed on the {@link Tech}
22969   * causing the media element to reload.
22970   *
22971   * It will fire for the initial source and each subsequent source.
22972   * This event is a custom event from Video.js and is triggered by the {@link Tech}.
22973   *
22974   * The event object for this event contains a `src` property that will contain the source
22975   * that was available when the event was triggered. This is generally only necessary if Video.js
22976   * is switching techs while the source was being changed.
22977   *
22978   * It is also fired when `load` is called on the player (or media element)
22979   * because the {@link https://html.spec.whatwg.org/multipage/media.html#dom-media-load|specification for `load`}
22980   * says that the resource selection algorithm needs to be aborted and restarted.
22981   * In this case, it is very likely that the `src` property will be set to the
22982   * empty string `""` to indicate we do not know what the source will be but
22983   * that it is changing.
22984   *
22985   * *This event is currently still experimental and may change in minor releases.*
22986   * __To use this, pass `enableSourceset` option to the player.__
22987   *
22988   * @event Player#sourceset
22989   * @type {EventTarget~Event}
22990   * @prop {string} src
22991   *                The source url available when the `sourceset` was triggered.
22992   *                It will be an empty string if we cannot know what the source is
22993   *                but know that the source will change.
22994   */
22995  /**
22996   * Retrigger the `sourceset` event that was triggered by the {@link Tech}.
22997   *
22998   * @fires Player#sourceset
22999   * @listens Tech#sourceset
23000   * @private
23001   */
23002
23003
23004  Player.prototype.handleTechSourceset_ = function handleTechSourceset_(event) {
23005    var _this5 = this;
23006
23007    // only update the source cache when the source
23008    // was not updated using the player api
23009    if (!this.changingSrc_) {
23010      // update the source to the intial source right away
23011      // in some cases this will be empty string
23012      this.updateSourceCaches_(event.src);
23013
23014      // if the `sourceset` `src` was an empty string
23015      // wait for a `loadstart` to update the cache to `currentSrc`.
23016      // If a sourceset happens before a `loadstart`, we reset the state
23017      // as this function will be called again.
23018      if (!event.src) {
23019        var updateCache = function updateCache(e) {
23020          if (e.type !== 'sourceset') {
23021            _this5.updateSourceCaches_(_this5.techGet_('currentSrc'));
23022          }
23023
23024          _this5.tech_.off(['sourceset', 'loadstart'], updateCache);
23025        };
23026
23027        this.tech_.one(['sourceset', 'loadstart'], updateCache);
23028      }
23029    }
23030
23031    this.trigger({
23032      src: event.src,
23033      type: 'sourceset'
23034    });
23035  };
23036
23037  /**
23038   * Add/remove the vjs-has-started class
23039   *
23040   * @fires Player#firstplay
23041   *
23042   * @param {boolean} request
23043   *        - true: adds the class
23044   *        - false: remove the class
23045   *
23046   * @return {boolean}
23047   *         the boolean value of hasStarted_
23048   */
23049
23050
23051  Player.prototype.hasStarted = function hasStarted(request) {
23052    if (request === undefined) {
23053      // act as getter, if we have no request to change
23054      return this.hasStarted_;
23055    }
23056
23057    if (request === this.hasStarted_) {
23058      return;
23059    }
23060
23061    this.hasStarted_ = request;
23062
23063    if (this.hasStarted_) {
23064      this.addClass('vjs-has-started');
23065      this.trigger('firstplay');
23066    } else {
23067      this.removeClass('vjs-has-started');
23068    }
23069  };
23070
23071  /**
23072   * Fired whenever the media begins or resumes playback
23073   *
23074   * @see [Spec]{@link https://html.spec.whatwg.org/multipage/embedded-content.html#dom-media-play}
23075   * @fires Player#play
23076   * @listens Tech#play
23077   * @private
23078   */
23079
23080
23081  Player.prototype.handleTechPlay_ = function handleTechPlay_() {
23082    this.removeClass('vjs-ended');
23083    this.removeClass('vjs-paused');
23084    this.addClass('vjs-playing');
23085
23086    // hide the poster when the user hits play
23087    this.hasStarted(true);
23088    /**
23089     * Triggered whenever an {@link Tech#play} event happens. Indicates that
23090     * playback has started or resumed.
23091     *
23092     * @event Player#play
23093     * @type {EventTarget~Event}
23094     */
23095    this.trigger('play');
23096  };
23097
23098  /**
23099   * Retrigger the `ratechange` event that was triggered by the {@link Tech}.
23100   *
23101   * If there were any events queued while the playback rate was zero, fire
23102   * those events now.
23103   *
23104   * @private
23105   * @method Player#handleTechRateChange_
23106   * @fires Player#ratechange
23107   * @listens Tech#ratechange
23108   */
23109
23110
23111  Player.prototype.handleTechRateChange_ = function handleTechRateChange_() {
23112    if (this.tech_.playbackRate() > 0 && this.cache_.lastPlaybackRate === 0) {
23113      this.queuedCallbacks_.forEach(function (queued) {
23114        return queued.callback(queued.event);
23115      });
23116      this.queuedCallbacks_ = [];
23117    }
23118    this.cache_.lastPlaybackRate = this.tech_.playbackRate();
23119    /**
23120     * Fires when the playing speed of the audio/video is changed
23121     *
23122     * @event Player#ratechange
23123     * @type {event}
23124     */
23125    this.trigger('ratechange');
23126  };
23127
23128  /**
23129   * Retrigger the `waiting` event that was triggered by the {@link Tech}.
23130   *
23131   * @fires Player#waiting
23132   * @listens Tech#waiting
23133   * @private
23134   */
23135
23136
23137  Player.prototype.handleTechWaiting_ = function handleTechWaiting_() {
23138    var _this6 = this;
23139
23140    this.addClass('vjs-waiting');
23141    /**
23142     * A readyState change on the DOM element has caused playback to stop.
23143     *
23144     * @event Player#waiting
23145     * @type {EventTarget~Event}
23146     */
23147    this.trigger('waiting');
23148    this.one('timeupdate', function () {
23149      return _this6.removeClass('vjs-waiting');
23150    });
23151  };
23152
23153  /**
23154   * Retrigger the `canplay` event that was triggered by the {@link Tech}.
23155   * > Note: This is not consistent between browsers. See #1351
23156   *
23157   * @fires Player#canplay
23158   * @listens Tech#canplay
23159   * @private
23160   */
23161
23162
23163  Player.prototype.handleTechCanPlay_ = function handleTechCanPlay_() {
23164    this.removeClass('vjs-waiting');
23165    /**
23166     * The media has a readyState of HAVE_FUTURE_DATA or greater.
23167     *
23168     * @event Player#canplay
23169     * @type {EventTarget~Event}
23170     */
23171    this.trigger('canplay');
23172  };
23173
23174  /**
23175   * Retrigger the `canplaythrough` event that was triggered by the {@link Tech}.
23176   *
23177   * @fires Player#canplaythrough
23178   * @listens Tech#canplaythrough
23179   * @private
23180   */
23181
23182
23183  Player.prototype.handleTechCanPlayThrough_ = function handleTechCanPlayThrough_() {
23184    this.removeClass('vjs-waiting');
23185    /**
23186     * The media has a readyState of HAVE_ENOUGH_DATA or greater. This means that the
23187     * entire media file can be played without buffering.
23188     *
23189     * @event Player#canplaythrough
23190     * @type {EventTarget~Event}
23191     */
23192    this.trigger('canplaythrough');
23193  };
23194
23195  /**
23196   * Retrigger the `playing` event that was triggered by the {@link Tech}.
23197   *
23198   * @fires Player#playing
23199   * @listens Tech#playing
23200   * @private
23201   */
23202
23203
23204  Player.prototype.handleTechPlaying_ = function handleTechPlaying_() {
23205    this.removeClass('vjs-waiting');
23206    /**
23207     * The media is no longer blocked from playback, and has started playing.
23208     *
23209     * @event Player#playing
23210     * @type {EventTarget~Event}
23211     */
23212    this.trigger('playing');
23213  };
23214
23215  /**
23216   * Retrigger the `seeking` event that was triggered by the {@link Tech}.
23217   *
23218   * @fires Player#seeking
23219   * @listens Tech#seeking
23220   * @private
23221   */
23222
23223
23224  Player.prototype.handleTechSeeking_ = function handleTechSeeking_() {
23225    this.addClass('vjs-seeking');
23226    /**
23227     * Fired whenever the player is jumping to a new time
23228     *
23229     * @event Player#seeking
23230     * @type {EventTarget~Event}
23231     */
23232    this.trigger('seeking');
23233  };
23234
23235  /**
23236   * Retrigger the `seeked` event that was triggered by the {@link Tech}.
23237   *
23238   * @fires Player#seeked
23239   * @listens Tech#seeked
23240   * @private
23241   */
23242
23243
23244  Player.prototype.handleTechSeeked_ = function handleTechSeeked_() {
23245    this.removeClass('vjs-seeking');
23246    /**
23247     * Fired when the player has finished jumping to a new time
23248     *
23249     * @event Player#seeked
23250     * @type {EventTarget~Event}
23251     */
23252    this.trigger('seeked');
23253  };
23254
23255  /**
23256   * Retrigger the `firstplay` event that was triggered by the {@link Tech}.
23257   *
23258   * @fires Player#firstplay
23259   * @listens Tech#firstplay
23260   * @deprecated As of 6.0 firstplay event is deprecated.
23261   *             As of 6.0 passing the `starttime` option to the player and the firstplay event are deprecated.
23262   * @private
23263   */
23264
23265
23266  Player.prototype.handleTechFirstPlay_ = function handleTechFirstPlay_() {
23267    // If the first starttime attribute is specified
23268    // then we will start at the given offset in seconds
23269    if (this.options_.starttime) {
23270      log$1.warn('Passing the `starttime` option to the player will be deprecated 
vendor: 17,788 bytes, lines 23270-23970
23270in 6.0');
23271      this.currentTime(this.options_.starttime);
23272    }
23273
23274    this.addClass('vjs-has-started');
23275    /**
23276     * Fired the first time a video is played. Not part of the HLS spec, and this is
23277     * probably not the best implementation yet, so use sparingly. If you don't have a
23278     * reason to prevent playback, use `myPlayer.one('play');` instead.
23279     *
23280     * @event Player#firstplay
23281     * @deprecated As of 6.0 firstplay event is deprecated.
23282     * @type {EventTarget~Event}
23283     */
23284    this.trigger('firstplay');
23285  };
23286
23287  /**
23288   * Retrigger the `pause` event that was triggered by the {@link Tech}.
23289   *
23290   * @fires Player#pause
23291   * @listens Tech#pause
23292   * @private
23293   */
23294
23295
23296  Player.prototype.handleTechPause_ = function handleTechPause_() {
23297    this.removeClass('vjs-playing');
23298    this.addClass('vjs-paused');
23299    /**
23300     * Fired whenever the media has been paused
23301     *
23302     * @event Player#pause
23303     * @type {EventTarget~Event}
23304     */
23305    this.trigger('pause');
23306  };
23307
23308  /**
23309   * Retrigger the `ended` event that was triggered by the {@link Tech}.
23310   *
23311   * @fires Player#ended
23312   * @listens Tech#ended
23313   * @private
23314   */
23315
23316
23317  Player.prototype.handleTechEnded_ = function handleTechEnded_() {
23318    this.addClass('vjs-ended');
23319    if (this.options_.loop) {
23320      this.currentTime(0);
23321      this.play();
23322    } else if (!this.paused()) {
23323      this.pause();
23324    }
23325
23326    /**
23327     * Fired when the end of the media resource is reached (currentTime == duration)
23328     *
23329     * @event Player#ended
23330     * @type {EventTarget~Event}
23331     */
23332    this.trigger('ended');
23333  };
23334
23335  /**
23336   * Fired when the duration of the media resource is first known or changed
23337   *
23338   * @listens Tech#durationchange
23339   * @private
23340   */
23341
23342
23343  Player.prototype.handleTechDurationChange_ = function handleTechDurationChange_() {
23344    this.duration(this.techGet_('duration'));
23345  };
23346
23347  /**
23348   * Handle a click on the media element to play/pause
23349   *
23350   * @param {EventTarget~Event} event
23351   *        the event that caused this function to trigger
23352   *
23353   * @listens Tech#mousedown
23354   * @private
23355   */
23356
23357
23358  Player.prototype.handleTechClick_ = function handleTechClick_(event) {
23359    if (!isSingleLeftClick(event)) {
23360      return;
23361    }
23362
23363    // When controls are disabled a click should not toggle playback because
23364    // the click is considered a control
23365    if (!this.controls_) {
23366      return;
23367    }
23368
23369    if (this.paused()) {
23370      silencePromise(this.play());
23371    } else {
23372      this.pause();
23373    }
23374  };
23375
23376  /**
23377   * Handle a tap on the media element. It will toggle the user
23378   * activity state, which hides and shows the controls.
23379   *
23380   * @listens Tech#tap
23381   * @private
23382   */
23383
23384
23385  Player.prototype.handleTechTap_ = function handleTechTap_() {
23386    this.userActive(!this.userActive());
23387  };
23388
23389  /**
23390   * Handle touch to start
23391   *
23392   * @listens Tech#touchstart
23393   * @private
23394   */
23395
23396
23397  Player.prototype.handleTechTouchStart_ = function handleTechTouchStart_() {
23398    this.userWasActive = this.userActive();
23399  };
23400
23401  /**
23402   * Handle touch to move
23403   *
23404   * @listens Tech#touchmove
23405   * @private
23406   */
23407
23408
23409  Player.prototype.handleTechTouchMove_ = function handleTechTouchMove_() {
23410    if (this.userWasActive) {
23411      this.reportUserActivity();
23412    }
23413  };
23414
23415  /**
23416   * Handle touch to end
23417   *
23418   * @param {EventTarget~Event} event
23419   *        the touchend event that triggered
23420   *        this function
23421   *
23422   * @listens Tech#touchend
23423   * @private
23424   */
23425
23426
23427  Player.prototype.handleTechTouchEnd_ = function handleTechTouchEnd_(event) {
23428    // Stop the mouse events from also happening
23429    event.preventDefault();
23430  };
23431
23432  /**
23433   * Fired when the player switches in or out of fullscreen mode
23434   *
23435   * @private
23436   * @listens Player#fullscreenchange
23437   */
23438
23439
23440  Player.prototype.handleFullscreenChange_ = function handleFullscreenChange_() {
23441    if (this.isFullscreen()) {
23442      this.addClass('vjs-fullscreen');
23443    } else {
23444      this.removeClass('vjs-fullscreen');
23445    }
23446  };
23447
23448  /**
23449   * native click events on the SWF aren't triggered on IE11, Win8.1RT
23450   * use stageclick events triggered from inside the SWF instead
23451   *
23452   * @private
23453   * @listens stageclick
23454   */
23455
23456
23457  Player.prototype.handleStageClick_ = function handleStageClick_() {
23458    this.reportUserActivity();
23459  };
23460
23461  /**
23462   * Handle Tech Fullscreen Change
23463   *
23464   * @param {EventTarget~Event} event
23465   *        the fullscreenchange event that triggered this function
23466   *
23467   * @param {Object} data
23468   *        the data that was sent with the event
23469   *
23470   * @private
23471   * @listens Tech#fullscreenchange
23472   * @fires Player#fullscreenchange
23473   */
23474
23475
23476  Player.prototype.handleTechFullscreenChange_ = function handleTechFullscreenChange_(event, data) {
23477    if (data) {
23478      this.isFullscreen(data.isFullscreen);
23479    }
23480    /**
23481     * Fired when going in and out of fullscreen.
23482     *
23483     * @event Player#fullscreenchange
23484     * @type {EventTarget~Event}
23485     */
23486    this.trigger('fullscreenchange');
23487  };
23488
23489  /**
23490   * Fires when an error occurred during the loading of an audio/video.
23491   *
23492   * @private
23493   * @listens Tech#error
23494   */
23495
23496
23497  Player.prototype.handleTechError_ = function handleTechError_() {
23498    var error = this.tech_.error();
23499
23500    this.error(error);
23501  };
23502
23503  /**
23504   * Retrigger the `textdata` event that was triggered by the {@link Tech}.
23505   *
23506   * @fires Player#textdata
23507   * @listens Tech#textdata
23508   * @private
23509   */
23510
23511
23512  Player.prototype.handleTechTextData_ = function handleTechTextData_() {
23513    var data = null;
23514
23515    if (arguments.length > 1) {
23516      data = arguments[1];
23517    }
23518
23519    /**
23520     * Fires when we get a textdata event from tech
23521     *
23522     * @event Player#textdata
23523     * @type {EventTarget~Event}
23524     */
23525    this.trigger('textdata', data);
23526  };
23527
23528  /**
23529   * Get object for cached values.
23530   *
23531   * @return {Object}
23532   *         get the current object cache
23533   */
23534
23535
23536  Player.prototype.getCache = function getCache() {
23537    return this.cache_;
23538  };
23539
23540  /**
23541   * Pass values to the playback tech
23542   *
23543   * @param {string} [method]
23544   *        the method to call
23545   *
23546   * @param {Object} arg
23547   *        the argument to pass
23548   *
23549   * @private
23550   */
23551
23552
23553  Player.prototype.techCall_ = function techCall_(method, arg) {
23554    // If it's not ready yet, call method when it is
23555
23556    this.ready(function () {
23557      if (method in allowedSetters) {
23558        return set$1(this.middleware_, this.tech_, method, arg);
23559      } else if (method in allowedMediators) {
23560        return mediate(this.middleware_, this.tech_, method, arg);
23561      }
23562
23563      try {
23564        if (this.tech_) {
23565          this.tech_[method](arg);
23566        }
23567      } catch (e) {
23568        log$1(e);
23569        throw e;
23570      }
23571    }, true);
23572  };
23573
23574  /**
23575   * Get calls can't wait for the tech, and sometimes don't need to.
23576   *
23577   * @param {string} method
23578   *        Tech method
23579   *
23580   * @return {Function|undefined}
23581   *         the method or undefined
23582   *
23583   * @private
23584   */
23585
23586
23587  Player.prototype.techGet_ = function techGet_(method) {
23588    if (!this.tech_ || !this.tech_.isReady_) {
23589      return;
23590    }
23591
23592    if (method in allowedGetters) {
23593      return get$1(this.middleware_, this.tech_, method);
23594    } else if (method in allowedMediators) {
23595      return mediate(this.middleware_, this.tech_, method);
23596    }
23597
23598    // Flash likes to die and reload when you hide or reposition it.
23599    // In these cases the object methods go away and we get errors.
23600    // When that happens we'll catch the errors and inform tech that it's not ready any more.
23601    try {
23602      return this.tech_[method]();
23603    } catch (e) {
23604
23605      // When building additional tech libs, an expected method may not be defined yet
23606      if (this.tech_[method] === undefined) {
23607        log$1('Video.js: ' + method + ' method not defined for ' + this.techName_ + ' playback technology.', e);
23608        throw e;
23609      }
23610
23611      // When a method isn't available on the object it throws a TypeError
23612      if (e.name === 'TypeError') {
23613        log$1('Video.js: ' + method + ' unavailable on ' + this.techName_ + ' playback technology element.', e);
23614        this.tech_.isReady_ = false;
23615        throw e;
23616      }
23617
23618      // If error unknown, just log and throw
23619      log$1(e);
23620      throw e;
23621    }
23622  };
23623
23624  /**
23625   * Attempt to begin playback at the first opportunity.
23626   *
23627   * @return {Promise|undefined}
23628   *         Returns a `Promise` only if the browser returns one and the player
23629   *         is ready to begin playback. For some browsers and all non-ready
23630   *         situations, this will return `undefined`.
23631   */
23632
23633
23634  Player.prototype.play = function play() {
23635    var _this7 = this;
23636
23637    // If this is called while we have a play queued up on a loadstart, remove
23638    // that listener to avoid getting in a potentially bad state.
23639    if (this.playOnLoadstart_) {
23640      this.off('loadstart', this.playOnLoadstart_);
23641    }
23642
23643    // If the player/tech is not ready, queue up another call to `play()` for
23644    // when it is. This will loop back into this method for another attempt at
23645    // playback when the tech is ready.
23646    if (!this.isReady_) {
23647
23648      // Bail out if we're already waiting for `ready`!
23649      if (this.playWaitingForReady_) {
23650        return;
23651      }
23652
23653      this.playWaitingForReady_ = true;
23654      this.ready(function () {
23655        _this7.playWaitingForReady_ = false;
23656        silencePromise(_this7.play());
23657      });
23658
23659      // If the player/tech is ready and we have a source, we can attempt playback.
23660    } else if (!this.changingSrc_ && (this.src() || this.currentSrc())) {
23661      return this.techGet_('play');
23662
23663      // If the tech is ready, but we do not have a source, we'll need to wait
23664      // for both the `ready` and a `loadstart` when the source is finally
23665      // resolved by middleware and set on the player.
23666      //
23667      // This can happen if `play()` is called while changing sources or before
23668      // one has been set on the player.
23669    } else {
23670
23671      this.playOnLoadstart_ = function () {
23672        _this7.playOnLoadstart_ = null;
23673        silencePromise(_this7.play());
23674      };
23675
23676      this.one('loadstart', this.playOnLoadstart_);
23677    }
23678  };
23679
23680  /**
23681   * Pause the video playback
23682   *
23683   * @return {Player}
23684   *         A reference to the player object this function was called on
23685   */
23686
23687
23688  Player.prototype.pause = function pause() {
23689    this.techCall_('pause');
23690  };
23691
23692  /**
23693   * Check if the player is paused or has yet to play
23694   *
23695   * @return {boolean}
23696   *         - false: if the media is currently playing
23697   *         - true: if media is not currently playing
23698   */
23699
23700
23701  Player.prototype.paused = function paused() {
23702    // The initial state of paused should be true (in Safari it's actually false)
23703    return this.techGet_('paused') === false ? false : true;
23704  };
23705
23706  /**
23707   * Get a TimeRange object representing the current ranges of time that the user
23708   * has played.
23709   *
23710   * @return {TimeRange}
23711   *         A time range object that represents all the increments of time that have
23712   *         been played.
23713   */
23714
23715
23716  Player.prototype.played = function played() {
23717    return this.techGet_('played') || createTimeRanges(0, 0);
23718  };
23719
23720  /**
23721   * Returns whether or not the user is "scrubbing". Scrubbing is
23722   * when the user has clicked the progress bar handle and is
23723   * dragging it along the progress bar.
23724   *
23725   * @param {boolean} [isScrubbing]
23726   *        wether the user is or is not scrubbing
23727   *
23728   * @return {boolean}
23729   *         The value of scrubbing when getting
23730   */
23731
23732
23733  Player.prototype.scrubbing = function scrubbing(isScrubbing) {
23734    if (typeof isScrubbing === 'undefined') {
23735      return this.scrubbing_;
23736    }
23737    this.scrubbing_ = !!isScrubbing;
23738
23739    if (isScrubbing) {
23740      this.addClass('vjs-scrubbing');
23741    } else {
23742      this.removeClass('vjs-scrubbing');
23743    }
23744  };
23745
23746  /**
23747   * Get or set the current time (in seconds)
23748   *
23749   * @param {number|string} [seconds]
23750   *        The time to seek to in seconds
23751   *
23752   * @return {number}
23753   *         - the current time in seconds when getting
23754   */
23755
23756
23757  Player.prototype.currentTime = function currentTime(seconds) {
23758    if (typeof seconds !== 'undefined') {
23759      if (seconds < 0) {
23760        seconds = 0;
23761      }
23762      this.techCall_('setCurrentTime', seconds);
23763      return;
23764    }
23765
23766    // cache last currentTime and return. default to 0 seconds
23767    //
23768    // Caching the currentTime is meant to prevent a massive amount of reads on the tech's
23769    // currentTime when scrubbing, but may not provide much performance benefit afterall.
23770    // Should be tested. Also something has to read the actual current time or the cache will
23771    // never get updated.
23772    this.cache_.currentTime = this.techGet_('currentTime') || 0;
23773    return this.cache_.currentTime;
23774  };
23775
23776  /**
23777   * Normally gets the length in time of the video in seconds;
23778   * in all but the rarest use cases an argument will NOT be passed to the method
23779   *
23780   * > **NOTE**: The video must have started loading before the duration can be
23781   * known, and in the case of Flash, may not be known until the video starts
23782   * playing.
23783   *
23784   * @fires Player#durationchange
23785   *
23786   * @param {number} [seconds]
23787   *        The duration of the video to set in seconds
23788   *
23789   * @return {number}
23790   *         - The duration of the video in seconds when getting
23791   */
23792
23793
23794  Player.prototype.duration = function duration(seconds) {
23795    if (seconds === undefined) {
23796      // return NaN if the duration is not known
23797      return this.cache_.duration !== undefined ? this.cache_.duration : NaN;
23798    }
23799
23800    seconds = parseFloat(seconds);
23801
23802    // Standardize on Inifity for signaling video is live
23803    if (seconds < 0) {
23804      seconds = Infinity;
23805    }
23806
23807    if (seconds !== this.cache_.duration) {
23808      // Cache the last set value for optimized scrubbing (esp. Flash)
23809      this.cache_.duration = seconds;
23810
23811      if (seconds === Infinity) {
23812        this.addClass('vjs-live');
23813      } else {
23814        this.removeClass('vjs-live');
23815      }
23816      /**
23817       * @event Player#durationchange
23818       * @type {EventTarget~Event}
23819       */
23820      this.trigger('durationchange');
23821    }
23822  };
23823
23824  /**
23825   * Calculates how much time is left in the video. Not part
23826   * of the native video API.
23827   *
23828   * @return {number}
23829   *         The time remaining in seconds
23830   */
23831
23832
23833  Player.prototype.remainingTime = function remainingTime() {
23834    return this.duration() - this.currentTime();
23835  };
23836
23837  /**
23838   * A remaining time function that is intented to be used when
23839   * the time is to be displayed directly to the user.
23840   *
23841   * @return {number}
23842   *         The rounded time remaining in seconds
23843   */
23844
23845
23846  Player.prototype.remainingTimeDisplay = function remainingTimeDisplay() {
23847    return Math.floor(this.duration()) - Math.floor(this.currentTime());
23848  };
23849
23850  //
23851  // Kind of like an array of portions of the video that have been downloaded.
23852
23853  /**
23854   * Get a TimeRange object with an array of the times of the video
23855   * that have been downloaded. If you just want the percent of the
23856   * video that's been downloaded, use bufferedPercent.
23857   *
23858   * @see [Buffered Spec]{@link http://dev.w3.org/html5/spec/video.html#dom-media-buffered}
23859   *
23860   * @return {TimeRange}
23861   *         A mock TimeRange object (following HTML spec)
23862   */
23863
23864
23865  Player.prototype.buffered = function buffered() {
23866    var buffered = this.techGet_('buffered');
23867
23868    if (!buffered || !buffered.length) {
23869      buffered = createTimeRanges(0, 0);
23870    }
23871
23872    return buffered;
23873  };
23874
23875  /**
23876   * Get the percent (as a decimal) of the video that's been downloaded.
23877   * This method is not a part of the native HTML video API.
23878   *
23879   * @return {number}
23880   *         A decimal between 0 and 1 representing the percent
23881   *         that is bufferred 0 being 0% and 1 being 100%
23882   */
23883
23884
23885  Player.prototype.bufferedPercent = function bufferedPercent$$1() {
23886    return bufferedPercent(this.buffered(), this.duration());
23887  };
23888
23889  /**
23890   * Get the ending time of the last buffered time range
23891   * This is used in the progress bar to encapsulate all time ranges.
23892   *
23893   * @return {number}
23894   *         The end of the last buffered time range
23895   */
23896
23897
23898  Player.prototype.bufferedEnd = function bufferedEnd() {
23899    var buffered = this.buffered();
23900    var duration = this.duration();
23901    var end = buffered.end(buffered.length - 1);
23902
23903    if (end > duration) {
23904      end = duration;
23905    }
23906
23907    return end;
23908  };
23909
23910  /**
23911   * Get or set the current volume of the media
23912   *
23913   * @param  {number} [percentAsDecimal]
23914   *         The new volume as a decimal percent:
23915   *         - 0 is muted/0%/off
23916   *         - 1.0 is 100%/full
23917   *         - 0.5 is half volume or 50%
23918   *
23919   * @return {number}
23920   *         The current volume as a percent when getting
23921   */
23922
23923
23924  Player.prototype.volume = function volume(percentAsDecimal) {
23925    var vol = void 0;
23926
23927    if (percentAsDecimal !== undefined) {
23928      // Force value to between 0 and 1
23929      vol = Math.max(0, Math.min(1, parseFloat(percentAsDecimal)));
23930      this.cache_.volume = vol;
23931      this.techCall_('setVolume', vol);
23932
23933      if (vol > 0) {
23934        this.lastVolume_(vol);
23935      }
23936
23937      return;
23938    }
23939
23940    // Default to 1 when returning current volume.
23941    vol = parseFloat(this.techGet_('volume'));
23942    return isNaN(vol) ? 1 : vol;
23943  };
23944
23945  /**
23946   * Get the current muted state, or turn mute on or off
23947   *
23948   * @param {boolean} [muted]
23949   *        - true to mute
23950   *        - false to unmute
23951   *
23952   * @return {boolean}
23953   *         - true if mute is on and getting
23954   *         - false if mute is off and getting
23955   */
23956
23957
23958  Player.prototype.muted = function muted(_muted) {
23959    if (_muted !== undefined) {
23960      this.techCall_('setMuted', _muted);
23961      return;
23962    }
23963    return this.techGet_('muted') || false;
23964  };
23965
23966  /**
23967   * Get the current defaultMuted state, or turn defaultMuted on or off. defaultMuted
23968   * indicates the state of muted on intial playback.
23969   *
23970   * ```js
23971   *   var myPlayer = videojs('some-player-id');
23972   *
23973   *   myPlayer.src("http://www.example.com/path/to/video.mp4");
23974   *
23975   *   // get, should be false
23976   *   console.log(myPlayer.defaultMuted());
23977   *   // set to true
23978   *   myPlayer.defaultMuted(true);
23979   *   // get should be true
23980   *   console.log(myPlayer.defaultMuted());
23981   * ```
23982   *
23983   * @param {boolean} [defaultMuted]
23984   *        - true to mute
23985   *        - false to unmute
23986   *
23987   * @return {boolean|Player}
23988   *         - true if defaultMuted is on and getting
23989   *         - false if defaultMuted is off and getting
23990   *         - A reference to the current player when setting
23991   */
23992
23993
23994  Player.prototype.defaultMuted = function defaultMuted(_defaultMuted) {
23995    if (_defaultMuted !== undefined) {
23996      return this.techCall_('setDefaultMuted', _defaultMuted);
23997    }
23998    return this.techGet_('defaultMuted') || false;
23999  };
24000
24001  /**
24002   * Get the last volume, or set it
24003   *
24004   * @param  {number} [percentAsDecimal]
24005   *         The new last volume as a decimal percent:
24006   *         - 0 is muted/0%/off
24007   *         - 1.0 is 100%/full
24008   *         - 0.5 is half volume or 50%
24009   *
24010   * @return {number}
24011   *         the current value of lastVolume as a percent when getting
24012   *
24013   * @private
24014   */
24015
24016
24017  Player.prototype.lastVolume_ = function lastVolume_(percentAsDecimal) {
24018    if (percentAsDecimal !== undefined && percentAsDecimal !== 0) {
24019      this.cache_.lastVolume = percentAsDecimal;
24020      return;
24021    }
24022    return this.cache_.lastVolume;
24023  };
24024
24025  /**
24026   * Check if current tech can support native fullscreen
24027   * (e.g. with built in controls like iOS, so not our flash swf)
24028   *
24029   * @return {boolean}
24030   *         if native fullscreen is supported
24031   */
24032
24033
24034  Player.prototype.supportsFullScreen = function supportsFullScreen() {
24035    return this.techGet_('supportsFullScreen') || false;
24036  };
24037
24038  /**
24039   * Check if the player is in fullscreen mode or tell the player that it
24040   * is or is not in fullscreen mode.
24041   *
24042   * > NOTE: As of the latest HTML5 spec, isFullscreen is no longer an official
24043   * property and instead document.fullscreenElement is used. But isFullscreen is
24044   * still a valuable property for internal player workings.
24045   *
24046   * @param  {boolean} [isFS]
24047   *         Set the players current fullscreen state
24048   *
24049   * @return {boolean}
24050   *         - true if fullscreen is on and getting
24051   *         - false if fullscreen is off and getting
24052   */
24053
24054
24055  Player.prototype.isFullscreen = function isFullscreen(isFS) {
24056    if (isFS !== undefined) {
24057      this.isFullscreen_ = !!isFS;
24058      return;
24059    }
24060    return !!this.isFullscreen_;
24061  };
24062
24063  /**
24064   * Increase the size of the video to full screen
24065   * In some browsers, full screen is not supported natively, so it enters
24066   * "full window mode", where the video fills the browser window.
24067   * In browsers and devices that support native full screen, sometimes the
24068   * browser's default controls will be shown, and not the Video.js custom skin.
24069   * This includes most mobile devices (iOS, Android) and older versions of
24070   * Safari.
24071   *
24072   * @fires Player#fullscreenchange
24073   */
24074
24075
24076  Player.prototype.requestFullscreen = function requestFullscreen() {
24077    var fsApi = FullscreenApi;
24078
24079    this.isFullscreen(true);
24080
24081    if (fsApi.requestFullscreen) {
24082      // the browser supports going fullscreen at the element level so we can
24083      // take the controls fullscreen as well as the video
24084
24085      // Trigger fullscreenchange event after change
24086      // We have to specifically add this each time, and remove
24087      // when canceling fullscreen. Otherwise if there's multiple
24088      // players on a page, they would all be reacting to the same fullscreen
24089      // events
24090      on(document_1, fsApi.fullscreenchange, bind(this, function documentFullscreenChange(e) {
24091        this.isFullscreen(document_1[fsApi.fullscreenElement]);
24092
24093        // If cancelling fullscreen, remove event listener.
24094        if (this.isFullscreen() === false) {
24095          off(document_1, fsApi.fullscreenchange, documentFullscreenChange);
24096        }
24097        /**
24098         * @event Player#fullscreenchange
24099         * @type {EventTarget~Event}
24100         */
24101        this.trigger('fullscreenchange');
24102      }));
24103
24104      this.el_[fsApi.requestFullscreen]();
24105    } else if (this.tech_.supportsFullScreen()) {
24106      // we can't take the video.js controls fullscreen but we can go fullscreen
24107      // with native controls
24108      this.techCall_('enterFullScreen');
24109    } else {
24110      // fullscreen isn't supported so we'll just stretch the video element to
24111      // fill the viewport
24112      this.enterFullWindow();
24113      /**
24114       * @event Player#fullscreenchange
24115       * @type {EventTarget~Event}
24116       */
24117      this.trigger('fullscreenchange');
24118    }
24119  };
24120
24121  /**
24122   * Return the video to its normal size after having been in full screen mode
24123   *
24124   * @fires Player#fullscreenchange
24125   */
24126
24127
24128  Player.prototype.exitFullscreen = function exitFullscreen() {
24129    var fsApi = FullscreenApi;
24130
24131    this.isFullscreen(false);
24132
24133    // Check for browser element fullscreen support
24134    if (fsApi.requestFullscreen) {
24135      document_1[fsApi.exitFullscreen]();
24136    } else if (this.tech_.supportsFullScreen()) {
24137      this.techCall_('exitFullScreen');
24138    } else {
24139      this.exitFullWindow();
24140      /**
24141       * @event Player#fullscreenchange
24142       * @type {EventTarget~Event}
24143       */
24144      this.trigger('fullscreenchange');
24145    }
24146  };
24147
24148  /**
24149   * When fullscreen isn't supported we can stretch the
24150   * video container to as wide as the browser will let us.
24151   *
24152   * @fires Player#enterFullWindow
24153   */
24154
24155
24156  Player.prototype.enterFullWindow = function enterFullWindow() {
24157    this.isFullWindow = true;
24158
24159    // Storing original doc overflow value to return to when fullscreen is off
24160    this.docOrigOverflow = document_1.documentElement.style.overflow;
24161
24162    // Add listener for esc key to exit fullscreen
24163    on(document_1, 'keydown', bind(this, this.fullWindowOnEscKey));
24164
24165    // Hide any scroll bars
24166    document_1.documentElement.style.overflow = 'hidden';
24167
24168    // Apply fullscreen styles
24169    addClass(document_1.body, 'vjs-full-window');
24170
24171    /**
24172     * @event Player#enterFullWindow
24173     * @type {EventTarget~Event}
24174     */
24175    this.trigger('enterFullWindow');
24176  };
24177
24178  /**
24179   * Check for call to either exit full window or
24180   * full screen on ESC key
24181   *
24182   * @param {string} event
24183   *        Event to check for key press
24184   */
24185
24186
24187  Player.prototype.fullWindowOnEscKey = function fullWindowOnEscKey(event) {
24188    if (event.keyCode === 27) {
24189      if (this.isFullscreen() === true) {
24190        this.exitFullscreen();
24191      } else {
24192        this.exitFullWindow();
24193      }
24194    }
24195  };
24196
24197  /**
24198   * Exit full window
24199   *
24200   * @fires Player#exitFullWindow
24201   */
24202
24203
24204  Player.prototype.exitFullWindow = function exitFullWindow() {
24205    this.isFullWindow = false;
vendor: 10,398 bytes, lines 24206-24569
24206    off(document_1, 'keydown', this.fullWindowOnEscKey);
24207
24208    // Unhide scroll bars.
24209    document_1.documentElement.style.overflow = this.docOrigOverflow;
24210
24211    // Remove fullscreen styles
24212    removeClass(document_1.body, 'vjs-full-window');
24213
24214    // Resize the box, controller, and poster to original sizes
24215    // this.positionAll();
24216    /**
24217     * @event Player#exitFullWindow
24218     * @type {EventTarget~Event}
24219     */
24220    this.trigger('exitFullWindow');
24221  };
24222
24223  /**
24224   * Check whether the player can play a given mimetype
24225   *
24226   * @see https://www.w3.org/TR/2011/WD-html5-20110113/video.html#dom-navigator-canplaytype
24227   *
24228   * @param {string} type
24229   *        The mimetype to check
24230   *
24231   * @return {string}
24232   *         'probably', 'maybe', or '' (empty string)
24233   */
24234
24235
24236  Player.prototype.canPlayType = function canPlayType(type) {
24237    var can = void 0;
24238
24239    // Loop through each playback technology in the options order
24240    for (var i = 0, j = this.options_.techOrder; i < j.length; i++) {
24241      var techName = j[i];
24242      var tech = Tech.getTech(techName);
24243
24244      // Support old behavior of techs being registered as components.
24245      // Remove once that deprecated behavior is removed.
24246      if (!tech) {
24247        tech = Component.getComponent(techName);
24248      }
24249
24250      // Check if the current tech is defined before continuing
24251      if (!tech) {
24252        log$1.error('The "' + techName + '" tech is undefined. Skipped browser support check for that tech.');
24253        continue;
24254      }
24255
24256      // Check if the browser supports this technology
24257      if (tech.isSupported()) {
24258        can = tech.canPlayType(type);
24259
24260        if (can) {
24261          return can;
24262        }
24263      }
24264    }
24265
24266    return '';
24267  };
24268
24269  /**
24270   * Select source based on tech-order or source-order
24271   * Uses source-order selection if `options.sourceOrder` is truthy. Otherwise,
24272   * defaults to tech-order selection
24273   *
24274   * @param {Array} sources
24275   *        The sources for a media asset
24276   *
24277   * @return {Object|boolean}
24278   *         Object of source and tech order or false
24279   */
24280
24281
24282  Player.prototype.selectSource = function selectSource(sources) {
24283    var _this8 = this;
24284
24285    // Get only the techs specified in `techOrder` that exist and are supported by the
24286    // current platform
24287    var techs = this.options_.techOrder.map(function (techName) {
24288      return [techName, Tech.getTech(techName)];
24289    }).filter(function (_ref) {
24290      var techName = _ref[0],
24291          tech = _ref[1];
24292
24293      // Check if the current tech is defined before continuing
24294      if (tech) {
24295        // Check if the browser supports this technology
24296        return tech.isSupported();
24297      }
24298
24299      log$1.error('The "' + techName + '" tech is undefined. Skipped browser support check for that tech.');
24300      return false;
24301    });
24302
24303    // Iterate over each `innerArray` element once per `outerArray` element and execute
24304    // `tester` with both. If `tester` returns a non-falsy value, exit early and return
24305    // that value.
24306    var findFirstPassingTechSourcePair = function findFirstPassingTechSourcePair(outerArray, innerArray, tester) {
24307      var found = void 0;
24308
24309      outerArray.some(function (outerChoice) {
24310        return innerArray.some(function (innerChoice) {
24311          found = tester(outerChoice, innerChoice);
24312
24313          if (found) {
24314            return true;
24315          }
24316        });
24317      });
24318
24319      return found;
24320    };
24321
24322    var foundSourceAndTech = void 0;
24323    var flip = function flip(fn) {
24324      return function (a, b) {
24325        return fn(b, a);
24326      };
24327    };
24328    var finder = function finder(_ref2, source) {
24329      var techName = _ref2[0],
24330          tech = _ref2[1];
24331
24332      if (tech.canPlaySource(source, _this8.options_[techName.toLowerCase()])) {
24333        return { source: source, tech: techName };
24334      }
24335    };
24336
24337    // Depending on the truthiness of `options.sourceOrder`, we swap the order of techs and sources
24338    // to select from them based on their priority.
24339    if (this.options_.sourceOrder) {
24340      // Source-first ordering
24341      foundSourceAndTech = findFirstPassingTechSourcePair(sources, techs, flip(finder));
24342    } else {
24343      // Tech-first ordering
24344      foundSourceAndTech = findFirstPassingTechSourcePair(techs, sources, finder);
24345    }
24346
24347    return foundSourceAndTech || false;
24348  };
24349
24350  /**
24351   * Get or set the video source.
24352   *
24353   * @param {Tech~SourceObject|Tech~SourceObject[]|string} [source]
24354   *        A SourceObject, an array of SourceObjects, or a string referencing
24355   *        a URL to a media source. It is _highly recommended_ that an object
24356   *        or array of objects is used here, so that source selection
24357   *        algorithms can take the `type` into account.
24358   *
24359   *        If not provided, this method acts as a getter.
24360   *
24361   * @return {string|undefined}
24362   *         If the `source` argument is missing, returns the current source
24363   *         URL. Otherwise, returns nothing/undefined.
24364   */
24365
24366
24367  Player.prototype.src = function src(source) {
24368    var _this9 = this;
24369
24370    // getter usage
24371    if (typeof source === 'undefined') {
24372      return this.cache_.src || '';
24373    }
24374    // filter out invalid sources and turn our source into
24375    // an array of source objects
24376    var sources = filterSource(source);
24377
24378    // if a source was passed in then it is invalid because
24379    // it was filtered to a zero length Array. So we have to
24380    // show an error
24381    if (!sources.length) {
24382      this.setTimeout(function () {
24383        this.error({ code: 4, message: this.localize(this.options_.notSupportedMessage) });
24384      }, 0);
24385      return;
24386    }
24387
24388    // intial sources
24389    this.changingSrc_ = true;
24390
24391    this.cache_.sources = sources;
24392    this.updateSourceCaches_(sources[0]);
24393
24394    // middlewareSource is the source after it has been changed by middleware
24395    setSource(this, sources[0], function (middlewareSource, mws) {
24396      _this9.middleware_ = mws;
24397
24398      // since sourceSet is async we have to update the cache again after we select a source since
24399      // the source that is selected could be out of order from the cache update above this callback.
24400      _this9.cache_.sources = sources;
24401      _this9.updateSourceCaches_(middlewareSource);
24402
24403      var err = _this9.src_(middlewareSource);
24404
24405      if (err) {
24406        if (sources.length > 1) {
24407          return _this9.src(sources.slice(1));
24408        }
24409
24410        _this9.changingSrc_ = false;
24411
24412        // We need to wrap this in a timeout to give folks a chance to add error event handlers
24413        _this9.setTimeout(function () {
24414          this.error({ code: 4, message: this.localize(this.options_.notSupportedMessage) });
24415        }, 0);
24416
24417        // we could not find an appropriate tech, but let's still notify the delegate that this is it
24418        // this needs a better comment about why this is needed
24419        _this9.triggerReady();
24420
24421        return;
24422      }
24423
24424      setTech(mws, _this9.tech_);
24425    });
24426  };
24427
24428  /**
24429   * Set the source object on the tech, returns a boolean that indicates wether
24430   * there is a tech that can play the source or not
24431   *
24432   * @param {Tech~SourceObject} source
24433   *        The source object to set on the Tech
24434   *
24435   * @return {Boolean}
24436   *         - True if there is no Tech to playback this source
24437   *         - False otherwise
24438   *
24439   * @private
24440   */
24441
24442
24443  Player.prototype.src_ = function src_(source) {
24444    var _this10 = this;
24445
24446    var sourceTech = this.selectSource([source]);
24447
24448    if (!sourceTech) {
24449      return true;
24450    }
24451
24452    if (!titleCaseEquals(sourceTech.tech, this.techName_)) {
24453      this.changingSrc_ = true;
24454      // load this technology with the chosen source
24455      this.loadTech_(sourceTech.tech, sourceTech.source);
24456      this.tech_.ready(function () {
24457        _this10.changingSrc_ = false;
24458      });
24459      return false;
24460    }
24461
24462    // wait until the tech is ready to set the source
24463    // and set it synchronously if possible (#2326)
24464    this.ready(function () {
24465
24466      // The setSource tech method was added with source handlers
24467      // so older techs won't support it
24468      // We need to check the direct prototype for the case where subclasses
24469      // of the tech do not support source handlers
24470      if (this.tech_.constructor.prototype.hasOwnProperty('setSource')) {
24471        this.techCall_('setSource', source);
24472      } else {
24473        this.techCall_('src', source.src);
24474      }
24475
24476      this.changingSrc_ = false;
24477    }, true);
24478
24479    return false;
24480  };
24481
24482  /**
24483   * Begin loading the src data.
24484   */
24485
24486
24487  Player.prototype.load = function load() {
24488    this.techCall_('load');
24489  };
24490
24491  /**
24492   * Reset the player. Loads the first tech in the techOrder,
24493   * and calls `reset` on the tech`.
24494   */
24495
24496
24497  Player.prototype.reset = function reset() {
24498    if (this.tech_) {
24499      this.tech_.clearTracks('text');
24500    }
24501    this.loadTech_(this.options_.techOrder[0], null);
24502    this.techCall_('reset');
24503  };
24504
24505  /**
24506   * Returns all of the current source objects.
24507   *
24508   * @return {Tech~SourceObject[]}
24509   *         The current source objects
24510   */
24511
24512
24513  Player.prototype.currentSources = function currentSources() {
24514    var source = this.currentSource();
24515    var sources = [];
24516
24517    // assume `{}` or `{ src }`
24518    if (Object.keys(source).length !== 0) {
24519      sources.push(source);
24520    }
24521
24522    return this.cache_.sources || sources;
24523  };
24524
24525  /**
24526   * Returns the current source object.
24527   *
24528   * @return {Tech~SourceObject}
24529   *         The current source object
24530   */
24531
24532
24533  Player.prototype.currentSource = function currentSource() {
24534    return this.cache_.source || {};
24535  };
24536
24537  /**
24538   * Returns the fully qualified URL of the current source value e.g. http://mysite.com/video.mp4
24539   * Can be used in conjuction with `currentType` to assist in rebuilding the current source object.
24540   *
24541   * @return {string}
24542   *         The current source
24543   */
24544
24545
24546  Player.prototype.currentSrc = function currentSrc() {
24547    return this.currentSource() && this.currentSource().src || '';
24548  };
24549
24550  /**
24551   * Get the current source type e.g. video/mp4
24552   * This can allow you rebuild the current source object so that you could load the same
24553   * source and tech later
24554   *
24555   * @return {string}
24556   *         The source MIME type
24557   */
24558
24559
24560  Player.prototype.currentType = function currentType() {
24561    return this.currentSource() && this.currentSource().type || '';
24562  };
24563
24564  /**
24565   * Get or set the preload attribute
24566   *
24567   * @param {boolean} [value]
24568   *        - true means that we should preload
24569   *        - false mae
vendor: 9,205 bytes, lines 24569-24908
24569ns that we should not preload
24570   *
24571   * @return {string}
24572   *         The preload attribute value when getting
24573   */
24574
24575
24576  Player.prototype.preload = function preload(value) {
24577    if (value !== undefined) {
24578      this.techCall_('setPreload', value);
24579      this.options_.preload = value;
24580      return;
24581    }
24582    return this.techGet_('preload');
24583  };
24584
24585  /**
24586   * Get or set the autoplay option. When this is a boolean it will
24587   * modify the attribute on the tech. When this is a string the attribute on
24588   * the tech will be removed and `Player` will handle autoplay on loadstarts.
24589   *
24590   * @param {boolean|string} [value]
24591   *        - true: autoplay using the browser behavior
24592   *        - false: do not autoplay
24593   *        - 'play': call play() on every loadstart
24594   *        - 'muted': call muted() then play() on every loadstart
24595   *        - 'any': call play() on every loadstart. if that fails call muted() then play().
24596   *        - *: values other than those listed here will be set `autoplay` to true
24597   *
24598   * @return {boolean|string}
24599   *         The current value of autoplay when getting
24600   */
24601
24602
24603  Player.prototype.autoplay = function autoplay(value) {
24604    // getter usage
24605    if (value === undefined) {
24606      return this.options_.autoplay || false;
24607    }
24608
24609    var techAutoplay = void 0;
24610
24611    // if the value is a valid string set it to that
24612    if (typeof value === 'string' && /(any|play|muted)/.test(value)) {
24613      this.options_.autoplay = value;
24614      this.manualAutoplay_(value);
24615      techAutoplay = false;
24616
24617      // any falsy value sets autoplay to false in the browser,
24618      // lets do the same
24619    } else if (!value) {
24620      this.options_.autoplay = false;
24621
24622      // any other value (ie truthy) sets autoplay to true
24623    } else {
24624      this.options_.autoplay = true;
24625    }
24626
24627    techAutoplay = techAutoplay || this.options_.autoplay;
24628
24629    // if we don't have a tech then we do not queue up
24630    // a setAutoplay call on tech ready. We do this because the
24631    // autoplay option will be passed in the constructor and we
24632    // do not need to set it twice
24633    if (this.tech_) {
24634      this.techCall_('setAutoplay', techAutoplay);
24635    }
24636  };
24637
24638  /**
24639   * Set or unset the playsinline attribute.
24640   * Playsinline tells the browser that non-fullscreen playback is preferred.
24641   *
24642   * @param {boolean} [value]
24643   *        - true means that we should try to play inline by default
24644   *        - false means that we should use the browser's default playback mode,
24645   *          which in most cases is inline. iOS Safari is a notable exception
24646   *          and plays fullscreen by default.
24647   *
24648   * @return {string|Player}
24649   *         - the current value of playsinline
24650   *         - the player when setting
24651   *
24652   * @see [Spec]{@link https://html.spec.whatwg.org/#attr-video-playsinline}
24653   */
24654
24655
24656  Player.prototype.playsinline = function playsinline(value) {
24657    if (value !== undefined) {
24658      this.techCall_('setPlaysinline', value);
24659      this.options_.playsinline = value;
24660      return this;
24661    }
24662    return this.techGet_('playsinline');
24663  };
24664
24665  /**
24666   * Get or set the loop attribute on the video element.
24667   *
24668   * @param {boolean} [value]
24669   *        - true means that we should loop the video
24670   *        - false means that we should not loop the video
24671   *
24672   * @return {string}
24673   *         The current value of loop when getting
24674   */
24675
24676
24677  Player.prototype.loop = function loop(value) {
24678    if (value !== undefined) {
24679      this.techCall_('setLoop', value);
24680      this.options_.loop = value;
24681      return;
24682    }
24683    return this.techGet_('loop');
24684  };
24685
24686  /**
24687   * Get or set the poster image source url
24688   *
24689   * @fires Player#posterchange
24690   *
24691   * @param {string} [src]
24692   *        Poster image source URL
24693   *
24694   * @return {string}
24695   *         The current value of poster when getting
24696   */
24697
24698
24699  Player.prototype.poster = function poster(src) {
24700    if (src === undefined) {
24701      return this.poster_;
24702    }
24703
24704    // The correct way to remove a poster is to set as an empty string
24705    // other falsey values will throw errors
24706    if (!src) {
24707      src = '';
24708    }
24709
24710    if (src === this.poster_) {
24711      return;
24712    }
24713
24714    // update the internal poster variable
24715    this.poster_ = src;
24716
24717    // update the tech's poster
24718    this.techCall_('setPoster', src);
24719
24720    this.isPosterFromTech_ = false;
24721
24722    // alert components that the poster has been set
24723    /**
24724     * This event fires when the poster image is changed on the player.
24725     *
24726     * @event Player#posterchange
24727     * @type {EventTarget~Event}
24728     */
24729    this.trigger('posterchange');
24730  };
24731
24732  /**
24733   * Some techs (e.g. YouTube) can provide a poster source in an
24734   * asynchronous way. We want the poster component to use this
24735   * poster source so that it covers up the tech's controls.
24736   * (YouTube's play button). However we only want to use this
24737   * source if the player user hasn't set a poster through
24738   * the normal APIs.
24739   *
24740   * @fires Player#posterchange
24741   * @listens Tech#posterchange
24742   * @private
24743   */
24744
24745
24746  Player.prototype.handleTechPosterChange_ = function handleTechPosterChange_() {
24747    if ((!this.poster_ || this.options_.techCanOverridePoster) && this.tech_ && this.tech_.poster) {
24748      var newPoster = this.tech_.poster() || '';
24749
24750      if (newPoster !== this.poster_) {
24751        this.poster_ = newPoster;
24752        this.isPosterFromTech_ = true;
24753
24754        // Let components know the poster has changed
24755        this.trigger('posterchange');
24756      }
24757    }
24758  };
24759
24760  /**
24761   * Get or set whether or not the controls are showing.
24762   *
24763   * @fires Player#controlsenabled
24764   *
24765   * @param {boolean} [bool]
24766   *        - true to turn controls on
24767   *        - false to turn controls off
24768   *
24769   * @return {boolean}
24770   *         The current value of controls when getting
24771   */
24772
24773
24774  Player.prototype.controls = function controls(bool) {
24775    if (bool === undefined) {
24776      return !!this.controls_;
24777    }
24778
24779    bool = !!bool;
24780
24781    // Don't trigger a change event unless it actually changed
24782    if (this.controls_ === bool) {
24783      return;
24784    }
24785
24786    this.controls_ = bool;
24787
24788    if (this.usingNativeControls()) {
24789      this.techCall_('setControls', bool);
24790    }
24791
24792    if (this.controls_) {
24793      this.removeClass('vjs-controls-disabled');
24794      this.addClass('vjs-controls-enabled');
24795      /**
24796       * @event Player#controlsenabled
24797       * @type {EventTarget~Event}
24798       */
24799      this.trigger('controlsenabled');
24800      if (!this.usingNativeControls()) {
24801        this.addTechControlsListeners_();
24802      }
24803    } else {
24804      this.removeClass('vjs-controls-enabled');
24805      this.addClass('vjs-controls-disabled');
24806      /**
24807       * @event Player#controlsdisabled
24808       * @type {EventTarget~Event}
24809       */
24810      this.trigger('controlsdisabled');
24811      if (!this.usingNativeControls()) {
24812        this.removeTechControlsListeners_();
24813      }
24814    }
24815  };
24816
24817  /**
24818   * Toggle native controls on/off. Native controls are the controls built into
24819   * devices (e.g. default iPhone controls), Flash, or other techs
24820   * (e.g. Vimeo Controls)
24821   * **This should only be set by the current tech, because only the tech knows
24822   * if it can support native controls**
24823   *
24824   * @fires Player#usingnativecontrols
24825   * @fires Player#usingcustomcontrols
24826   *
24827   * @param {boolean} [bool]
24828   *        - true to turn native controls on
24829   *        - false to turn native controls off
24830   *
24831   * @return {boolean}
24832   *         The current value of native controls when getting
24833   */
24834
24835
24836  Player.prototype.usingNativeControls = function usingNativeControls(bool) {
24837    if (bool === undefined) {
24838      return !!this.usingNativeControls_;
24839    }
24840
24841    bool = !!bool;
24842
24843    // Don't trigger a change event unless it actually changed
24844    if (this.usingNativeControls_ === bool) {
24845      return;
24846    }
24847
24848    this.usingNativeControls_ = bool;
24849
24850    if (this.usingNativeControls_) {
24851      this.addClass('vjs-using-native-controls');
24852
24853      /**
24854       * player is using the native device controls
24855       *
24856       * @event Player#usingnativecontrols
24857       * @type {EventTarget~Event}
24858       */
24859      this.trigger('usingnativecontrols');
24860    } else {
24861      this.removeClass('vjs-using-native-controls');
24862
24863      /**
24864       * player is using the custom HTML controls
24865       *
24866       * @event Player#usingcustomcontrols
24867       * @type {EventTarget~Event}
24868       */
24869      this.trigger('usingcustomcontrols');
24870    }
24871  };
24872
24873  /**
24874   * Set or get the current MediaError
24875   *
24876   * @fires Player#error
24877   *
24878   * @param  {MediaError|string|number} [err]
24879   *         A MediaError or a string/number to be turned
24880   *         into a MediaError
24881   *
24882   * @return {MediaError|null}
24883   *         The current MediaError when getting (or null)
24884   */
24885
24886
24887  Player.prototype.error = function error(err) {
24888    if (err === undefined) {
24889      return this.error_ || null;
24890    }
24891
24892    // restoring to default
24893    if (err === null) {
24894      this.error_ = err;
24895      this.removeClass('vjs-error');
24896      if (this.errorDisplay) {
24897        this.errorDisplay.close();
24898      }
24899      return;
24900    }
24901
24902    this.error_ = new MediaError(err);
24903
24904    // add the vjs-error classname to the player
24905    this.addClass('vjs-error');
24906
24907    // log the name of the error type and any message
24908    // 
24908ie8 just logs "[object object]" if you just log th
vendor: 6,559 bytes, lines 24908-25121
24908e error object
24909    log$1.error('(CODE:' + this.error_.code + ' ' + MediaError.errorTypes[this.error_.code] + ')', this.error_.message, this.error_);
24910
24911    /**
24912     * @event Player#error
24913     * @type {EventTarget~Event}
24914     */
24915    this.trigger('error');
24916
24917    return;
24918  };
24919
24920  /**
24921   * Report user activity
24922   *
24923   * @param {Object} event
24924   *        Event object
24925   */
24926
24927
24928  Player.prototype.reportUserActivity = function reportUserActivity(event) {
24929    this.userActivity_ = true;
24930  };
24931
24932  /**
24933   * Get/set if user is active
24934   *
24935   * @fires Player#useractive
24936   * @fires Player#userinactive
24937   *
24938   * @param {boolean} [bool]
24939   *        - true if the user is active
24940   *        - false if the user is inactive
24941   *
24942   * @return {boolean}
24943   *         The current value of userActive when getting
24944   */
24945
24946
24947  Player.prototype.userActive = function userActive(bool) {
24948    if (bool === undefined) {
24949      return this.userActive_;
24950    }
24951
24952    bool = !!bool;
24953
24954    if (bool === this.userActive_) {
24955      return;
24956    }
24957
24958    this.userActive_ = bool;
24959
24960    if (this.userActive_) {
24961      this.userActivity_ = true;
24962      this.removeClass('vjs-user-inactive');
24963      this.addClass('vjs-user-active');
24964      /**
24965       * @event Player#useractive
24966       * @type {EventTarget~Event}
24967       */
24968      this.trigger('useractive');
24969      return;
24970    }
24971
24972    // Chrome/Safari/IE have bugs where when you change the cursor it can
24973    // trigger a mousemove event. This causes an issue when you're hiding
24974    // the cursor when the user is inactive, and a mousemove signals user
24975    // activity. Making it impossible to go into inactive mode. Specifically
24976    // this happens in fullscreen when we really need to hide the cursor.
24977    //
24978    // When this gets resolved in ALL browsers it can be removed
24979    // https://code.google.com/p/chromium/issues/detail?id=103041
24980    if (this.tech_) {
24981      this.tech_.one('mousemove', function (e) {
24982        e.stopPropagation();
24983        e.preventDefault();
24984      });
24985    }
24986
24987    this.userActivity_ = false;
24988    this.removeClass('vjs-user-active');
24989    this.addClass('vjs-user-inactive');
24990    /**
24991     * @event Player#userinactive
24992     * @type {EventTarget~Event}
24993     */
24994    this.trigger('userinactive');
24995  };
24996
24997  /**
24998   * Listen for user activity based on timeout value
24999   *
25000   * @private
25001   */
25002
25003
25004  Player.prototype.listenForUserActivity_ = function listenForUserActivity_() {
25005    var mouseInProgress = void 0;
25006    var lastMoveX = void 0;
25007    var lastMoveY = void 0;
25008    var handleActivity = bind(this, this.reportUserActivity);
25009
25010    var handleMouseMove = function handleMouseMove(e) {
25011      // #1068 - Prevent mousemove spamming
25012      // Chrome Bug: https://code.google.com/p/chromium/issues/detail?id=366970
25013      if (e.screenX !== lastMoveX || e.screenY !== lastMoveY) {
25014        lastMoveX = e.screenX;
25015        lastMoveY = e.screenY;
25016        handleActivity();
25017      }
25018    };
25019
25020    var handleMouseDown = function handleMouseDown() {
25021      handleActivity();
25022      // For as long as the they are touching the device or have their mouse down,
25023      // we consider them active even if they're not moving their finger or mouse.
25024      // So we want to continue to update that they are active
25025      this.clearInterval(mouseInProgress);
25026      // Setting userActivity=true now and setting the interval to the same time
25027      // as the activityCheck interval (250) should ensure we never miss the
25028      // next activityCheck
25029      mouseInProgress = this.setInterval(handleActivity, 250);
25030    };
25031
25032    var handleMouseUp = function handleMouseUp(event) {
25033      handleActivity();
25034      // Stop the interval that maintains activity if the mouse/touch is down
25035      this.clearInterval(mouseInProgress);
25036    };
25037
25038    // Any mouse movement will be considered user activity
25039    this.on('mousedown', handleMouseDown);
25040    this.on('mousemove', handleMouseMove);
25041    this.on('mouseup', handleMouseUp);
25042
25043    // Listen for keyboard navigation
25044    // Shouldn't need to use inProgress interval because of key repeat
25045    this.on('keydown', handleActivity);
25046    this.on('keyup', handleActivity);
25047
25048    // Run an interval every 250 milliseconds instead of stuffing everything into
25049    // the mousemove/touchmove function itself, to prevent performance degradation.
25050    // `this.reportUserActivity` simply sets this.userActivity_ to true, which
25051    // then gets picked up by this loop
25052    // http://ejohn.org/blog/learning-from-twitter/
25053    var inactivityTimeout = void 0;
25054
25055    this.setInterval(function () {
25056      // Check to see if mouse/touch activity has happened
25057      if (!this.userActivity_) {
25058        return;
25059      }
25060
25061      // Reset the activity tracker
25062      this.userActivity_ = false;
25063
25064      // If the user state was inactive, set the state to active
25065      this.userActive(true);
25066
25067      // Clear any existing inactivity timeout to start the timer over
25068      this.clearTimeout(inactivityTimeout);
25069
25070      var timeout = this.options_.inactivityTimeout;
25071
25072      if (timeout <= 0) {
25073        return;
25074      }
25075
25076      // In <timeout> milliseconds, if no more activity has occurred the
25077      // user will be considered inactive
25078      inactivityTimeout = this.setTimeout(function () {
25079        // Protect against the case where the inactivityTimeout can trigger just
25080        // before the next user activity is picked up by the activity check loop
25081        // causing a flicker
25082        if (!this.userActivity_) {
25083          this.userActive(false);
25084        }
25085      }, timeout);
25086    }, 250);
25087  };
25088
25089  /**
25090   * Gets or sets the current playback rate. A playback rate of
25091   * 1.0 represents normal speed and 0.5 would indicate half-speed
25092   * playback, for instance.
25093   *
25094   * @see https://html.spec.whatwg.org/multipage/embedded-content.html#dom-media-playbackrate
25095   *
25096   * @param {number} [rate]
25097   *       New playback rate to set.
25098   *
25099   * @return {number}
25100   *         The current playback rate when getting or 1.0
25101   */
25102
25103
25104  Player.prototype.playbackRate = function playbackRate(rate) {
25105    if (rate !== undefined) {
25106      // NOTE: this.cache_.lastPlaybackRate is set from the tech handler
25107      // that is registered above
25108      this.techCall_('setPlaybackRate', rate);
25109      return;
25110    }
25111
25112    if (this.tech_ && this.tech_.featuresPlaybackRate) {
25113      return this.cache_.lastPlaybackRate || this.techGet_('playbackRate');
25114    }
25115    return 1.0;
25116  };
25117
25118  /**
25119   * Gets or sets the current default playback rate. A default playback rate of
25120   * 1.0 represents normal speed and 0.5 would indicate half-speed playback, for instance.
25121   * defaultPlaybackRate will only represent what the in
25121tial playbackRate of a video was, not
25122   * not the current playbackRate.
25123   *
25124   * @see https://html.spec.whatwg.org/multipage/embedded-content.html#dom-media-defaultplaybackrate
25125   *
25126   * @param {number} [rate]
25127   *       New default playback rate to set.
25128   *
25129   * @return {number|Player}
25130   *         - The default playback rate when getting or 1.0
25131   *         - the player when setting
25132   */
25133
25134
25135  Player.prototype.defaultPlaybackRate = function defaultPlaybackRate(rate) {
25136    if (rate !== undefined) {
25137      return this.techCall_('setDefaultPlaybackRate', rate);
25138    }
25139
25140    if (this.tech_ && this.tech_.featuresPlaybackRate) {
25141      return this.techGet_('defaultPlaybackRate');
25142    }
25143    return 1.0;
25144  };
25145
25146  /**
25147   * Gets or sets the audio flag
25148   *
25149   * @param {boolean} bool
25150   *        - true signals that this is an audio player
25151   *        - false signals that this is not an audio player
25152   *
25153   * @return {boolean}
25154   *         The current value of isAudio when getting
25155   */
25156
25157
25158  Player.prototype.isAudio = function isAudio(bool) {
25159    if (bool !== undefined) {
25160      this.isAudio_ = !!bool;
25161      return;
25162    }
25163
25164    return !!this.isAudio_;
25165  };
25166
25167  /**
25168   * A helper method for adding a {@link TextTrack} to our
25169   * {@link TextTrackList}.
25170   *
25171   * In addition to the W3C settings we allow adding additional info through options.
25172   *
25173   * @see http://www.w3.org/html/wg/drafts/html/master/embedded-content-0.html#dom-media-addtexttrack
25174   *
25175   * @param {string} [kind]
25176   *        the kind of TextTrack you are adding
25177   *
25178   * @param {string} [label]
25179   *        the label to give the TextTrack label
25180   *
25181   * @param {string} [language]
25182   *        the language to set on the TextTrack
25183   *
25184   * @return {TextTrack|undefined}
25185   *         the TextTrack that was added or undefined
25186   *         if there is no tech
25187   */
25188
25189
25190  Player.prototype.addTextTrack = function addTextTrack(kind, label, language) {
25191    if (this.tech_) {
25192      return this.tech_.addTextTrack(kind, label, language);
25193    }
25194  };
25195
25196  /**
25197   * Create a remote {@link TextTrack} and an {@link HTMLTrackElement}. It will
25198   * automatically removed from the video element whenever the source changes, unless
25199   * manualCleanup is set to false.
25200   *
25201   * @param {Object} options
25202   *        Options to pass to {@link HTMLTrackElement} during creation. See
25203   *        {@link HTMLTrackElement} for object properties that you should use.
25204   *
25205   * @param {boolean} [manualCleanup=true] if set to false, the TextTrack will be
25206   *
25207   * @return {HtmlTrackElement}
25208   *         the HTMLTrackElement that was created and added
25209   *         to the HtmlTrackElementList and the remote
25210   *         TextTrackList
25211   *
25212   * @deprecated The default value of the "manualCleanup" parameter will default
vendor: 4,868 bytes, lines 25213-25392
25213   *             to "false" in upcoming versions of Video.js
25214   */
25215
25216
25217  Player.prototype.addRemoteTextTrack = function addRemoteTextTrack(options, manualCleanup) {
25218    if (this.tech_) {
25219      return this.tech_.addRemoteTextTrack(options, manualCleanup);
25220    }
25221  };
25222
25223  /**
25224   * Remove a remote {@link TextTrack} from the respective
25225   * {@link TextTrackList} and {@link HtmlTrackElementList}.
25226   *
25227   * @param {Object} track
25228   *        Remote {@link TextTrack} to remove
25229   *
25230   * @return {undefined}
25231   *         does not return anything
25232   */
25233
25234
25235  Player.prototype.removeRemoteTextTrack = function removeRemoteTextTrack() {
25236    var _ref3 = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {},
25237        _ref3$track = _ref3.track,
25238        track = _ref3$track === undefined ? arguments[0] : _ref3$track;
25239
25240    // destructure the input into an object with a track argument, defaulting to arguments[0]
25241    // default the whole argument to an empty object if nothing was passed in
25242
25243    if (this.tech_) {
25244      return this.tech_.removeRemoteTextTrack(track);
25245    }
25246  };
25247
25248  /**
25249   * Gets available media playback quality metrics as specified by the W3C's Media
25250   * Playback Quality API.
25251   *
25252   * @see [Spec]{@link https://wicg.github.io/media-playback-quality}
25253   *
25254   * @return {Object|undefined}
25255   *         An object with supported media playback quality metrics or undefined if there
25256   *         is no tech or the tech does not support it.
25257   */
25258
25259
25260  Player.prototype.getVideoPlaybackQuality = function getVideoPlaybackQuality() {
25261    return this.techGet_('getVideoPlaybackQuality');
25262  };
25263
25264  /**
25265   * Get video width
25266   *
25267   * @return {number}
25268   *         current video width
25269   */
25270
25271
25272  Player.prototype.videoWidth = function videoWidth() {
25273    return this.tech_ && this.tech_.videoWidth && this.tech_.videoWidth() || 0;
25274  };
25275
25276  /**
25277   * Get video height
25278   *
25279   * @return {number}
25280   *         current video height
25281   */
25282
25283
25284  Player.prototype.videoHeight = function videoHeight() {
25285    return this.tech_ && this.tech_.videoHeight && this.tech_.videoHeight() || 0;
25286  };
25287
25288  /**
25289   * The player's language code
25290   * NOTE: The language should be set in the player options if you want the
25291   * the controls to be built with a specific language. Changing the lanugage
25292   * later will not update controls text.
25293   *
25294   * @param {string} [code]
25295   *        the language code to set the player to
25296   *
25297   * @return {string}
25298   *         The current language code when getting
25299   */
25300
25301
25302  Player.prototype.language = function language(code) {
25303    if (code === undefined) {
25304      return this.language_;
25305    }
25306
25307    this.language_ = String(code).toLowerCase();
25308  };
25309
25310  /**
25311   * Get the player's language dictionary
25312   * Merge every time, because a newly added plugin might call videojs.addLanguage() at any time
25313   * Languages specified directly in the player options have precedence
25314   *
25315   * @return {Array}
25316   *         An array of of supported languages
25317   */
25318
25319
25320  Player.prototype.languages = function languages() {
25321    return mergeOptions(Player.prototype.options_.languages, this.languages_);
25322  };
25323
25324  /**
25325   * returns a JavaScript object reperesenting the current track
25326   * information. **DOES not return it as JSON**
25327   *
25328   * @return {Object}
25329   *         Object representing the current of track info
25330   */
25331
25332
25333  Player.prototype.toJSON = function toJSON() {
25334    var options = mergeOptions(this.options_);
25335    var tracks = options.tracks;
25336
25337    options.tracks = [];
25338
25339    for (var i = 0; i < tracks.length; i++) {
25340      var track = tracks[i];
25341
25342      // deep merge tracks and null out player so no circular references
25343      track = mergeOptions(track);
25344      track.player = undefined;
25345      options.tracks[i] = track;
25346    }
25347
25348    return options;
25349  };
25350
25351  /**
25352   * Creates a simple modal dialog (an instance of the {@link ModalDialog}
25353   * component) that immediately overlays the player with arbitrary
25354   * content and removes itself when closed.
25355   *
25356   * @param {string|Function|Element|Array|null} content
25357   *        Same as {@link ModalDialog#content}'s param of the same name.
25358   *        The most straight-forward usage is to provide a string or DOM
25359   *        element.
25360   *
25361   * @param {Object} [options]
25362   *        Extra options which will be passed on to the {@link ModalDialog}.
25363   *
25364   * @return {ModalDialog}
25365   *         the {@link ModalDialog} that was created
25366   */
25367
25368
25369  Player.prototype.createModal = function createModal(content, options) {
25370    var _this11 = this;
25371
25372    options = options || {};
25373    options.content = content || '';
25374
25375    var modal = new ModalDialog(this, options);
25376
25377    this.addChild(modal);
25378    modal.on('dispose', function () {
25379      _this11.removeChild(modal);
25380    });
25381
25382    modal.open();
25383    return modal;
25384  };
25385
25386  /**
25387   * Gets tag settings
25388   *
25389   * @param {Element} tag
25390   *        The player tag
25391   *
25392   * @return {Object}
vendor: 4,271 bytes, lines 25393-25563
25393   *         An object containing all of the settings
25394   *         for a player tag
25395   */
25396
25397
25398  Player.getTagSettings = function getTagSettings(tag) {
25399    var baseOptions = {
25400      sources: [],
25401      tracks: []
25402    };
25403
25404    var tagOptions = getAttributes(tag);
25405    var dataSetup = tagOptions['data-setup'];
25406
25407    if (hasClass(tag, 'vjs-fluid')) {
25408      tagOptions.fluid = true;
25409    }
25410
25411    // Check if data-setup attr exists.
25412    if (dataSetup !== null) {
25413      // Parse options JSON
25414      // If empty string, make it a parsable json object.
25415      var _safeParseTuple = tuple(dataSetup || '{}'),
25416          err = _safeParseTuple[0],
25417          data = _safeParseTuple[1];
25418
25419      if (err) {
25420        log$1.error(err);
25421      }
25422      assign(tagOptions, data);
25423    }
25424
25425    assign(baseOptions, tagOptions);
25426
25427    // Get tag children settings
25428    if (tag.hasChildNodes()) {
25429      var children = tag.childNodes;
25430
25431      for (var i = 0, j = children.length; i < j; i++) {
25432        var child = children[i];
25433        // Change case needed: http://ejohn.org/blog/nodename-case-sensitivity/
25434        var childName = child.nodeName.toLowerCase();
25435
25436        if (childName === 'source') {
25437          baseOptions.sources.push(getAttributes(child));
25438        } else if (childName === 'track') {
25439          baseOptions.tracks.push(getAttributes(child));
25440        }
25441      }
25442    }
25443
25444    return baseOptions;
25445  };
25446
25447  /**
25448   * Determine wether or not flexbox is supported
25449   *
25450   * @return {boolean}
25451   *         - true if flexbox is supported
25452   *         - false if flexbox is not supported
25453   */
25454
25455
25456  Player.prototype.flexNotSupported_ = function flexNotSupported_() {
25457    var elem = document_1.createElement('i');
25458
25459    // Note: We don't actually use flexBasis (or flexOrder), but it's one of the more
25460    // common flex features that we can rely on when checking for flex support.
25461    return !('flexBasis' in elem.style || 'webkitFlexBasis' in elem.style || 'mozFlexBasis' in elem.style || 'msFlexBasis' in elem.style ||
25462    // IE10-specific (2012 flex spec)
25463    'msFlexOrder' in elem.style);
25464  };
25465
25466  return Player;
25467}(Component);
25468
25469/**
25470 * Get the {@link VideoTrackList}
25471 * @link https://html.spec.whatwg.org/multipage/embedded-content.html#videotracklist
25472 *
25473 * @return {VideoTrackList}
25474 *         the current video track list
25475 *
25476 * @method Player.prototype.videoTracks
25477 */
25478
25479/**
25480 * Get the {@link AudioTrackList}
25481 * @link https://html.spec.whatwg.org/multipage/embedded-content.html#audiotracklist
25482 *
25483 * @return {AudioTrackList}
25484 *         the current audio track list
25485 *
25486 * @method Player.prototype.audioTracks
25487 */
25488
25489/**
25490 * Get the {@link TextTrackList}
25491 *
25492 * @link http://www.w3.org/html/wg/drafts/html/master/embedded-content-0.html#dom-media-texttracks
25493 *
25494 * @return {TextTrackList}
25495 *         the current text track list
25496 *
25497 * @method Player.prototype.textTracks
25498 */
25499
25500/**
25501 * Get the remote {@link TextTrackList}
25502 *
25503 * @return {TextTrackList}
25504 *         The current remote text track list
25505 *
25506 * @method Player.prototype.remoteTextTracks
25507 */
25508
25509/**
25510 * Get the remote {@link HtmlTrackElementList} tracks.
25511 *
25512 * @return {HtmlTrackElementList}
25513 *         The current remote text track element list
25514 *
25515 * @method Player.prototype.remoteTextTrackEls
25516 */
25517
25518ALL.names.forEach(function (name$$1) {
25519  var props = ALL[name$$1];
25520
25521  Player.prototype[props.getterName] = function () {
25522    if (this.tech_) {
25523      return this.tech_[props.getterName]();
25524    }
25525
25526    // if we have not yet loadTech_, we create {video,audio,text}Tracks_
25527    // these will be passed to the tech during loading
25528    this[props.privateName] = this[props.privateName] || new props.ListClass();
25529    return this[props.privateName];
25530  };
25531});
25532
25533/**
25534 * Global player list
25535 *
25536 * @type {Object}
25537 */
25538Player.players = {};
25539
25540var navigator$1 = window_1.navigator;
25541
25542/*
25543 * Player instance options, surfaced using options
25544 * options = Player.prototype.options_
25545 * Make changes in options, not here.
25546 *
25547 * @type {Object}
25548 * @private
25549 */
25550Player.prototype.options_ = {
25551  // Default order of fallback technology
25552  techOrder: Tech.defaultTechOrder_,
25553
25554  html5: {},
25555  flash: {},
25556
25557  // default inactivity timeout
25558  inactivityTimeout: 2000,
25559
25560  // default playback rates
25561  playbackRates: [],
25562  // Add playback rate selection by adding rates
25563  // 'playbackRates': [0.5, 1, 1.5, 2],
25564
25565  // Included control sets
25566  children: ['mediaLoader', 'posterImage', 'textTrackDisplay', 'loadingSpinner', 'bigPlayButton', 'controlBar', 'errorDisplay', 'textTrackSettings'],
25567
25568  language: navigator$1 && (navigator$1.languages && navigator$1.languages[0] || navigator$1.userLanguage || navigator$1.language) || 'en',
25569
25570  // locales and their language translations
25571  languages: {},
25572
25573  // Default message to show when a video cannot be played.
25574  notSupportedMessage: 'No compatible source was found for this media.'
25575};
25576
25577if (!IS_IE8) {
25578  Player.prototype.options_.children.push('resizeManager');
25579}
25580
25581[
25582/**
25583 * Returns whether or not the player is in the "ended" state.
25584 *
25585 * @return {Boolean} True if the player is in the ended state, false if not.
25586 * @method Player#ended
25587 */
25588'ended',
25589/**
25590 * Returns whether or not the player is in the "seeking" state.
25591 *
25592 * @return {Boolean} True if the player is in the seeking state, false if not.
25593 * @method Player#seeking
25594 */
25595'seeking',
25596/**
25597 * Returns the TimeRanges of the media that are currently available
25598 * for seeking to.
25599 *
25600 * @return {TimeRanges} the seekable intervals of the media timeline
25601 * @method Player#seekable
25602 */
25603'seekable',
25604/**
25605 * Returns the current state of network activity for the element, from
25606 * the codes in the list below.
25607 * - NETWORK_EMPTY (numeric value 0)
25608 *   The element has not yet been initialised. All attributes are in
25609 *   their initial states.
25610 * - NETWORK_IDLE (numeric value 1)
25611 *   The element's resource selection algorithm is active and has
25612 *   selected a resource, but it is not actually using the network at
25613 *   this time.
25614 * - NETWORK_LOADING (numeric value 2)
25615 *   The user agent is actively trying to download data.
25616 * - NETWORK_NO_SOURCE (numeric value 3)
25617 *   The element's resource selection algorithm is active, but it has
25618 *   not yet found a resource to use.
25619 *
25620 * @see https://html.spec.whatwg.org/multipage/embedded-content.html#network-states
25621 * @return {number} the current network activity state
25622 * @method Player#networkState
25623 */
25624'networkState',
25625/**
25626 * Returns a value that expresses the current state of the element
25627 * with respect to rendering the current playback position, from the
25628 * codes in the list below.
25629 * - HAVE_NOTHING (numeric value 0)
25630 *   No information regarding the media resource is available.
25631 * - HAVE_METADATA (numeric value 1)
25632 *   Enough of the resource has been obtained that the duration of the
25633 *   resource is available.
25634 * - HAVE_CURRENT_DATA (numeric value 2)
25635 *   Data for the immediate current playback position is available.
25636 * - HAVE_FUTURE_DATA (numeric value 3)
25637 *   Data for the immediate current playback position is available, as
25638 *   well as enough data for the user agent to advance the current
25639 *   playback position in the direction of playback.
25640 * - HAVE_ENOUGH_DATA (numeric value 4)
25641 *   The user agent estimates that enough data is available for
25642 *   playback to proceed uninterrupted.
25643 *
25644 * @see https://html.spec.whatwg.org/multipage/embedded-content.html#dom-media-readystate
25645 * @return {number} the current playback rendering state
25646 * @method Player#readyState
25647 */
25648'readyState'].forEach(function (fn) {
25649  Player.prototype[fn] = function () {
25650    return this.techGet_(fn);
25651  };
25652});
25653
25654TECH_EVENTS_RETRIGGER.forEach(function (event) {
25655  Player.prototype['handleTech' + toTitleCase(event) + '_'] = function () {
25656    return this.trigger(event);
25657  };
25658});
25659
25660/**
25661 * Fired when the player has initial duration and dimension information
25662 *
25663 * @event Player#loadedmetadata
25664 * @type {EventTarget~Event}
25665 */
25666
25667/**
25668 * Fired when the player has downloaded data at the current playback position
25669 *
25670 * @event Player#loadeddata
25671 * @type {EventTarget~Event}
25672 */
25673
25674/**
25675 * Fired when the current playback position has changed *
25676 * During playback this is fired every 15-250 milliseconds, depending on the
25677 * playback technology in use.
25678 *
25679 * @event Player#timeupdate
25680 * @type {EventTarget~Event}
25681 */
25682
25683/**
25684 * Fired when the volume changes
25685 *
25686 * @event Player#volumechange
25687 * @type {EventTarget~Event}
25688 */
25689
25690/**
25691 * Reports whether or not a player has a plugin available.
25692 *
25693 * This does not report whether or not the plugin has ever been initialized
25694 * on this player. For that, [usingPlugin]{@link Player#usingPlugin}.
25695 *
25696 * @method Player#hasPlugin
25697 * @param  {string}  name
25698 *         The name of a plugin.
25699 *
25700 * @return {boolean}
25701 *         Whether or not this player has the requested plugin available.
25702 */
25703
25704/**
25705 * Reports whether or not a player is using a plugin by name.
25706 *
25707 * For basic plugins, this only reports whether the plugin has _ever_ been
25708 * initialized on this player.
25709 *
25710 * @method Player#usingPlugin
25711 * @param  {string} name
25712 *         The name of a plugin.
25713 *
25714 * @return {boolean}
25715 *         Whether or not this player is using the requested plugin.
25716 */
25717
25718Component.registerComponent('Player', Player);
25719
25720/**
25721 * @file plugin.js
25722 */
vendor: 13,630 bytes, lines 25722-26230
25722
25723/**
25724 * The base plugin name.
25725 *
25726 * @private
25727 * @constant
25728 * @type {string}
25729 */
25730var BASE_PLUGIN_NAME = 'plugin';
25731
25732/**
25733 * The key on which a player's active plugins cache is stored.
25734 *
25735 * @private
25736 * @constant
25737 * @type     {string}
25738 */
25739var PLUGIN_CACHE_KEY = 'activePlugins_';
25740
25741/**
25742 * Stores registered plugins in a private space.
25743 *
25744 * @private
25745 * @type    {Object}
25746 */
25747var pluginStorage = {};
25748
25749/**
25750 * Reports whether or not a plugin has been registered.
25751 *
25752 * @private
25753 * @param   {string} name
25754 *          The name of a plugin.
25755 *
25756 * @returns {boolean}
25757 *          Whether or not the plugin has been registered.
25758 */
25759var pluginExists = function pluginExists(name) {
25760  return pluginStorage.hasOwnProperty(name);
25761};
25762
25763/**
25764 * Get a single registered plugin by name.
25765 *
25766 * @private
25767 * @param   {string} name
25768 *          The name of a plugin.
25769 *
25770 * @returns {Function|undefined}
25771 *          The plugin (or undefined).
25772 */
25773var getPlugin = function getPlugin(name) {
25774  return pluginExists(name) ? pluginStorage[name] : undefined;
25775};
25776
25777/**
25778 * Marks a plugin as "active" on a player.
25779 *
25780 * Also, ensures that the player has an object for tracking active plugins.
25781 *
25782 * @private
25783 * @param   {Player} player
25784 *          A Video.js player instance.
25785 *
25786 * @param   {string} name
25787 *          The name of a plugin.
25788 */
25789var markPluginAsActive = function markPluginAsActive(player, name) {
25790  player[PLUGIN_CACHE_KEY] = player[PLUGIN_CACHE_KEY] || {};
25791  player[PLUGIN_CACHE_KEY][name] = true;
25792};
25793
25794/**
25795 * Triggers a pair of plugin setup events.
25796 *
25797 * @private
25798 * @param  {Player} player
25799 *         A Video.js player instance.
25800 *
25801 * @param  {Plugin~PluginEventHash} hash
25802 *         A plugin event hash.
25803 *
25804 * @param  {Boolean} [before]
25805 *         If true, prefixes the event name with "before". In other words,
25806 *         use this to trigger "beforepluginsetup" instead of "pluginsetup".
25807 */
25808var triggerSetupEvent = function triggerSetupEvent(player, hash, before) {
25809  var eventName = (before ? 'before' : '') + 'pluginsetup';
25810
25811  player.trigger(eventName, hash);
25812  player.trigger(eventName + ':' + hash.name, hash);
25813};
25814
25815/**
25816 * Takes a basic plugin function and returns a wrapper function which marks
25817 * on the player that the plugin has been activated.
25818 *
25819 * @private
25820 * @param   {string} name
25821 *          The name of the plugin.
25822 *
25823 * @param   {Function} plugin
25824 *          The basic plugin.
25825 *
25826 * @returns {Function}
25827 *          A wrapper function for the given plugin.
25828 */
25829var createBasicPlugin = function createBasicPlugin(name, plugin) {
25830  var basicPluginWrapper = function basicPluginWrapper() {
25831
25832    // We trigger the "beforepluginsetup" and "pluginsetup" events on the player
25833    // regardless, but we want the hash to be consistent with the hash provided
25834    // for advanced plugins.
25835    //
25836    // The only potentially counter-intuitive thing here is the `instance` in
25837    // the "pluginsetup" event is the value returned by the `plugin` function.
25838    triggerSetupEvent(this, { name: name, plugin: plugin, instance: null }, true);
25839
25840    var instance = plugin.apply(this, arguments);
25841
25842    markPluginAsActive(this, name);
25843    triggerSetupEvent(this, { name: name, plugin: plugin, instance: instance });
25844
25845    return instance;
25846  };
25847
25848  Object.keys(plugin).forEach(function (prop) {
25849    basicPluginWrapper[prop] = plugin[prop];
25850  });
25851
25852  return basicPluginWrapper;
25853};
25854
25855/**
25856 * Takes a plugin sub-class and returns a factory function for generating
25857 * instances of it.
25858 *
25859 * This factory function will replace itself with an instance of the requested
25860 * sub-class of Plugin.
25861 *
25862 * @private
25863 * @param   {string} name
25864 *          The name of the plugin.
25865 *
25866 * @param   {Plugin} PluginSubClass
25867 *          The advanced plugin.
25868 *
25869 * @returns {Function}
25870 */
25871var createPluginFactory = function createPluginFactory(name, PluginSubClass) {
25872
25873  // Add a `name` property to the plugin prototype so that each plugin can
25874  // refer to itself by name.
25875  PluginSubClass.prototype.name = name;
25876
25877  return function () {
25878    triggerSetupEvent(this, { name: name, plugin: PluginSubClass, instance: null }, true);
25879
25880    for (var _len = arguments.length, args = Array(_len), _key = 0; _key < _len; _key++) {
25881      args[_key] = arguments[_key];
25882    }
25883
25884    var instance = new (Function.prototype.bind.apply(PluginSubClass, [null].concat([this].concat(args))))();
25885
25886    // The plugin is replaced by a function that returns the current instance.
25887    this[name] = function () {
25888      return instance;
25889    };
25890
25891    triggerSetupEvent(this, instance.getEventHash());
25892
25893    return instance;
25894  };
25895};
25896
25897/**
25898 * Parent class for all advanced plugins.
25899 *
25900 * @mixes   module:evented~EventedMixin
25901 * @mixes   module:stateful~StatefulMixin
25902 * @fires   Player#beforepluginsetup
25903 * @fires   Player#beforepluginsetup:$name
25904 * @fires   Player#pluginsetup
25905 * @fires   Player#pluginsetup:$name
25906 * @listens Player#dispose
25907 * @throws  {Error}
25908 *          If attempting to instantiate the base {@link Plugin} class
25909 *          directly instead of via a sub-class.
25910 */
25911
25912var Plugin = function () {
25913
25914  /**
25915   * Creates an instance of this class.
25916   *
25917   * Sub-classes should call `super` to ensure plugins are properly initialized.
25918   *
25919   * @param {Player} player
25920   *        A Video.js player instance.
25921   */
25922  function Plugin(player) {
25923    classCallCheck(this, Plugin);
25924
25925    if (this.constructor === Plugin) {
25926      throw new Error('Plugin must be sub-classed; not directly instantiated.');
25927    }
25928
25929    this.player = player;
25930
25931    // Make this object evented, but remove the added `trigger` method so we
25932    // use the prototype version instead.
25933    evented(this);
25934    delete this.trigger;
25935
25936    stateful(this, this.constructor.defaultState);
25937    markPluginAsActive(player, this.name);
25938
25939    // Auto-bind the dispose method so we can use it as a listener and unbind
25940    // it later easily.
25941    this.dispose = bind(this, this.dispose);
25942
25943    // If the player is disposed, dispose the plugin.
25944    player.on('dispose', this.dispose);
25945  }
25946
25947  /**
25948   * Get the version of the plugin that was set on <pluginName>.VERSION
25949   */
25950
25951
25952  Plugin.prototype.version = function version() {
25953    return this.constructor.VERSION;
25954  };
25955
25956  /**
25957   * Each event triggered by plugins includes a hash of additional data with
25958   * conventional properties.
25959   *
25960   * This returns that object or mutates an existing hash.
25961   *
25962   * @param   {Object} [hash={}]
25963   *          An object to be used as event an event hash.
25964   *
25965   * @returns {Plugin~PluginEventHash}
25966   *          An event hash object with provided properties mixed-in.
25967   */
25968
25969
25970  Plugin.prototype.getEventHash = function getEventHash() {
25971    var hash = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
25972
25973    hash.name = this.name;
25974    hash.plugin = this.constructor;
25975    hash.instance = this;
25976    return hash;
25977  };
25978
25979  /**
25980   * Triggers an event on the plugin object and overrides
25981   * {@link module:evented~EventedMixin.trigger|EventedMixin.trigger}.
25982   *
25983   * @param   {string|Object} event
25984   *          An event type or an object with a type property.
25985   *
25986   * @param   {Object} [hash={}]
25987   *          Additional data hash to merge with a
25988   *          {@link Plugin~PluginEventHash|PluginEventHash}.
25989   *
25990   * @returns {boolean}
25991   *          Whether or not default was prevented.
25992   */
25993
25994
25995  Plugin.prototype.trigger = function trigger$$1(event) {
25996    var hash = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
25997
25998    return trigger(this.eventBusEl_, event, this.getEventHash(hash));
25999  };
26000
26001  /**
26002   * Handles "statechanged" events on the plugin. No-op by default, override by
26003   * subclassing.
26004   *
26005   * @abstract
26006   * @param    {Event} e
26007   *           An event object provided by a "statechanged" event.
26008   *
26009   * @param    {Object} e.changes
26010   *           An object describing changes that occurred with the "statechanged"
26011   *           event.
26012   */
26013
26014
26015  Plugin.prototype.handleStateChanged = function handleStateChanged(e) {};
26016
26017  /**
26018   * Disposes a plugin.
26019   *
26020   * Subclasses can override this if they want, but for the sake of safety,
26021   * it's probably best to subscribe the "dispose" event.
26022   *
26023   * @fires Plugin#dispose
26024   */
26025
26026
26027  Plugin.prototype.dispose = function dispose() {
26028    var name = this.name,
26029        player = this.player;
26030
26031    /**
26032     * Signals that a advanced plugin is about to be disposed.
26033     *
26034     * @event Plugin#dispose
26035     * @type  {EventTarget~Event}
26036     */
26037
26038    this.trigger('dispose');
26039    this.off();
26040    player.off('dispose', this.dispose);
26041
26042    // Eliminate any possible sources of leaking memory by clearing up
26043    // references between the player and the plugin instance and nulling out
26044    // the plugin's state and replacing methods with a function that throws.
26045    player[PLUGIN_CACHE_KEY][name] = false;
26046    this.player = this.state = null;
26047
26048    // Finally, replace the plugin name on the player with a new factory
26049    // function, so that the plugin is ready to be set up again.
26050    player[name] = createPluginFactory(name, pluginStorage[name]);
26051  };
26052
26053  /**
26054   * Determines if a plugin is a basic plugin (i.e. not a sub-class of `Plugin`).
26055   *
26056   * @param   {string|Function} plugin
26057   *          If a string, matches the name of a plugin. If a function, will be
26058   *          tested directly.
26059   *
26060   * @returns {boolean}
26061   *          Whether or not a plugin is a basic plugin.
26062   */
26063
26064
26065  Plugin.isBasic = function isBasic(plugin) {
26066    var p = typeof plugin === 'string' ? getPlugin(plugin) : plugin;
26067
26068    return typeof p === 'function' && !Plugin.prototype.isPrototypeOf(p.prototype);
26069  };
26070
26071  /**
26072   * Register a Video.js plugin.
26073   *
26074   * @param   {string} name
26075   *          The name of the plugin to be registered. Must be a string and
26076   *          must not match an existing plugin or a method on the `Player`
26077   *          prototype.
26078   *
26079   * @param   {Function} plugin
26080   *          A sub-class of `Plugin` or a function for basic plugins.
26081   *
26082   * @returns {Function}
26083   *          For advanced plugins, a factory function for that plugin. For
26084   *          basic plugins, a wrapper function that initializes the plugin.
26085   */
26086
26087
26088  Plugin.registerPlugin = function registerPlugin(name, plugin) {
26089    if (typeof name !== 'string') {
26090      throw new Error('Illegal plugin name, "' + name + '", must be a string, was ' + (typeof name === 'undefined' ? 'undefined' : _typeof(name)) + '.');
26091    }
26092
26093    if (pluginExists(name)) {
26094      log$1.warn('A plugin named "' + name + '" already exists. You may want to avoid re-registering plugins!');
26095    } else if (Player.prototype.hasOwnProperty(name)) {
26096      throw new Error('Illegal plugin name, "' + name + '", cannot share a name with an existing player method!');
26097    }
26098
26099    if (typeof plugin !== 'function') {
26100      throw new Error('Illegal plugin for "' + name + '", must be a function, was ' + (typeof plugin === 'undefined' ? 'undefined' : _typeof(plugin)) + '.');
26101    }
26102
26103    pluginStorage[name] = plugin;
26104
26105    // Add a player prototype method for all sub-classed plugins (but not for
26106    // the base Plugin class).
26107    if (name !== BASE_PLUGIN_NAME) {
26108      if (Plugin.isBasic(plugin)) {
26109        Player.prototype[name] = createBasicPlugin(name, plugin);
26110      } else {
26111        Player.prototype[name] = createPluginFactory(name, plugin);
26112      }
26113    }
26114
26115    return plugin;
26116  };
26117
26118  /**
26119   * De-register a Video.js plugin.
26120   *
26121   * @param {string} name
26122   *        The name of the plugin to be deregistered.
26123   */
26124
26125
26126  Plugin.deregisterPlugin = function deregisterPlugin(name) {
26127    if (name === BASE_PLUGIN_NAME) {
26128      throw new Error('Cannot de-register base plugin.');
26129    }
26130    if (pluginExists(name)) {
26131      delete pluginStorage[name];
26132      delete Player.prototype[name];
26133    }
26134  };
26135
26136  /**
26137   * Gets an object containing multiple Video.js plugins.
26138   *
26139   * @param   {Array} [names]
26140   *          If provided, should be an array of plugin names. Defaults to _all_
26141   *          plugin names.
26142   *
26143   * @returns {Object|undefined}
26144   *          An object containing plugin(s) associated with their name(s) or
26145   *          `undefined` if no matching plugins exist).
26146   */
26147
26148
26149  Plugin.getPlugins = function getPlugins() {
26150    var names = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : Object.keys(pluginStorage);
26151
26152    var result = void 0;
26153
26154    names.forEach(function (name) {
26155      var plugin = getPlugin(name);
26156
26157      if (plugin) {
26158        result = result || {};
26159        result[name] = plugin;
26160      }
26161    });
26162
26163    return result;
26164  };
26165
26166  /**
26167   * Gets a plugin's version, if available
26168   *
26169   * @param   {string} name
26170   *          The name of a plugin.
26171   *
26172   * @returns {string}
26173   *          The plugin's version or an empty string.
26174   */
26175
26176
26177  Plugin.getPluginVersion = function getPluginVersion(name) {
26178    var plugin = getPlugin(name);
26179
26180    return plugin && plugin.VERSION || '';
26181  };
26182
26183  return Plugin;
26184}();
26185
26186/**
26187 * Gets a plugin by name if it exists.
26188 *
26189 * @static
26190 * @method   getPlugin
26191 * @memberOf Plugin
26192 * @param    {string} name
26193 *           The name of a plugin.
26194 *
26195 * @returns  {Function|undefined}
26196 *           The plugin (or `undefined`).
26197 */
26198
26199
26200Plugin.getPlugin = getPlugin;
26201
26202/**
26203 * The name of the base plugin class as it is registered.
26204 *
26205 * @type {string}
26206 */
26207Plugin.BASE_PLUGIN_NAME = BASE_PLUGIN_NAME;
26208
26209Plugin.registerPlugin(BASE_PLUGIN_NAME, Plugin);
26210
26211/**
26212 * Documented in player.js
26213 *
26214 * @ignore
26215 */
26216Player.prototype.usingPlugin = function (name) {
26217  return !!this[PLUGIN_CACHE_KEY] && this[PLUGIN_CACHE_KEY][name] === true;
26218};
26219
26220/**
26221 * Documented in player.js
26222 *
26223 * @ignore
26224 */
26225Player.prototype.hasPlugin = function (name) {
26226  return !!pluginExists(name);
26227};
26228
26229/**
26230 * 
vendor: 12,140 bytes, lines 26230-26703
26230Signals that a plugin is about to be set up on a player.
26231 *
26232 * @event    Player#beforepluginsetup
26233 * @type     {Plugin~PluginEventHash}
26234 */
26235
26236/**
26237 * Signals that a plugin is about to be set up on a player - by name. The name
26238 * is the name of the plugin.
26239 *
26240 * @event    Player#beforepluginsetup:$name
26241 * @type     {Plugin~PluginEventHash}
26242 */
26243
26244/**
26245 * Signals that a plugin has just been set up on a player.
26246 *
26247 * @event    Player#pluginsetup
26248 * @type     {Plugin~PluginEventHash}
26249 */
26250
26251/**
26252 * Signals that a plugin has just been set up on a player - by name. The name
26253 * is the name of the plugin.
26254 *
26255 * @event    Player#pluginsetup:$name
26256 * @type     {Plugin~PluginEventHash}
26257 */
26258
26259/**
26260 * @typedef  {Object} Plugin~PluginEventHash
26261 *
26262 * @property {string} instance
26263 *           For basic plugins, the return value of the plugin function. For
26264 *           advanced plugins, the plugin instance on which the event is fired.
26265 *
26266 * @property {string} name
26267 *           The name of the plugin.
26268 *
26269 * @property {string} plugin
26270 *           For basic plugins, the plugin function. For advanced plugins, the
26271 *           plugin class/constructor.
26272 */
26273
26274/**
26275 * @file extend.js
26276 * @module extend
26277 */
26278
26279/**
26280 * A combination of node inherits and babel's inherits (after transpile).
26281 * Both work the same but node adds `super_` to the subClass
26282 * and Bable adds the superClass as __proto__. Both seem useful.
26283 *
26284 * @param {Object} subClass
26285 *        The class to inherit to
26286 *
26287 * @param {Object} superClass
26288 *        The class to inherit from
26289 *
26290 * @private
26291 */
26292var _inherits = function _inherits(subClass, superClass) {
26293  if (typeof superClass !== 'function' && superClass !== null) {
26294    throw new TypeError('Super expression must either be null or a function, not ' + (typeof superClass === 'undefined' ? 'undefined' : _typeof(superClass)));
26295  }
26296
26297  subClass.prototype = Object.create(superClass && superClass.prototype, {
26298    constructor: {
26299      value: subClass,
26300      enumerable: false,
26301      writable: true,
26302      configurable: true
26303    }
26304  });
26305
26306  if (superClass) {
26307    // node
26308    subClass.super_ = superClass;
26309  }
26310};
26311
26312/**
26313 * Function for subclassing using the same inheritance that
26314 * videojs uses internally
26315 *
26316 * @static
26317 * @const
26318 *
26319 * @param {Object} superClass
26320 *        The class to inherit from
26321 *
26322 * @param {Object} [subClassMethods={}]
26323 *        The class to inherit to
26324 *
26325 * @return {Object}
26326 *         The new object with subClassMethods that inherited superClass.
26327 */
26328var extendFn = function extendFn(superClass) {
26329  var subClassMethods = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
26330
26331  var subClass = function subClass() {
26332    superClass.apply(this, arguments);
26333  };
26334
26335  var methods = {};
26336
26337  if ((typeof subClassMethods === 'undefined' ? 'undefined' : _typeof(subClassMethods)) === 'object') {
26338    if (subClassMethods.constructor !== Object.prototype.constructor) {
26339      subClass = subClassMethods.constructor;
26340    }
26341    methods = subClassMethods;
26342  } else if (typeof subClassMethods === 'function') {
26343    subClass = subClassMethods;
26344  }
26345
26346  _inherits(subClass, superClass);
26347
26348  // Extend subObj's prototype with functions and other properties from props
26349  for (var name in methods) {
26350    if (methods.hasOwnProperty(name)) {
26351      subClass.prototype[name] = methods[name];
26352    }
26353  }
26354
26355  return subClass;
26356};
26357
26358/**
26359 * @file video.js
26360 * @module videojs
26361 */
26362// Include the built-in techs
26363// HTML5 Element Shim for IE8
26364if (typeof HTMLVideoElement === 'undefined' && isReal()) {
26365  document_1.createElement('video');
26366  document_1.createElement('audio');
26367  document_1.createElement('track');
26368  document_1.createElement('video-js');
26369}
26370
26371/**
26372 * Normalize an `id` value by trimming off a leading `#`
26373 *
26374 * @param   {string} id
26375 *          A string, maybe with a leading `#`.
26376 *
26377 * @returns {string}
26378 *          The string, without any leading `#`.
26379 */
26380var normalizeId = function normalizeId(id) {
26381  return id.indexOf('#') === 0 ? id.slice(1) : id;
26382};
26383
26384/**
26385 * Doubles as the main function for users to create a player instance and also
26386 * the main library object.
26387 * The `videojs` function can be used to initialize or retrieve a player.
26388  *
26389 * @param {string|Element} id
26390 *        Video element or video element ID
26391 *
26392 * @param {Object} [options]
26393 *        Optional options object for config/settings
26394 *
26395 * @param {Component~ReadyCallback} [ready]
26396 *        Optional ready callback
26397 *
26398 * @return {Player}
26399 *         A player instance
26400 */
26401function videojs(id, options, ready) {
26402  var player = videojs.getPlayer(id);
26403
26404  if (player) {
26405    if (options) {
26406      log$1.warn('Player "' + id + '" is already initialised. Options will not be applied.');
26407    }
26408    if (ready) {
26409      player.ready(ready);
26410    }
26411    return player;
26412  }
26413
26414  var el = typeof id === 'string' ? $('#' + normalizeId(id)) : id;
26415
26416  if (!isEl(el)) {
26417    throw new TypeError('The element or ID supplied is not valid. (videojs)');
26418  }
26419
26420  if (!document_1.body.contains(el)) {
26421    log$1.warn('The element supplied is not included in the DOM');
26422  }
26423
26424  options = options || {};
26425
26426  videojs.hooks('beforesetup').forEach(function (hookFunction) {
26427    var opts = hookFunction(el, mergeOptions(options));
26428
26429    if (!isObject(opts) || Array.isArray(opts)) {
26430      log$1.error('please return an object in beforesetup hooks');
26431      return;
26432    }
26433
26434    options = mergeOptions(options, opts);
26435  });
26436
26437  // We get the current "Player" component here in case an integration has
26438  // replaced it with a custom player.
26439  var PlayerComponent = Component.getComponent('Player');
26440
26441  player = new PlayerComponent(el, options, ready);
26442
26443  videojs.hooks('setup').forEach(function (hookFunction) {
26444    return hookFunction(player);
26445  });
26446
26447  return player;
26448}
26449
26450/**
26451 * An Object that contains lifecycle hooks as keys which point to an array
26452 * of functions that are run when a lifecycle is triggered
26453 */
26454videojs.hooks_ = {};
26455
26456/**
26457 * Get a list of hooks for a specific lifecycle
26458 * @function videojs.hooks
26459 *
26460 * @param {string} type
26461 *        the lifecyle to get hooks from
26462 *
26463 * @param {Function|Function[]} [fn]
26464 *        Optionally add a hook (or hooks) to the lifecycle that your are getting.
26465 *
26466 * @return {Array}
26467 *         an array of hooks, or an empty array if there are none.
26468 */
26469videojs.hooks = function (type, fn) {
26470  videojs.hooks_[type] = videojs.hooks_[type] || [];
26471  if (fn) {
26472    videojs.hooks_[type] = videojs.hooks_[type].concat(fn);
26473  }
26474  return videojs.hooks_[type];
26475};
26476
26477/**
26478 * Add a function hook to a specific videojs lifecycle.
26479 *
26480 * @param {string} type
26481 *        the lifecycle to hook the function to.
26482 *
26483 * @param {Function|Function[]}
26484 *        The function or array of functions to attach.
26485 */
26486videojs.hook = function (type, fn) {
26487  videojs.hooks(type, fn);
26488};
26489
26490/**
26491 * Add a function hook that will only run once to a specific videojs lifecycle.
26492 *
26493 * @param {string} type
26494 *        the lifecycle to hook the function to.
26495 *
26496 * @param {Function|Function[]}
26497 *        The function or array of functions to attach.
26498 */
26499videojs.hookOnce = function (type, fn) {
26500  videojs.hooks(type, [].concat(fn).map(function (original) {
26501    var wrapper = function wrapper() {
26502      videojs.removeHook(type, wrapper);
26503      return original.apply(undefined, arguments);
26504    };
26505
26506    return wrapper;
26507  }));
26508};
26509
26510/**
26511 * Remove a hook from a specific videojs lifecycle.
26512 *
26513 * @param {string} type
26514 *        the lifecycle that the function hooked to
26515 *
26516 * @param {Function} fn
26517 *        The hooked function to remove
26518 *
26519 * @return {boolean}
26520 *         The function that was removed or undef
26521 */
26522videojs.removeHook = function (type, fn) {
26523  var index = videojs.hooks(type).indexOf(fn);
26524
26525  if (index <= -1) {
26526    return false;
26527  }
26528
26529  videojs.hooks_[type] = videojs.hooks_[type].slice();
26530  videojs.hooks_[type].splice(index, 1);
26531
26532  return true;
26533};
26534
26535// Add default styles
26536if (window_1.VIDEOJS_NO_DYNAMIC_STYLE !== true && isReal()) {
26537  var style = $('.vjs-styles-defaults');
26538
26539  if (!style) {
26540    style = createStyleElement('vjs-styles-defaults');
26541    var head = $('head');
26542
26543    if (head) {
26544      head.insertBefore(style, head.firstChild);
26545    }
26546    setTextContent(style, '\n      .video-js {\n        width: 300px;\n        height: 150px;\n      }\n\n      .vjs-fluid {\n        padding-top: 56.25%\n      }\n    ');
26547  }
26548}
26549
26550// Run Auto-load players
26551// You have to wait at least once in case this script is loaded after your
26552// video in the DOM (weird behavior only with minified version)
26553autoSetupTimeout(1, videojs);
26554
26555/**
26556 * Current software version. Follows semver.
26557 *
26558 * @type {string}
26559 */
26560videojs.VERSION = version;
26561
26562/**
26563 * The global options object. These are the settings that take effect
26564 * if no overrides are specified when the player is created.
26565 *
26566 * @type {Object}
26567 */
26568videojs.options = Player.prototype.options_;
26569
26570/**
26571 * Get an object with the currently created players, keyed by player ID
26572 *
26573 * @return {Object}
26574 *         The created players
26575 */
26576videojs.getPlayers = function () {
26577  return Player.players;
26578};
26579
26580/**
26581 * Get a single player based on an ID or DOM element.
26582 *
26583 * This is useful if you want to check if an element or ID has an associated
26584 * Video.js player, but not create one if it doesn't.
26585 *
26586 * @param   {string|Element} id
26587 *          An HTML element - `<video>`, `<audio>`, or `<video-js>` -
26588 *          or a string matching the `id` of such an element.
26589 *
26590 * @returns {Player|undefined}
26591 *          A player instance or `undefined` if there is no player instance
26592 *          matching the argument.
26593 */
26594videojs.getPlayer = function (id) {
26595  var players = Player.players;
26596  var tag = void 0;
26597
26598  if (typeof id === 'string') {
26599    var nId = normalizeId(id);
26600    var player = players[nId];
26601
26602    if (player) {
26603      return player;
26604    }
26605
26606    tag = $('#' + nId);
26607  } else {
26608    tag = id;
26609  }
26610
26611  if (isEl(tag)) {
26612    var _tag = tag,
26613        _player = _tag.player,
26614        playerId = _tag.playerId;
26615
26616    // Element may have a `player` property referring to an already created
26617    // player instance. If so, return that.
26618
26619    if (_player || players[playerId]) {
26620      return _player || players[playerId];
26621    }
26622  }
26623};
26624
26625/**
26626 * Returns an array of all current players.
26627 *
26628 * @return {Array}
26629 *         An array of all players. The array will be in the order that
26630 *         `Object.keys` provides, which could potentially vary between
26631 *         JavaScript engines.
26632 *
26633 */
26634videojs.getAllPlayers = function () {
26635  return (
26636
26637    // Disposed players leave a key with a `null` value, so we need to make sure
26638    // we filter those out.
26639    Object.keys(Player.players).map(function (k) {
26640      return Player.players[k];
26641    }).filter(Boolean)
26642  );
26643};
26644
26645/**
26646 * Expose players object.
26647 *
26648 * @memberOf videojs
26649 * @property {Object} players
26650 */
26651videojs.players = Player.players;
26652
26653/**
26654 * Get a component class object by name
26655 *
26656 * @borrows Component.getComponent as videojs.getComponent
26657 */
26658videojs.getComponent = Component.getComponent;
26659
26660/**
26661 * Register a component so it can referred to by name. Used when adding to other
26662 * components, either through addChild `component.addChild('myComponent')` or through
26663 * default children options  `{ children: ['myComponent'] }`.
26664 *
26665 * > NOTE: You could also just initialize the component before adding.
26666 * `component.addChild(new MyComponent());`
26667 *
26668 * @param {string} name
26669 *        The class name of the component
26670 *
26671 * @param {Component} comp
26672 *        The component class
26673 *
26674 * @return {Component}
26675 *         The newly registered component
26676 */
26677videojs.registerComponent = function (name$$1, comp) {
26678  if (Tech.isTech(comp)) {
26679    log$1.warn('The ' + name$$1 + ' tech was registered as a component. It should instead be registered using videojs.registerTech(name, tech)');
26680  }
26681
26682  Component.registerComponent.call(Component, name$$1, comp);
26683};
26684
26685/**
26686 * Get a Tech class object by name
26687 *
26688 * @borrows Tech.getTech as videojs.getTech
26689 */
26690videojs.getTech = Tech.getTech;
26691
26692/**
26693 * Register a Tech so it can referred to by name.
26694 * This is used in the tech order for the player.
26695 *
26696 * @borrows Tech.registerTech as videojs.registerTech
26697 */
26698videojs.registerTech = Tech.registerTech;
26699
26700/**
26701 * Register a middleware to a source type.
26702 *
26703 * @param {String}
26703 type A string representing a MIME type.
26704 * @param {function(player):object} middleware A middleware factory that takes a player.
26705 */
26706videojs.use = use;
26707
26708/**
26709 * An object that can be returned by a middleware to signify
26710 * that the middleware is being terminated.
26711 *
26712 * @type {object}
26713 * @memberOf {videojs}
26714 * @property {object} middleware.TERMINATOR
26715 */
26716// Object.defineProperty is not available in IE8
26717if (!IS_IE8 && Object.defineProperty) {
26718  Object.defineProperty(videojs, 'middleware', {
26719    value: {},
26720    writeable: false,
26721    enumerable: true
26722  });
26723
26724  Object.defineProperty(videojs.middleware, 'TERMINATOR', {
26725    value: TERMINATOR,
26726    writeable: false,
26727    enumerable: true
26728  });
26729} else {
26730  videojs.middleware = { TERMINATOR: TERMINATOR };
26731}
26732
26733/**
26734 * A suite of browser and device tests from {@link browser}.
26735 *
26736 * @type {Object}
26737 * @private
26738 */
26739videojs.browser = browser;
26740
26741/**
26742 * Whether or not the browser supports touch events. Included for backward
26743 * compatibility with 4.x, but deprecated. Use `videojs.browser.TOUCH_ENABLED`
26744 * instead going forward.
26745 *
26746 * @deprecated since version 5.0
26747 * @type {boolean}
26748 */
26749videojs.TOUCH_ENABLED = TOUCH_ENABLED;
26750
26751/**
26752 * Subclass an existing class
26753 * Mimics ES6 subclassing with the `extend` keyword
26754 *
26755 * @borrows extend:extendFn as videojs.extend
26756 */
26757videojs.extend = extendFn;
26758
26759/**
26760 * Merge two options objects recursively
26761 * Performs a deep merge like lodash.merge but **only merges plain objects**
26762 * (not arrays, elements, anything else)
26763 * Other values will be copied directly from the second object.
26764 *
26765 * @borrows merge-options:mergeOptions as videojs.mergeOptions
26766 */
26767videojs.mergeOptions = mergeOptions;
26768
26769/**
26770 * Change the context (this) of a function
26771 *
26772 * > NOTE: as of v5.0 we require an ES5 shim, so you should use the native
26773 * `function() {}.bind(newContext);` instead of this.
26774 *
26775 * @borrows fn:bind as videojs.bind
26776 */
26777videojs.bind = bind;
26778
26779/**
26780 * Register a Video.js plugin.
26781 *
26782 * @borrows plugin:registerPlugin as videojs.registerPlugin
26783 * @method registerPlugin
26784 *
26785 * @param  {string} name
26786 *         The name of the plugin to be registered. Must be a string and
26787 *         must not match an existing plugin or a method on the `Player`
26788 *         prototype.
26789 *
26790 * @param  {Function} plugin
26791 *         A sub-class of `Plugin` or a function for basic plugins.
26792 *
26793 * @return {Function}
26794 *         For advanced plugins, a factory function for that plugin. For
26795 *         basic plugins, a wrapper function that initializes the plugin.
26796 */
26797videojs.registerPlugin = Plugin.registerPlugin;
26798
26799/**
26800 * Deregister a Video.js plugin.
26801 *
26802 * @borrows plugin:deregisterPlugin as videojs.deregisterPlugin
26803 * @method deregisterPlugin
26804 *
26805 * @param  {string} name
26806 *         The name of the plugin to be deregistered. Must be a string and
26807 *         must match an existing plugin or a method on the `Player`
26808 *         prototype.
26809 *
26810 */
26811videojs.deregisterPlugin = Plugin.deregisterPlugin;
26812
26813/**
26814 * Deprecated method to register a plugin with Video.js
26815 *
26816 * @deprecated
26817 *        videojs.plugin() is deprecated; use videojs.registerPlugin() instead
26818 *
26819 * @param {string} name
26820 *        The plugin name
26821 *
26822 * @param {Plugin|Function} plugin
26823 *         The plugin sub-class or function
26824 */
26825videojs.plugin = function (name$$1, plugin) {
26826  log$1.warn('videojs.plugin() is deprecated; use videojs.registerPlugin() instead');
26827  return Plugin.registerPlugin(name$$1, plugin);
26828};
26829
26830/**
26831 * Gets an object containing multiple Video.js plugins.
26832 *
26833 * @param  {Array} [names]
26834 *         If provided, should be an array of plugin names. Defaults to _all_
26835 *         plugin names.
26836 *
26837 * @return {Object|undefined}
26838 *         An object containing plugin(s) associated with their name(s) or
26839 *         `undefined` if no matching plugins exist).
26840 */
26841videojs.getPlugins = Plugin.getPlugins;
26842
26843/**
26844 * Gets a plugin by name if it exists.
26845 *
26846 * @param  {string} name
26847 *         The name of a plugin.
26848 *
26849 * @return {Function|undefined}
26850 *         The plugin (or `undefined`).
26851 */
26852videojs.getPlugin = Plugin.getPlugin;
26853
26854/**
26855 * Gets a plugin's version, if available
26856 *
26857 * @param  {string} name
26858 *         The name of a plugin.
26859 *
26860 * @return {string}
26861 *         The plugin's version or an empty string.
26862 */
26863videojs.getPluginVersion = Plugin.getPluginVersion;
26864
26865/**
26866 * Adding languages so that they're available to all players.
26867 * Example: `videojs.addLanguage('es', { 'Hello': 'Hola' });`
26868 *
26869 * @param {string} code
26870 *        The language code or dictionary property
26871 *
26872 * @param {Object} data
26873 *        The data values to be translated
26874 *
26875 * @return {Object}
26876 *         The resulting language dictionary object
26877 */
26878videojs.addLanguage = function (code, data) {
26879  var _mergeOptions;
26880
26881  code = ('' + code).toLowerCase();
26882
26883  videojs.options.languages = mergeOptions(videojs.options.languages, (_mergeOptions = {}, _mergeOptions[code] = data, _mergeOptions));
26884
26885  return videojs.options.languages[code];
26886};
26887
26888/**
26889 * Log messages
26890 *
26891 * @borrows log:log as videojs.log
26892 */
26893videojs.log = log$1;
26894
26895/**
26896 * Creates an emulated TimeRange object.
26897 *
26898 * @borrows time-ranges:createTimeRanges as videojs.createTimeRange
26899 */
26900/**
26901 * @borrows time-ranges:createTimeRanges as videojs.createTimeRanges
26902 */
26903videojs.createTimeRange = videojs.createTimeRanges = createTimeRanges;
26904
26905/**
26906 * Format seconds as a time string, H:MM:SS or M:SS
26907 * Supplying a guide (in seconds) will force a number of leading zeros
26908 * to cover the length of the guide
26909 *
26910 * @borrows format-time:formatTime as videojs.formatTime
26911 */
26912videojs.formatTime = formatTime;
26913
26914/**
26915 * Replaces format-time with a custom implementation, to be used in place of the default.
26916 *
26917 * @borrows format-time:setFormatTime as videojs.setFormatTime
26918 *
26919 * @method setFormatTime
26920 *
26921 * @param {Function} customFn
26922 *        A custom format-time function which will be called with the current time and guide (in seconds) as arguments.
26923 *        Passed fn should return a string.
26924 */
26925videojs.setFormatTime = setFormatTime;
26926
26927/**
26928 * Resets format-time to the default implementation.
26929 *
26930 * @borrows format-time:resetFormatTime as videojs.resetFormatTime
26931 *
26932 * @method resetFormatTime
26933 */
26934videojs.resetFormatTime = resetFormatTime;
26935
26936/**
26937 * Resolve and parse the elements of a URL
26938 *
26939 * @borrows url:parseUrl as videojs.parseUrl
26940 *
26941 */
26942videojs.parseUrl = parseUrl;
26943
26944/**
26945 * Returns whether the url passed is a cross domain request or not.
26946 *
26947 * @borrows url:isCrossOrigin as videojs.isCrossOrigin
26948 */
26949videojs.isCrossOrigin = isCrossOrigin;
26950
26951/**
26952 * Event target class.
26953 *
26954 * @borrows EventTarget as videojs.EventTarget
26955 */
26956videojs.EventTarget = EventTarget;
26957
26958/**
26959 * Add an event listener to element
26960 * It stores the handler function in a separate cache object
26961 * and adds a generic handler to the element's event,
26962 * along with a unique id (guid) to the element.
26963 *
26964 * @borrows events:on as videojs.on
26965 */
26966videojs.on = on;
26967
26968/**
26969 * Trigger a listener only once for an event
26970 *
26971 * @borrows events:one as videojs.one
26972 */
26973videojs.one = one;
26974
26975/**
26976 * Removes event listeners from an element
26977 *
26978 * @borrows events:off as videojs.off
26979 */
26980videojs.off = off;
26981
26982/**
26983 * Trigger an event for an element
26984 *
26985 * @borrows events:trigger as videojs.trigger
26986 */
26987videojs.trigger = trigger;
26988
26989/**
26990 * A cross-browser XMLHttpRequest wrapper. Here's a simple example:
26991 *
26992 * @param {Object} options
26993 *        settings for the request.
26994 *
26995 * @return {XMLHttpRequest|XDomainRequest}
26996 *         The request object.
26997 *
26998 * @see https://github.com/Raynos/xhr
26999 */
27000videojs.xhr = xhr;
27001
27002/**
27003 * TextTrack class
27004 *
27005 * @borrows TextTrack as videojs.TextTrack
27006 */
27007videojs.TextTrack = TextTrack;
27008
27009/**
27010 * export the AudioTrack class so that source handlers can create
27011 * AudioTracks and then add them to the players AudioTrackList
27012 *
27013 * @borrows AudioTrack as videojs.AudioTrack
27014 */
27015videojs.AudioTrack = AudioTrack;
27016
27017/**
27018 * export the VideoTrack class so that source handlers can create
27019 * VideoTracks and then add them to the players VideoTrackList
27020 *
27021 * @borrows VideoTrack as videojs.VideoTrack
27022 */
27023videojs.VideoTrack = VideoTrack;
27024
27025/**
27026 * Determines, via duck typing, whether or not a value is a DOM element.
27027 *
27028 * @borrows dom:isEl as videojs.isEl
27029 * @deprecated Use videojs.dom.isEl() instead
27030 */
27031
27032/**
27033 * Determines, via duck typing, whether or not a value is a text node.
27034 *
27035 * @borrows dom:isTextNode as videojs.isTextNode
27036 * @deprecated Use videojs.dom.isTextNode() instead
27037 */
27038
27039/**
27040 * Creates an element and applies properties.
27041 *
27042 * @borrows dom:createEl as videojs.createEl
27043 * @deprecated Use videojs.dom.createEl() instead
27044 */
27045
27046/**
27047 * Check if an element has a CSS class
27048 *
27049 * @borrows dom:hasElClass as videojs.hasClass
27050 * @deprecated Use videojs.dom.hasClass() instead
27051 */
27052
27053/**
27054 * Add a CSS class name to an element
27055 *
27056 * @borrows dom:addElClass as videojs.addClass
27057 * @deprecated Use videojs.dom.addClass() instead
27058 */
27059
27060/**
27061 * Remove a CSS class name from an element
27062 *
27063 * @borrows dom:removeElClass as videojs.removeClass
27064 * @deprecated Use videojs.dom.removeClass() instead
27065 */
27066
27067/**
27068 * Adds or removes a CSS class name on an element depending on an optional
27069 * condition or the presence/absence of the class name.
27070 *
27071 * @borrows dom:toggleElClass as videojs.toggleClass
27072 * @deprecated Use videojs.dom.toggleClass() instead
27073 */
27074
27075/**
27076 * Apply attributes to an HTML element.
27077 *
27078 * @borrows dom:setElAttributes as videojs.setAttribute
27079 * @deprecated Use videojs.dom.setAttributes() instead
27080 */
27081
27082/**
27083 * Get an element's attribute values, as defined on the HTML tag
27084 * Attributes are not the same as properties. They're defined on the tag
27085 * or with setAttribute (which shouldn't be used with HTML)
27086 * This will return true or false for boolean attributes.
27087 *
27088 * @borrows dom:getElAttributes as videojs.getAttributes
27089 * @deprecated Use videojs.dom.getAttributes() instead
27090 */
27091
27092/**
27093 * Empties the contents of an element.
27094 *
27095 * @borrows dom:emptyEl as videojs.emptyEl
27096 * @deprecated Use videojs.dom.emptyEl() instead
27097 */
27098
27099/**
27100 * Normalizes and appends content to an element.
27101 *
27102 * The content for an element can be passed in multiple types and
27103 * combinations, whose behavior is as follows:
27104 *
27105 * - String
27106 *   Normalized into a text node.
27107 *
27108 * - Element, TextNode
27109 *   Passed through.
27110 *
27111 * - Array
27112 *   A one-dimensional array of strings, elements, nodes, or functions (which
27113 *   return single strings, elements, or nodes).
27114 *
27115 * - Function
27116 *   If the sole argument, is expected to produce a string, element,
27117 *   node, or array.
27118 *
27119 * @borrows dom:appendContents as videojs.appendContet
27120 * @deprecated Use videojs.dom.appendContent() instead
27121 */
27122
27123/**
27124 * Normalizes and inserts content into an element; this is identical to
27125 * `appendContent()`, except it empties the element first.
27126 *
27127 * The content for an element can be passed in multiple types and
27128 * combinations, whose behavior is as follows:
27129 *
27130 * - String
27131 *   Normalized into a text node.
27132 *
27133 * - Element, TextNode
27134 *   Passed through.
27135 *
27136 * - Array
27137 *   A one-dimensional array of strings, elements, nodes, or functions (which
27138 *   return single strings, elements, or nodes).
27139 *
27140 * - Function
27141 *   If the sole argument, is expected to produce a string, element,
27142 *   node, or array.
27143 *
27144 * @borrows dom:insertContent as videojs.insertContent
27145 * @deprecated Use videojs.dom.insertContent() instead
27146 */
27147['isEl', 'isTextNode', 'createEl', 'hasClass', 'addClass', 'removeClass', 'toggleClass', 'setAttributes', 'getAttributes', 'emptyEl', 'appendContent', 'insertContent'].forEach(function (k) {
27148  videojs[k] = function () {
27149    log$1.warn('videojs.' + k + '() is deprecated; use videojs.dom.' + k + '() instead');
27150    return Dom[k].apply(null, arguments);
27151  };
27152});
27153
27154/**
27155 * A safe getComputedStyle with an IE8 fallback.
27156 *
27157 * This is because in Firefox, if the player is loaded in an iframe with `display:none`,
27158 * then `getComputedStyle` returns `null`, so, we do a null-check to make sure
27159 * that the player doesn't break in these cases.
27160 * See https://bugzilla.mozilla.org/show_bug.cgi?id=548397 for more details.
27161 *
27162 * @borrows computed-style:computedStyle as videojs.computedStyle
27163 */
27164videojs.computedStyle = computedStyle;
27165
27166/**
27167 * Export the Dom utilities for use in external plugins
27168 * and Tech's
27169 */
27170videojs.dom = Dom;
27171
27172/**
27173 * Export the Url utilities for use in external plugins
27174 * and Tech's
27175 */
27176videojs.url = Url;
27177
27178return videojs;
27179
27180})));
27181
27182!function(){!function(a){var b=a&&a.videojs;b&&(b.CDN_VERSION="6.12.0")}(window),function(a,b,c,d,e,f,g){b&&b.HELP_IMPROVE_VIDEOJS!==!1&&(c&&c.doNotTrack&&"1"===c.doNotTrack||c&&c.msDoNotTrack&&"1"===c.msDoNotTrack||b.doNotTrack&&"1"===b.doNotTrack||e.random()>.01||(f=b.location,g=b.videojs||{},a.src="//www.google-analytics.com/__utm.gif?utmwv=5.4.2&utmac=UA-16505296-3&utmn=1&utmhn="+d(f.hostname)+"&utmsr="+b.screen.availWi
vendor: 24,293 bytes, lines 27182-27980
27182dth+"x"+b.screen.availHeight+"&utmul="+(c.language||c.userLanguage||"").toLowerCase()+"&utmr="+d(f.href)+"&utmp="+d(f.hostname+f.pathname)+"&utmcc=__utma%3D1."+e.floor(1e10*e.random())+".1.1.1.1%3B&utme=8(vjsv*cdnv)9("+g.VERSION+"*"+g.CDN_VERSION+")"))}(new Image,window,navigator,encodeURIComponent,Math)}();
27183/* The MIT License (MIT)
27184
27185Copyright (c) 2014-2015 Benoit Tremblay <[email protected]>
27186
27187Permission is hereby granted, free of charge, to any person obtaining a copy
27188of this software and associated documentation files (the "Software"), to deal
27189in the Software without restriction, including without limitation the rights
27190to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
27191copies of the Software, and to permit persons to whom the Software is
27192furnished to do so, subject to the following conditions:
27193
27194The above copyright notice and this permission notice shall be included in
27195all copies or substantial portions of the Software.
27196
27197THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
27198IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
27199FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
27200AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
27201LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
27202OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
27203THE SOFTWARE. */
27204/*global define, YT*/
27205(function (root, factory) {
27206    if(typeof exports==='object' && typeof module!=='undefined') {
27207      var videojs = require('video.js');
27208      module.exports = factory(videojs.default || videojs);
27209    } else if(typeof define === 'function' && define.amd) {
27210      define(['videojs'], function(videojs){
27211        return (root.Youtube = factory(videojs));
27212      });
27213    } else {
27214      root.Youtube = factory(root.videojs);
27215    }
27216  }(this, function(videojs) {
27217    'use strict';
27218  
27219    var _isOnMobile = videojs.browser.IS_IOS || videojs.browser.IS_NATIVE_ANDROID;
27220    var Tech = videojs.getTech('Tech');
27221  
27222    class Youtube extends Tech {
27223  
27224      constructor(options, ready) {
27225        super(options, ready);
27226  
27227        this.setPoster(options.poster);
27228        this.setSrc(this.options_.source, true);
27229  
27230        // Set the vjs-youtube class to the player
27231        // Parent is not set yet so we have to wait a tick
27232        this.setTimeout(function() {
27233          if (this.el_) {
27234            this.el_.parentNode.className += ' vjs-youtube';
27235  
27236            if (_isOnMobile) {
27237              this.el_.parentNode.className += ' vjs-youtube-mobile';
27238            }
27239  
27240            if (Youtube.isApiReady) {
27241              this.initYTPlayer();
27242            } else {
27243              Youtube.apiReadyQueue.push(this);
27244            }
27245          }
27246        }.bind(this));
27247      }
27248  
27249      dispose() {
27250        if (this.ytPlayer) {
27251          //Dispose of the YouTube Player
27252          if (this.ytPlayer.stopVideo) {
27253            this.ytPlayer.stopVideo();
27254          }
27255          if (this.ytPlayer.destroy) {
27256            this.ytPlayer.destroy();
27257          }
27258        } else {
27259          //YouTube API hasn't finished loading or the player is already disposed
27260          var index = Youtube.apiReadyQueue.indexOf(this);
27261          if (index !== -1) {
27262            Youtube.apiReadyQueue.splice(index, 1);
27263          }
27264        }
27265        this.ytPlayer = null;
27266  
27267        this.el_.parentNode.className = this.el_.parentNode.className
27268            .replace(' vjs-youtube', '')
27269            .replace(' vjs-youtube-mobile', '');
27270        this.el_.parentNode.removeChild(this.el_);
27271  
27272        //Needs to be called after the YouTube player is destroyed, otherwise there will be a null reference exception
27273        Tech.prototype.dispose.call(this);
27274      }
27275  
27276      createEl() {
27277        var div = document.createElement('div');
27278        div.setAttribute('id', this.options_.techId);
27279        div.setAttribute('style', 'width:100%;height:100%;top:0;left:0;position:absolute');
27280        div.setAttribute('class', 'vjs-tech');
27281  
27282        var divWrapper = document.createElement('div');
27283        divWrapper.appendChild(div);
27284  
27285        if (!_isOnMobile && !this.options_.ytControls) {
27286          var divBlocker = document.createElement('div');
27287          divBlocker.setAttribute('class', 'vjs-iframe-blocker');
27288          divBlocker.setAttribute('style', 'position:absolute;top:0;left:0;width:100%;height:100%');
27289  
27290          // In case the blocker is still there and we want to pause
27291          divBlocker.onclick = function() {
27292            this.pause();
27293          }.bind(this);
27294  
27295          divWrapper.appendChild(divBlocker);
27296        }
27297  
27298        return divWrapper;
27299      }
27300  
27301      initYTPlayer() {
27302        var playerVars = {
27303          controls: 0,
27304          modestbranding: 1,
27305          rel: 0,
27306          showinfo: 0,
27307          loop: this.options_.loop ? 1 : 0
27308        };
27309  
27310        // Let the user set any YouTube parameter
27311        // https://developers.google.com/youtube/player_parameters?playerVersion=HTML5#Parameters
27312        // To use YouTube controls, you must use ytControls instead
27313        // To use the loop or autoplay, use the video.js settings
27314  
27315        if (typeof this.options_.autohide !== 'undefined') {
27316          playerVars.autohide = this.options_.autohide;
27317        }
27318  
27319        if (typeof this.options_['cc_load_policy'] !== 'undefined') {
27320          playerVars['cc_load_policy'] = this.options_['cc_load_policy'];
27321        }
27322  
27323        if (typeof this.options_.ytControls !== 'undefined') {
27324          playerVars.controls = this.options_.ytControls;
27325        }
27326  
27327        if (typeof this.options_.disablekb !== 'undefined') {
27328          playerVars.disablekb = this.options_.disablekb;
27329        }
27330  
27331        if (typeof this.options_.color !== 'undefined') {
27332          playerVars.color = this.options_.color;
27333        }
27334  
27335        if (!playerVars.controls) {
27336          // Let video.js handle the fullscreen unless it is the YouTube native controls
27337          playerVars.fs = 0;
27338        } else if (typeof this.options_.fs !== 'undefined') {
27339          playerVars.fs = this.options_.fs;
27340        }
27341  
27342        if (this.options_.source.src.indexOf('end=') !== -1) {
27343          var srcEndTime = this.options_.source.src.match(/end=([0-9]*)/);
27344          this.options_.end = parseInt(srcEndTime[1]);
27345        }
27346  
27347        if (typeof this.options_.end !== 'undefined') {
27348          playerVars.end = this.options_.end;
27349        }
27350  
27351        if (typeof this.options_.hl !== 'undefined') {
27352          playerVars.hl = this.options_.hl;
27353        } else if (typeof this.options_.language !== 'undefined') {
27354          // Set the YouTube player on the same language than video.js
27355          playerVars.hl = this.options_.language.substr(0, 2);
27356        }
27357  
27358        if (typeof this.options_['iv_load_policy'] !== 'undefined') {
27359          playerVars['iv_load_policy'] = this.options_['iv_load_policy'];
27360        }
27361  
27362        if (typeof this.options_.list !== 'undefined') {
27363          playerVars.list = this.options_.list;
27364        } else if (this.url && typeof this.url.listId !== 'undefined') {
27365          playerVars.list = this.url.listId;
27366        }
27367  
27368        if (typeof this.options_.listType !== 'undefined') {
27369          playerVars.listType = this.options_.listType;
27370        }
27371  
27372        if (typeof this.options_.modestbranding !== 'undefined') {
27373          playerVars.modestbranding = this.options_.modestbranding;
27374        }
27375  
27376        if (typeof this.options_.playlist !== 'undefined') {
27377          playerVars.playlist = this.options_.playlist;
27378        }
27379  
27380        if (typeof this.options_.playsinline !== 'undefined') {
27381          playerVars.playsinline = this.options_.playsinline;
27382        }
27383  
27384        if (typeof this.options_.rel !== 'undefined') {
27385          playerVars.rel = this.options_.rel;
27386        }
27387  
27388        if (typeof this.options_.showinfo !== 'undefined') {
27389          playerVars.showinfo = this.options_.showinfo;
27390        }
27391  
27392        if (this.options_.source.src.indexOf('start=') !== -1) {
27393          var srcStartTime = this.options_.source.src.match(/start=([0-9]*)/);
27394          this.options_.start = parseInt(srcStartTime[1]);
27395        }
27396  
27397        if (typeof this.options_.start !== 'undefined') {
27398          playerVars.start = this.options_.start;
27399        }
27400  
27401        if (typeof this.options_.theme !== 'undefined') {
27402          playerVars.theme = this.options_.theme;
27403        }
27404  
27405        // Allow undocumented options to be passed along via customVars
27406        if (typeof this.options_.customVars !== 'undefined') {
27407          var customVars = this.options_.customVars;
27408          Object.keys(customVars).forEach(function(key) {
27409            playerVars[key] = customVars[key];
27410          });
27411        }
27412  
27413        this.activeVideoId = this.url ? this.url.videoId : null;
27414        this.activeList = playerVars.list;
27415  
27416        var playerConfig = {
27417          videoId: this.activeVideoId,
27418          playerVars: playerVars,
27419          events: {
27420            onReady: this.onPlayerReady.bind(this),
27421            onPlaybackQualityChange: this.onPlayerPlaybackQualityChange.bind(this),
27422            onPlaybackRateChange: this.onPlayerPlaybackRateChange.bind(this),
27423            onStateChange: this.onPlayerStateChange.bind(this),
27424            onVolumeChange: this.onPlayerVolumeChange.bind(this),
27425            onError: this.onPlayerError.bind(this)
27426          }
27427        };
27428  
27429        if (typeof this.options_.enablePrivacyEnhancedMode !== 'undefined' && this.options_.enablePrivacyEnhancedMode) {
27430          playerConfig.host = 'https://www.youtube-nocookie.com';
27431        }
27432  
27433        this.ytPlayer = new YT.Player(this.options_.techId, playerConfig);
27434      }
27435  
27436      onPlayerReady() {
27437        if (this.options_.muted) {
27438          this.ytPlayer.mute();
27439        }
27440  
27441        var playbackRates = this.ytPlayer.getAvailablePlaybackRates();
27442        if (playbackRates.length > 1) {
27443          this.featuresPlaybackRate = true;
27444        }
27445  
27446        this.playerReady_ = true;
27447        this.triggerReady();
27448  
27449        if (this.playOnReady) {
27450          this.play();
27451        } else if (this.cueOnReady) {
27452          this.cueVideoById_(this.url.videoId);
27453          this.activeVideoId = this.url.videoId;
27454        }
27455      }
27456  
27457      onPlayerPlaybackQualityChange() {
27458  
27459      }
27460  
27461      onPlayerPlaybackRateChange() {
27462        this.trigger('ratechange');
27463      }
27464  
27465      onPlayerStateChange(e) {
27466        var state = e.data;
27467  
27468        if (state === this.lastState || this.errorNumber) {
27469          return;
27470        }
27471  
27472        this.lastState = state;
27473  
27474        switch (state) {
27475          case -1:
27476            this.trigger('loadstart');
27477            this.trigger('loadedmetadata');
27478            this.trigger('durationchange');
27479            this.trigger('ratechange');
27480            break;
27481  
27482          case YT.PlayerState.ENDED:
27483            this.trigger('ended');
27484            break;
27485  
27486          case YT.PlayerState.PLAYING:
27487            this.trigger('timeupdate');
27488            this.trigger('durationchange');
27489            this.trigger('playing');
27490            this.trigger('play');
27491  
27492            if (this.isSeeking) {
27493              this.onSeeked();
27494            }
27495            break;
27496  
27497          case YT.PlayerState.PAUSED:
27498            this.trigger('canplay');
27499            if (this.isSeeking) {
27500              this.onSeeked();
27501            } else {
27502              this.trigger('pause');
27503            }
27504            break;
27505  
27506          case YT.PlayerState.BUFFERING:
27507            this.player_.trigger('timeupdate');
27508            this.player_.trigger('waiting');
27509            break;
27510        }
27511      }
27512  
27513      onPlayerVolumeChange() {
27514        this.trigger('volumechange');
27515      }
27516  
27517      onPlayerError(e) {
27518        this.errorNumber = e.data;
27519        this.trigger('pause');
27520        this.trigger('error');
27521      }
27522  
27523      error() {
27524        var code = 1000 + this.errorNumber; // as smaller codes are reserved
27525        switch (this.errorNumber) {
27526          case 5:
27527            return { code: code, message: 'Error while trying to play the video' };
27528  
27529          case 2:
27530          case 100:
27531            return { code: code, message: 'Unable to find the video' };
27532  
27533          case 101:
27534          case 150:
27535            return {
27536              code: code,
27537              message: 'Playback on other Websites has been disabled by the video owner.'
27538            };
27539        }
27540  
27541        return { code: code, message: 'YouTube unknown error (' + this.errorNumber + ')' };
27542      }
27543  
27544      loadVideoById_(id) {
27545        var options = {
27546          videoId: id
27547        };
27548        if (this.options_.start) {
27549          options.startSeconds = this.options_.start;
27550        }
27551        if (this.options_.end) {
27552          options.endSeconds = this.options_.end;
27553        }
27554        this.ytPlayer.loadVideoById(options);
27555      }
27556  
27557      cueVideoById_(id) {
27558        var options = {
27559          videoId: id
27560        };
27561        if (this.options_.start) {
27562          options.startSeconds = this.options_.start;
27563        }
27564        if (this.options_.end) {
27565          options.endSeconds = this.options_.end;
27566        }
27567        this.ytPlayer.cueVideoById(options);
27568      }
27569  
27570      src(src) {
27571        if (src) {
27572          this.setSrc({ src: src });
27573        }
27574  
27575        return this.source;
27576      }
27577  
27578      poster() {
27579        // You can't start programmaticlly a video with a mobile
27580        // through the iframe so we hide the poster and the play button (with CSS)
27581        if (_isOnMobile) {
27582          return null;
27583        }
27584  
27585        return this.poster_;
27586      }
27587  
27588      setPoster(poster) {
27589        this.poster_ = poster;
27590      }
27591  
27592      setSrc(source) {
27593        if (!source || !source.src) {
27594          return;
27595        }
27596  
27597        delete this.errorNumber;
27598        this.source = source;
27599        this.url = Youtube.parseUrl(source.src);
27600  
27601        if (!this.options_.poster) {
27602          if (this.url.videoId) {
27603            // Set the low resolution first
27604            this.poster_ = 'https://img.youtube.com/vi/' + this.url.videoId + '/0.jpg';
27605            this.trigger('posterchange');
27606  
27607            // Check if their is a high res
27608            this.checkHighResPoster();
27609          }
27610        }
27611  
27612        if (this.options_.autoplay && !_isOnMobile) {
27613          if (this.isReady_) {
27614            this.play();
27615          } else {
27616            this.playOnReady = true;
27617          }
27618        } else if (this.activeVideoId !== this.url.videoId) {
27619          if (this.isReady_) {
27620            this.cueVideoById_(this.url.videoId);
27621            this.activeVideoId = this.url.videoId;
27622          } else {
27623            this.cueOnReady = true;
27624          }
27625        }
27626      }
27627  
27628      autoplay() {
27629        return this.options_.autoplay;
27630      }
27631  
27632      setAutoplay(val) {
27633        this.options_.autoplay = val;
27634      }
27635  
27636      loop() {
27637        return this.options_.loop;
27638      }
27639  
27640      setLoop(val) {
27641        this.options_.loop = val;
27642      }
27643  
27644      play() {
27645        if (!this.url || !this.url.videoId) {
27646          return;
27647        }
27648  
27649        this.wasPausedBeforeSeek = false;
27650  
27651        if (this.isReady_) {
27652          if (this.url.listId) {
27653            if (this.activeList === this.url.listId) {
27654              this.ytPlayer.playVideo();
27655            } else {
27656              this.ytPlayer.loadPlaylist(this.url.listId);
27657              this.activeList = this.url.listId;
27658            }
27659          }
27660  
27661          if (this.activeVideoId === this.url.videoId) {
27662            this.ytPlayer.playVideo();
27663          } else {
27664            this.loadVideoById_(this.url.videoId);
27665            this.activeVideoId = this.url.videoId;
27666          }
27667        } else {
27668          this.trigger('waiting');
27669          this.playOnReady = true;
27670        }
27671      }
27672  
27673      pause() {
27674        if (this.ytPlayer) {
27675          this.ytPlayer.pauseVideo();
27676        }
27677      }
27678  
27679      paused() {
27680        return (this.ytPlayer) ?
27681            (this.lastState !== YT.PlayerState.PLAYING && this.lastState !== YT.PlayerState.BUFFERING)
27682            : true;
27683      }
27684  
27685      currentTime() {
27686        return this.ytPlayer ? this.ytPlayer.getCurrentTime() : 0;
27687      }
27688  
27689      setCurrentTime(seconds) {
27690        if (this.lastState === YT.PlayerState.PAUSED) {
27691          this.timeBeforeSeek = this.currentTime();
27692        }
27693  
27694        if (!this.isSeeking) {
27695          this.wasPausedBeforeSeek = this.paused();
27696        }
27697  
27698        this.ytPlayer.seekTo(seconds, true);
27699        this.trigger('timeupdate');
27700        this.trigger('seeking');
27701        this.isSeeking = true;
27702  
27703        // A seek event during pause does not return an event to trigger a seeked event,
27704        // so run an interval timer to look for the currentTime to change
27705        if (this.lastState === YT.PlayerState.PAUSED && this.timeBeforeSeek !== seconds) {
27706          clearInterval(this.checkSeekedInPauseInterval);
27707          this.checkSeekedInPauseInterval = setInterval(function() {
27708            if (this.lastState !== YT.PlayerState.PAUSED || !this.isSeeking) {
27709              // If something changed while we were waiting for the currentTime to change,
27710              //  clear the interval timer
27711              clearInterval(this.checkSeekedInPauseInterval);
27712            } else if (this.currentTime() !== this.timeBeforeSeek) {
27713              this.trigger('timeupdate');
27714              this.onSeeked();
27715            }
27716          }.bind(this), 250);
27717        }
27718      }
27719  
27720      seeking() {
27721        return this.isSeeking;
27722      }
27723  
27724      seekable() {
27725        if(!this.ytPlayer) {
27726          return videojs.createTimeRange();
27727        }
27728  
27729        return videojs.createTimeRange(0, this.ytPlayer.getDuration());
27730      }
27731  
27732      onSeeked() {
27733        clearInterval(this.checkSeekedInPauseInterval);
27734        this.isSeeking = false;
27735  
27736        if (this.wasPausedBeforeSeek) {
27737          this.pause();
27738        }
27739  
27740        this.trigger('seeked');
27741      }
27742  
27743      playbackRate() {
27744        return this.ytPlayer ? this.ytPlayer.getPlaybackRate() : 1;
27745      }
27746  
27747      setPlaybackRate(suggestedRate) {
27748        if (!this.ytPlayer) {
27749          return;
27750        }
27751  
27752        this.ytPlayer.setPlaybackRate(suggestedRate);
27753      }
27754  
27755      duration() {
27756        return this.ytPlayer ? this.ytPlayer.getDuration() : 0;
27757      }
27758  
27759      currentSrc() {
27760        return this.source && this.source.src;
27761      }
27762  
27763      ended() {
27764        return this.ytPlayer ? (this.lastState === YT.PlayerState.ENDED) : false;
27765      }
27766  
27767      volume() {
27768        return this.ytPlayer ? this.ytPlayer.getVolume() / 100.0 : 1;
27769      }
27770  
27771      setVolume(percentAsDecimal) {
27772        if (!this.ytPlayer) {
27773          return;
27774        }
27775  
27776        this.ytPlayer.setVolume(percentAsDecimal * 100.0);
27777      }
27778  
27779      muted() {
27780        return this.ytPlayer ? this.ytPlayer.isMuted() : false;
27781      }
27782  
27783      setMuted(mute) {
27784        if (!this.ytPlayer) {
27785          return;
27786        }
27787        else{
27788          this.muted(true);
27789        }
27790  
27791        if (mute) {
27792          this.ytPlayer.mute();
27793        } else {
27794          this.ytPlayer.unMute();
27795        }
27796        this.setTimeout( function(){
27797          this.trigger('volumechange');
27798        }, 50);
27799      }
27800  
27801      buffered() {
27802        if(!this.ytPlayer || !this.ytPlayer.getVideoLoadedFraction) {
27803          return videojs.createTimeRange();
27804        }
27805  
27806        var bufferedEnd = this.ytPlayer.getVideoLoadedFraction() * this.ytPlayer.getDuration();
27807  
27808        return videojs.createTimeRange(0, bufferedEnd);
27809      }
27810  
27811      // TODO: Can we really do something with this on YouTUbe?
27812      preload() {}
27813      load() {}
27814      reset() {}
27815      networkState() {
27816        if (!this.ytPlayer) {
27817          return 0; //NETWORK_EMPTY
27818        }
27819        switch (this.ytPlayer.getPlayerState()) {
27820          case -1: //unstarted
27821            return 0; //NETWORK_EMPTY
27822          case 3: //buffering
27823            return 2; //NETWORK_LOADING
27824          default:
27825            return 1; //NETWORK_IDLE
27826        }
27827      }
27828      readyState() {
27829        if (!this.ytPlayer) {
27830          return 0; //HAVE_NOTHING
27831        }
27832        switch (this.ytPlayer.getPlayerState()) {
27833          case -1: //unstarted
27834            return 0; //HAVE_NOTHING
27835          case 5: //video cued
27836            return 1; //HAVE_METADATA
27837          case 3: //buffering
27838            return 2; //HAVE_CURRENT_DATA
27839          default:
27840            return 4; //HAVE_ENOUGH_DATA
27841        }
27842      }
27843  
27844      supportsFullScreen() {
27845        return document.fullscreenEnabled ||
27846            document.webkitFullscreenEnabled ||
27847            document.mozFullScreenEnabled ||
27848            document.msFullscreenEnabled;
27849      }
27850  
27851      // Tries to get the highest resolution thumbnail available for the video
27852      checkHighResPoster(){
27853        var uri = 'https://img.youtube.com/vi/' + this.url.videoId + '/maxresdefault.jpg';
27854  
27855        try {
27856          var image = new Image();
27857          image.onload = function(){
27858            // Onload may still be called if YouTube returns the 120x90 error thumbnail
27859            if('naturalHeight' in image){
27860              if (image.naturalHeight <= 90 || image.naturalWidth <= 120) {
27861                return;
27862              }
27863            } else if(image.height <= 90 || image.width <= 120) {
27864              return;
27865            }
27866  
27867            this.poster_ = uri;
27868            this.trigger('posterchange');
27869          }.bind(this);
27870          image.onerror = function(){};
27871          image.src = uri;
27872        }
27873        catch(e){}
27874      }
27875    }
27876  
27877    Youtube.isSupported = function() {
27878      return true;
27879    };
27880  
27881    Youtube.canPlaySource = function(e) {
27882      return Youtube.canPlayType(e.type);
27883    };
27884  
27885    Youtube.canPlayType = function(e) {
27886      return (e === 'video/youtube');
27887    };
27888  
27889    Youtube.parseUrl = function(url) {
27890      var result = {
27891        videoId: null
27892      };
27893  
27894      var regex = /^.*(youtu.be\/|v\/|u\/\w\/|embed\/|watch\?v=|\&v=)([^#\&\?]*).*/;
27895      var match = url.match(regex);
27896  
27897      if (match && match[2].length === 11) {
27898        result.videoId = match[2];
27899      }
27900  
27901      var regPlaylist = /[?&]list=([^#\&\?]+)/;
27902      match = url.match(regPlaylist);
27903  
27904      if(match && match[1]) {
27905        result.listId = match[1];
27906      }
27907  
27908      return result;
27909    };
27910  
27911    function apiLoaded() {
27912      YT.ready(function() {
27913        Youtube.isApiReady = true;
27914  
27915        for (var i = 0; i < Youtube.apiReadyQueue.length; ++i) {
27916          Youtube.apiReadyQueue[i].initYTPlayer();
27917        }
27918      });
27919    }
27920  
27921    function loadScript(src, callback) {
27922      var loaded = false;
27923      var tag = document.createElement('script');
27924      var firstScriptTag = document.getElementsByTagName('script')[0];
27925      if (!firstScriptTag) {
27926        // when loaded in jest without jsdom setup it doesn't get any element.
27927        // In jest it doesn't really make sense to do anything, because no one is watching youtube in jest
27928        return;
27929      }
27930      firstScriptTag.parentNode.insertBefore(tag, firstScriptTag);
27931      tag.onload = function () {
27932        if (!loaded) {
27933          loaded = true;
27934          callback();
27935        }
27936      };
27937      tag.onreadystatechange = function () {
27938        if (!loaded && (this.readyState === 'complete' || this.readyState === 'loaded')) {
27939          loaded = true;
27940          callback();
27941        }
27942      };
27943      tag.src = src;
27944    }
27945  
27946    function injectCss() {
27947      var css = // iframe blocker to catch mouse events
27948          '.vjs-youtube .vjs-iframe-blocker { display: none; }' +
27949          '.vjs-youtube.vjs-user-inactive .vjs-iframe-blocker { display: block; }' +
27950          '.vjs-youtube .vjs-poster { background-size: cover; }' +
27951          '.vjs-youtube-mobile .vjs-big-play-button { display: none; }';
27952  
27953      var head = document.head || document.getElementsByTagName('head')[0];
27954  
27955      var style = document.createElement('style');
27956      style.type = 'text/css';
27957  
27958      if (style.styleSheet){
27959        style.styleSheet.cssText = css;
27960      } else {
27961        style.appendChild(document.createTextNode(css));
27962      }
27963  
27964      head.appendChild(style);
27965    }
27966  
27967    Youtube.apiReadyQueue = [];
27968  
27969    if (typeof document !== 'undefined'){
27970      loadScript('https://www.youtube.com/iframe_api', apiLoaded);
27971      injectCss();
27972    }
27973  
27974    // Older versions of VJS5 doesn't have the registerTech function
27975    if (typeof videojs.registerTech !== 'undefined') {
27976      videojs.registerTech('Youtube', Youtube);
27977    } else {
27978      videojs.registerComponent('Youtube', Youtube);
27979    }
27980  }));

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.